使用C++和Boost庫寫一個自己的腳本引擎

作者:CYM

衆所周知,腳本的重要性不言而喻.特別是在遊戲引擎開發中,

也許一個遊戲引擎的物理,人工智能,動畫,音效,特效可以使用第三方庫來實現.,但是腳本引擎卻不能是第三方的,你必須自己寫

有了腳本引擎我們就可以很方便的將第三方庫整合到自己的遊戲引擎中,,也讓遊戲開發變得簡單快速方便修改..

,可能你有使用過腳本語言,但是自己寫一個腳本引擎就需要花一點時間了.

下面這個例子我會教你寫一個極其簡單的腳本引擎,這是一個基於命令的腳本引擎..

這個腳本解析類有以下幾個函數:

 void LoadScript(char* fileName);           //載入腳本文件
 void RunScript();                                   //運行腳本
 void UnloadScript();                              //卸載腳本
 void PrintScript();                                  //打印腳本的基本信息,方便調試

有以下幾個私有函數:

 //獲取腳本命令和參數函數(這兩個函數現在可有可無,先保留着,方便以後擴展)
 const string GetScriptCmd(const string line) const;
 const string GetScriptPara(const string line) const;
 //獲取腳本命令和參數的集合
 const vector<string> GetScriptCmdPara(const string line) const;

有以下幾個成員變量

 //裝載腳本命令的容器
 boost::array<string,CYM_MAX_SOURCE_LINE> m_strSource;
 int m_iLine; //腳本的行數

還需要定義幾個宏,他的功能是用來比較命令

//每個腳本文件所支持最大的行數和每行腳本所支持的最大字符數
#define CYM_MAX_SOURCE_LINE 4096
#define CYM_MAX_LINE_CHAR 100

//定義腳本命令/腳本行數
#define DNF_CMD_PRINTSTR "PrintStr"
#define DNF_CMD_PRINTSTRLOOP "PrintStrLoop"
#define DNF_CMD_NEWLINE "NewLine"
#define DNF_CMD_PRESSANYKEY "PressAnyKey"

//中文腳本命令
#define DNF_CMD_PRINTSTR_CN "打印"
#define DNF_CMD_PRINTSTRLOOP_CN "循環打印"
#define DNF_CMD_NEWLINE_CN "換行"
#define DNF_CMD_PRESSANYKEY_CN "按任意鍵"

正如你看到的一樣這個腳本解析類支持中英雙語:是不是很酷

注意這個腳本解析類用到了Boost庫,如果在此之前你沒有接觸過boost庫,你可能還需要去下載Boost庫,並且熟悉一下Boost

ps:Boost庫是一個有着工業級強度的C++庫,有着C++準標準庫的美譽

1.從文件中載入腳本:

//裝載腳本/運行腳本/卸載腳本
void CCYMScript::LoadScript(char* fileName)
{
 m_iLine=0; //腳本的行數
 int i=0;   //用於循環
 ifstream inFile;

 inFile.open(fileName);

 if(inFile.good())
  cout<<"打開腳本文件成功!`"<<endl;
 else if(inFile.bad())
  cout<<"打開腳本文件有錯誤!`"<<endl;

 //載入腳本
 while(getline(inFile,m_strSource[i]))
 {
  ++i;
  ++m_iLine;
 }

 inFile.close();

}

 

這段代碼比較簡單:

1.使用ifstream 的open()函數打開腳本文件

2.通過循環和getline()函數將腳本文件中的內容載入到一個用於儲存腳本命令的數組中注意這裏的數組使用到了boost庫中的array數組管理器,他和數組一樣方便,速度和原始數組相差無幾,但是更加安全..

 

2.打印腳本的基本信息

