OpenGL game
Vlastní skriptovací jazyk

Proměnné skriptu.

zdroják ke stažení (6kB)

21.03.2006

Do předešlého skriptu přidáme proměnné. Do konzole se vypíše toto:

0: 7
1: 8
2: 6
3: 13

Proměnné nastavíme do dvouprvkových polí char

char strSetData1[] = {0, 7};
char strSetData2[] = {1, 7};
char strSetData3[] = {2, 7};

Do prvního prvku pole ukládáme index proměnné, do druhého prvku hodnotu. To znamená:
strSetData1 má index 0 a hodntotu 7
strSetData2 má index 1 a hodntotu 7
strSetData3 má index 2 a hodntotu 7

Pak nastavíme indexy proměnných, ke kterým budeme přičítat, nebo odčítat jedničku

char cIncData = 1;
char cDecData = 2;

cIncData přičte jedničku k hodnotě proměnné indexu 1 takže výsledek je 7+1=8 do konzole se vypíše (1: 8)
cDecData odečte jedničku od hodnoty proměnné indexu 2 takže výsledek je 7-1=6 do konzole se vypíše (2: 6)

Do tříprvkového pole char nastavíme další proměnnou

char strAddData[] = {3, 0, 2}; // Add 1st and 3rd var, and store in 4th

strAddData má index 3 a bude přičítat hodnoty proměnné indexu 0 a 2 takže výsledek je 7+6=13 do konzole se vypíše (3: 13)

Práci s proměnnými definujeme v enumerátoru

/// Enumerations describing the bytecode instructions the VM is able to process
enum EScriptOpCode
{
	K_OP_TALK,
	K_OP_PRINT,

	/// Variable manipulators
	K_OP_SET, ///< char, char : destination index, value to set
	K_OP_INC, ///< char : index to increment
	K_OP_DEC, ///< char : index to decrement
	K_OP_ADD, ///< char, char, char : dest index, srce index1, srce index2
	
	K_OP_END
};

Při načítání scény

Nastavíme velikost proměnných.

// Proper instruction data size constants (temporary for safety)
const int SET_SIZE  = 2*sizeof(char);
const int INC_SIZE  = sizeof(char);
const int DEC_SIZE  = sizeof(char);
const int ADD_SIZE  = 3*sizeof(char);

Vytvoříme pole instrukcí

vector<CInstruction> vVarInstr;
vVarInstr.push_back(CInstruction(K_OP_SET, strSetData1, SET_SIZE));   // Set first 3 vars to 7
vVarInstr.push_back(CInstruction(K_OP_SET, strSetData2, SET_SIZE));
vVarInstr.push_back(CInstruction(K_OP_SET, strSetData3, SET_SIZE));
vVarInstr.push_back(CInstruction(K_OP_INC, &cIncData, INC_SIZE));   // Inc 2nd var
vVarInstr.push_back(CInstruction(K_OP_DEC, &cDecData, DEC_SIZE));   // Dec 3rd var
vVarInstr.push_back(CInstruction(K_OP_ADD, strAddData, ADD_SIZE));
vVarInstr.push_back(CInstruction(K_OP_END));	// Then end

Vytvoříme skript o čtyřech proměnných

CScript kVarScript(vVarInstr, 4);  // We need 4 variables

Vytvoříme virtual machine VM a načteme skript

CVirtualMachine kVM;
size_t uiVarManipId = kVM.LoadScript(kVarScript);

Funkce

///\brief Load script
///\param _rkScript Reference to script object
size_t LoadScript(const CScript& _rkScript)
{
	return AddScript(_rkScript);
}

volá funkci

///\brief Add script to list and retrieve id
///\param _rScript Reference to script object
size_t AddScript(const CScript& _rScript) 
{
	m_vScript.push_back(_rScript); 
	return m_uiNumScripts++;
}

Celá VM třída vypadá takto:

class CVirtualMachine
{
	/// Useful abstractions
	/// pointers used as non-modifying dynamic references
	typedef const CScript* TScriptRef;
	typedef const CInstruction*  TInstrRef;

