作者: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