//輸出字符串
void CCYMScript::PrintScript()
{
 int i=0; //用於尋循環

 //打印腳本的基本信息,行數
 cout<<"命令的行數:"<<m_iLine<<endl;
 //打印腳本的所有,命令
 for(i=0;i<m_iLine;++i)
 {
  cout<<m_strSource[i]<<endl;
 }

 cout<<"--------------------------------"<<endl;

 //cout<<GetScriptCmd(m_strSource[0])<<endl;
 //cout<<GetScriptPara(m_strSource[0])<<endl;

 //cout<<GetScriptCmdPara(m_strSource[0])[0]<<endl;
}

這段代碼不需要解釋了....都看得懂

3.獲取命令和相關的參數:

 //獲取腳本命令和參數的集合
const vector<string> CCYMScript::GetScriptCmdPara(const string line) const
{
 vector<string> strVtr;
 string tok;
 stringstream str(line);
 while(getline(str,tok,' '))
 {
  strVtr.push_back(tok);
 }  

 return strVtr;
}

 

1.這段代碼使用了字符串流,,我們使用傳入的字符串來構造一個字符串流

2.使用getline()函數根據空格來分割字符串,每分割一個字符串就將他裝入一個字符串容器中.顯而易見,,這個容器的第一個元素永遠是

命令字符,,隨後的所有元素都將被視爲這個命令的參數..然後我們返回這個容器;

 

4.執行腳本:

void CCYMScript::RunScript()
{
 //用於儲存腳本命令和參數的容器
 vector<string> strVtr;
 int i=0; //用於循環

 for(i=0;i<m_iLine-1;++i)
 {
  strVtr.clear();
  strVtr=GetScriptCmdPara(m_strSource[i]);
  
  //比較命令
  //打印命令
  if(0==strVtr[0].compare(DNF_CMD_PRINTSTR)||0==strVtr[0].compare(DNF_CMD_PRINTSTR_CN))
  {
   cout<<strVtr[1]<<endl;
  }
  //循環打印
  else if(0==strVtr[0].compare(DNF_CMD_PRINTSTRLOOP)||0==strVtr[0].compare(DNF_CMD_PRINTSTRLOOP_CN))
  {
   for(int i=0;i<boost::lexical_cast<int>(strVtr[2]);++i)
   {
    cout<<strVtr[1]<<endl;
   }
  }
  //換行命令
  else if(0==strVtr[0].compare(DNF_CMD_NEWLINE)||0==strVtr[0].compare(DNF_CMD_NEWLINE_CN))
  {
   for(int i=0;i<boost::lexical_cast<int>(strVtr[1]);++i)
   {
    cout<<endl;
   }
  }
 }
}

1.這段命令也比較簡單:我們首先使用vector容器來獲取已經分割好的字符串..

2.使用if語句使每個命令和我們之前定義的宏來進行比較,如果是相同的,那就執行這個命令,否則忽略..


想要加上明確的註釋語句也是可以的,只要在for循環中加入一個if判斷語句,只需要使用boost庫中的starts_with()函數判斷

選定字符串中是否有"//"註釋語句,具體用法如下:

//註釋語句

if(boost::starts_with(m_strSource[i],"//"))

{

cout<<"這是註釋"<<endl;

}

 

卸載腳本:

注意這裏我並沒有實現UnloadScript()函數,因爲我使用了Boost庫中的array來管理數組.

他會在退出作用域時自動釋放內存,所以這裏沒有對保存命令的數組進行手動釋放,,但是這裏

我還是保留了UnloadScript()函數,方便以後擴展.

 

5.使用我們的腳本解析類

#include "CYMScript.h"

using namespace boost;
using namespace std;

void main()
{

 CCYMScript* script=new CCYMScript();

 script->LoadScript("test.txt");
 script->PrintScript();
 
 script->RunScript();

 getchar();

}

--------------------------------------------------以下是演示---------------------------------------------------------------------

//這是我寫的腳本文件,很簡單就幾句話,打印字符,換行,循環打印....

//注意他支持中英雙語,,所以我們也可以同時使用中文和英文

打印 chengyimingvb
換行 1
PrintStr 陳宜明
NewLine 2
循環打印 chenyimin 3


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章