	CScriptState m_kState;	///< Script state. Since we're single-threaded, only need one
	std::vector<CScript> m_vScript;	///< Script array 
	TScriptRef m_tScript;	///< Current script
	TInstrRef m_tInstrRoot;	///< Root instruction
	TInstrRef m_tInstrCurr;	///< Current instruction
	size_t m_uiNumScripts;	///< Number of loaded scripts

	///\brief Add script to list and retrieve id
	///\param _rScript Reference to script object
	size_t AddScript(const CScript& _rScript) 
	{
		m_vScript.push_back(_rScript); 
		return m_uiNumScripts++;
	}
	///\brief Set current script by id
	///\param _uiScriptId Script index
	void SelectScript(size_t _uiScriptId) 
	{
		assert(_uiScriptId < m_uiNumScripts);  // make sure the id is valid
		m_tScript = &m_vScript[_uiScriptId];
		m_tInstrRoot = m_tScript->GetInstruction();
	}
	void ExposeVariableState(const CScriptState& _rState) const;

public:
	CVirtualMachine()
		: m_tScript(0), m_tInstrRoot(0), m_tInstrCurr(0), m_uiNumScripts(0){}
	~CVirtualMachine(){}

	void ExecuteScript(size_t _uiScriptId);
	///\brief Load script
	///\param _rkScript Reference to script object
	size_t LoadScript(const CScript& _rkScript)
	{
		return AddScript(_rkScript);
	}
	///\brief Show variable state (debugging)
	void ShowVariableState() const    
	{ 
		ExposeVariableState(m_kState); 
	}
};

Při běhu aplikace

Spustíme načtený skript

kVM.ExecuteScript(uiVarManipId); 

Funkce vypadá takto:

///\brief Execute VM
///\param _uiScriptId Script index
void CVirtualMachine::ExecuteScript(size_t _uiScriptId)
{
	// Select root script by script index
	SelectScript(_uiScriptId);	
	// Initialize variable data
	m_kState.SetDataSize(m_tScript->GetNumVars());
	// Set our iterator to the beginning       
	m_tInstrCurr = m_tInstrRoot;	
	while (m_tInstrCurr)
	{
		switch(m_tInstrCurr->GetCode())
		{
		// Message functionality
		case K_OP_TALK:
			std::cout << "I am talking." << std::endl;
			// Iterate
			++m_tInstrCurr;	
			break;
		case K_OP_PRINT:
			std::cout << m_tInstrCurr->GetData() << std::endl;    // print data
			++m_tInstrCurr;   // iterate
			break;
		// new semi-math functionality
		case K_OP_SET:
			assert(m_tInstrCurr->GetData()[0] < m_tScript->GetNumVars());
			m_kState.SetVar(m_tInstrCurr->GetData()[0], m_tInstrCurr->GetData()[1]);
			++m_tInstrCurr;
			break;
		case K_OP_INC:
			assert(m_tInstrCurr->GetData()[0] < m_tScript->GetNumVars());
			m_kState.SetVar(m_tInstrCurr->GetData()[0], m_kState.GetVar(m_tInstrCurr->GetData()[0])+1);
			++m_tInstrCurr;
			break;
		case K_OP_DEC:
			assert(m_tInstrCurr->GetData()[0] < m_tScript->GetNumVars());
			m_kState.SetVar(m_tInstrCurr->GetData()[0], m_kState.GetVar(m_tInstrCurr->GetData()[0])-1);
			++m_tInstrCurr;
			break;
		case K_OP_ADD:
			assert(m_tInstrCurr->GetData()[0] < m_tScript->GetNumVars());
			assert(m_tInstrCurr->GetData()[1] < m_tScript->GetNumVars());
			assert(m_tInstrCurr->GetData()[2] < m_tScript->GetNumVars());
			m_kState.SetVar(m_tInstrCurr->GetData()[0],
							m_kState.GetVar(m_tInstrCurr->GetData()[1])
							+ m_kState.GetVar(m_tInstrCurr->GetData()[2]));
			++m_tInstrCurr;
		case K_OP_END:
			// Discontinue the loop
			m_tInstrCurr = 0;	
			break;
		}
	}
}

