Hello Lua,I'm C++ —— 用C++类管理Lua
2010-11-18 13:48 凌云健笔 阅读(596) 评论(1) 编辑 收藏 举报1、什么是脚本
脚本,英文表述为script,可翻译成“草稿”的意思,所以这就需要有一个“中间程序”把“草稿”翻译出来,然后交给计算机去执行。据Google资料上说,脚本语言又叫动态语言(未经考证),是一种为了缩短传统的编写- 编译-链接-运行(edit-compile-link-run)过程而创建的一种计算机编程语言,用来控制软件应用程序。几乎所有计算机系统的各个层次都有一种脚本语言,包括计算机游戏、网络应用程序、文字处理文档、网络软件等。在许多方面,高级编程语言和脚本语言之间互相交叉,二者之间没有明确的界限。目前脚本语言正成为开发者的重要工具,脚本不仅解决了C/C 无法解决的开发效率难题,还降低了开发的成本和风险。因此脚本技术在游戏业内蓬勃发展起来,并且已经成为游戏业中不可或缺的一环。
在游戏中,其作用一般是用来控制游戏(当然包括其他应用程序)中某些经常修改的过程或者数值;如果你写了一个角色扮演游戏项目,其中的一些游戏相关信息需要经常改动;如果直接硬编码在程序中,每次改动就要编译一次;对于小项目来说,编译所耗费的时间还可以接受,如果是大型程序,那么将难以忍受;这时我们可以求助于脚本。我们只需改动一下脚本,而程序本身就不许要重新编译,帮助我们更灵活更方便地去开发游戏。
2、脚本Lua
目前比较流行的脚本包括Python、Lua、ruby和Erlang等。Python和Lua是网络游戏开发的中坚力量;ruby凭借rubyonrails的web开发框架的强劲力量,其在游戏业的发展前途亦不可小视;Erlang在高并发性上的原生优势吸引了无数追求高性能网游服务器的开发人员的目光。脚本不是游戏业的新技术,但脚本为人所知悉,却应归功于Blizzard 的大规模多玩家在线 RPG,World of Warcraft(WOW)在世界范围的流行。通过Wow强悍的自定义界面系统,Lua这一脚本语言一举成名天下知。
WoW 的用户界面完全使用 Lua 实现;开发人员提供一些基本的 API 调用,实现与渲染引擎的交互,并请求关于世界的数据。这样一来,将用户界面代码置于沙盒中,使之远离游戏本身的干扰,从而提高了安全性和可靠性。反过来,Blizzard 可以向玩家开放用户界面,使玩家可以按照自己的喜好定制代码,实现他们与游戏独特的交互 的方式。
Lua小巧而高效,是用可移植的 C 编写的;作为C的扩展,能与C自然地结合与交互,所以常被用于写游戏逻辑,已经成为游戏里最常用的一种脚本。在当前的游戏开发行业中,除了WoW 使用了该脚本外,现在大多数比较流行的游戏也都使用了该脚本,如和网易经典“西游系列”、腾讯的《QQgame》、搜狐的《天龙八部》等。不仅如此,现今的游戏引擎也都提供了脚本接口,无论是商业的unreal引擎还是开源的OGRE,无一例外【1】。据传言,巨人网络的《征途》中好像也采用了LUA脚本技术。
Lua 还可以与 C++ 一起使用,核心语言非常易于移植;Lua 是在 MIT 许可下发布的,可完全免费用于任何用途,包括商业用途。因此,我们有权限随意地将它嵌入到任意应用程序中。
3、C/C++中嵌入Lua
当在Lua和C之间交换数据时,我们面临着两个问题:动态与静态类型系统的不匹配和自动与手动内存管理的不一致。解决之道就是利用“栈”!在C/C++中调用Lua时需要遵循以下步骤:
函数入栈 -> 参数入栈 -> 调用函数 -> 返回结果
lua_State 在所有的嵌入程序中都扮演着重要的角色,必不可少。lua_State 中放的是 lua 虚拟机中的环境表、注册表、运行堆栈、虚拟机的上下文等数据。从一个主线程中创建出来的新的 lua_State 会共享大部分数据,但会拥有一个独立的运行堆栈。为了更方便的应用,本文对Lua 中的C API进行了简单封装,代码如下:
声明:(因为CLuaState含有指针,所以拷贝操作时涉及到“深拷贝”、“浅拷贝”的问题。此处将其设置为禁止拷贝)
1 #ifndef LUASTATE_H
2 #define LUASTATE_H
3
4 #include <vector>
5
6 typedef struct lua_State lua_State;
7
8 enum SV_TYPE
9 {
10 SVT_INVALID = 0,
11 SVT_BOOL,
12 SVT_INT,
13 SVT_FLOAT,
14 SVT_DOUBLE,
15 SVT_CHAR,
16 };
17
18 union SV_DATA
19 {
20 bool bData;
21 int iData;
22 float fData;
23 double dData;
24 char cData[32];
25 };
26
27 struct CScriptValue
28 {
29 SV_TYPE m_eType;
30 SV_DATA m_uData;
31 };
32
33 class Uncopyable
34 {
35 protected:
36 Uncopyable() {}
37 ~Uncopyable() {}
38
39 private:
40 Uncopyable(const Uncopyable&);
41 Uncopyable& operator=(const Uncopyable&);
42 };
43
44 class CLuaState : private Uncopyable
45 {
46 public:
47 CLuaState();
48 ~CLuaState();
49
50 /*
51 功能 — 调用Lua
52 参数 — szFileName:Lua文件名
53 szMethed :调用函数名
54 args :输入参数
55 cOutFormat:输出格式
56 result :输出结果
57 */
58
59 bool Call(const char * szFileName, const char * szMethed, std::vector<CScriptValue>& args, char* cOutFormat, std::vector<CScriptValue>& result );
60
61
62 private:
63 lua_State* m_pState;
64 };
65 #endif //LUASTATE_H
实现:
1 #include "LuaState.h"
2 #include "include/lua.hpp"
3
4 CLuaState::CLuaState()
5 {
6 m_pState = luaL_newstate();
7 luaL_openlibs( m_pState );
8 }
9
10 CLuaState::~CLuaState()
11 {
12 if(m_pState != NULL)
13 {
14 lua_close( m_pState );
15 m_pState = NULL;
16 }
17 }
18
19 bool CLuaState::Call(const char * szFileName, const char * szMethed, std::vector<CScriptValue>& args, char* cOutFormat, std::vector<CScriptValue>& result )
20 {
21 //载入lua脚本文件
22 luaL_dofile(m_pState,szFileName);
23
24 // 获取Lua脚本中的函数
25 lua_getglobal(m_pState,szMethed);
26
27 size_t nArgs = args.size();
28 // 向栈中压入函数所需要的参数
29 for(size_t i=0; i<nArgs; i++)
30 {
31 if(args[i].m_eType == SVT_BOOL)
32 lua_pushnumber(m_pState, args[i].m_uData.bData);
33 else if(args[i].m_eType == SVT_INT)
34 lua_pushnumber(m_pState, args[i].m_uData.iData);
35 else if(args[i].m_eType == SVT_FLOAT)
36 lua_pushnumber(m_pState, args[i].m_uData.fData);
37 else if(args[i].m_eType == SVT_DOUBLE)
38 lua_pushnumber(m_pState, args[i].m_uData.dData);
39 else if(args[i].m_eType == SVT_CHAR)
40 lua_pushstring(m_pState, args[i].m_uData.cData);
41 }
42
43 //获取输出个数
44 int nResult = 0;
45 char* format = cOutFormat;
46 while (*format++) nResult++;
47
48 if (lua_pcall(m_pState, nArgs, nResult, 0) != 0)
49 return false;
50
51 int nPos = -nResult;
52 while (*cOutFormat)
53 {
54 CScriptValue val;
55 switch (*cOutFormat++)
56 {
57 case 'b':
58 if (lua_isnumber(m_pState, nPos))
59 {
60 val.m_eType = SVT_BOOL;
61 val.m_uData.bData = lua_tonumber(m_pState, nPos);
62 result.push_back(val);
63 }
64 break;
65 case 'i':
66 if (lua_isnumber(m_pState, nPos))
67 {
68 val.m_eType = SVT_INT;
69 val.m_uData.iData = lua_tonumber(m_pState, nPos);
70 result.push_back(val);
71 }
72 break;
73 case 'f':
74 if (lua_isnumber(m_pState, nPos))
75 {
76 val.m_eType = SVT_FLOAT;
77 val.m_uData.fData = lua_tonumber(m_pState, nPos);
78 result.push_back(val);
79 }
80 break;
81 case 'd':
82 if (lua_isnumber(m_pState, nPos))
83 {
84 val.m_eType = SVT_DOUBLE;
85 val.m_uData.dData = lua_tonumber(m_pState, nPos);
86 result.push_back(val);
87 }
88 break;
89 case 'c':
90 if (lua_isstring(m_pState, nPos))
91 {
92 val.m_eType = SVT_CHAR;
93 const char* res = lua_tostring(m_pState, nPos);
94 int iStart = 0;
95 while (*res) val.m_uData.cData[iStart++] = *res++;
96 val.m_uData.cData[iStart] ='\0';
97 result.push_back(val);
98 }
99 break;
100 default:
101 break;
102 }
103 nPos++;
104 }
105
106 return (nResult == result.size());
107 }
108
测试代码:
#include "LuaState.h"
int main()
{
CLuaState a;
std::vector<CScriptValue> args;
std::vector<CScriptValue> result;
// test 1:
/*CScriptValue x; x.m_eType = SVT_INT; x.m_uData.iData = 3; args.push_back(x);
CScriptValue y; y.m_eType = SVT_INT; y.m_uData.iData = 6; args.push_back(y);
char* format ="i";
if(a.Call("function.lua","Add",args, format, result))
{
printf("%d\n", result[0].m_uData.iData);
}*/
// test 2:
char* format ="cc";
if(a.Call("function.lua","Hello",args, format, result))
{
printf("%s %s\n", result[0].m_uData.cData,result[1].m_uData.cData);
}
system("pause");
return 0;
}
附 function.lua 代码:
function Add(a,b)
return a+b
end
function Hello()
x = "hello Lua"
y = "I am C++"
return x,y
end
测试输出:
注:【1】引自某一博友,忘记了具体出处;如您看到,请与我联系!
作者: 凌云健笔
出处:http://www.cnblogs.com/lijian2010/
版权:本文版权归作者和博客园共有
转载:欢迎转载,为了保存作者的创作热情,请按要求【转载
要求:未经作者同意,必须保留此段声明;必须在文章中给出原文连接;否则必究法律责任