Volá funkci

///\brief Set current script by id
///\param _uiScriptId Script index
void SelectScript(size_t _uiScriptId) 
{
	assert(_uiScriptId < m_uiNumScripts);  // make sure the id is valid
	m_tScript = &m_vScript[_uiScriptId];
	m_tInstrRoot = m_tScript->GetInstruction();
}

Která nastaví skript podle indexu _uiScriptId
Pak nastaví pole pro všechny proměnné

m_kState.SetDataSize(m_tScript->GetNumVars());


Nová třída
CScriptState
která do pole

std::vector<char>   m_vVarData;	///< Char variable data array

ukládá všechny proměnné, vypadá takto:

class CScriptState
{
	std::vector<char>   m_vVarData;	///< Char variable data array
public:
	///\brief Set data size
	///\param tSize Data size
	void SetDataSize(size_t _tSize)
	{
		m_vVarData.resize(_tSize); 
	}

	///\brief Set variable
	///\param _tSize Variable size
	///\param _cVal Char variable
	void SetVar(size_t _tSize, char _cVal) 
	{
		assert(_tSize < m_vVarData.size()); 
		m_vVarData[_tSize] = _cVal; 
	}
	///\brief Get variable
	///\param _tSize Variable size
	///\retval m_vVarData[_tSize] Char variable
	char GetVar(size_t _tSize) const
	{
		assert(_tSize < m_vVarData.size());
		return m_vVarData[_tSize]; 
	}
	///\brief Get variable data array
	///\retval m_vVarData Char variable data array
	const std::vector<char> & GetDataArray() const  
	{ 
		return m_vVarData; 
	}
};

Operace s proměnnými provádí virtual machine voláním těchto funkcí

// new semi-math functionality
case K_OP_SET:
	assert(m_tInstrCurr->GetData()[0] < m_tScript->GetNumVars());
	m_kState.SetVar(m_tInstrCurr->GetData()[0], m_tInstrCurr->GetData()[1]);
	++m_tInstrCurr;
	break;
case K_OP_INC:
	assert(m_tInstrCurr->GetData()[0] < m_tScript->GetNumVars());
	m_kState.SetVar(m_tInstrCurr->GetData()[0], m_kState.GetVar(m_tInstrCurr->GetData()[0])+1);
	++m_tInstrCurr;
	break;
case K_OP_DEC:
	assert(m_tInstrCurr->GetData()[0] < m_tScript->GetNumVars());
	m_kState.SetVar(m_tInstrCurr->GetData()[0], m_kState.GetVar(m_tInstrCurr->GetData()[0])-1);
	++m_tInstrCurr;
	break;
case K_OP_ADD:
	assert(m_tInstrCurr->GetData()[0] < m_tScript->GetNumVars());
	assert(m_tInstrCurr->GetData()[1] < m_tScript->GetNumVars());
	assert(m_tInstrCurr->GetData()[2] < m_tScript->GetNumVars());
	m_kState.SetVar(m_tInstrCurr->GetData()[0],
					m_kState.GetVar(m_tInstrCurr->GetData()[1])
					+ m_kState.GetVar(m_tInstrCurr->GetData()[2]));
	++m_tInstrCurr;


Výsledky vykreslíme do konzole voláním funkce

///\brief Expose variable state
///\param _rState Reference to state
void CVirtualMachine::ExposeVariableState(const CScriptState& _rState) const
{
	std::vector<char>::const_iterator itr;
	int n = 0;  // Used to denote indexed position of value
	for (itr = _rState.GetDataArray().begin(); itr != _rState.GetDataArray().end(); ++itr, ++n)
	{
		std::cout << n << ": ";
		std::cout << static_cast<int>(*itr);   // Cast for numeric value
		std::cout << std::endl;
	}
}




home / opengl game


Valid XHTML 1.0 Transitional