lua脚本在游戏中的应用

  • 为什么要在游戏中使用脚本语言?

  要解释这个问题首先我们先来了解一下脚本语言的特性:

  1. 学习门槛低,快速上手
  2. 开发成本低,可维护性强
  3. 动态语言,灵活性高

  相对于C/C++这类高复杂性、高风险的编译型语言来说,Lua脚本做为一种轻量级的动态语言,简单的语言特性,精简的核心和基础库,使得语言的学习门槛大大的降低,即使是没有任何游戏经验的人都能快速上手,开发游戏功能。实际上游戏设计是一种十分繁杂的工作,C/C++虽然给我们带来极大的高效性,但同时也不能忽视其复杂性,极易产生BUG,而且对于开发人员的要求非常高。从语言的的抽象层面来说C/C++的抽象低更加适合于底层逻辑的支持,而Lua脚本抽象层次高,更加适合游戏逻辑的实现。脚本语言运行在虚拟机之上,而虚拟机运行在游戏逻辑之上,作为一种解释型语言,我们可以随时修改并及时体现在游戏之中,快速完成开发。C/C++却做不到,对一个巨大的游戏工程,每次修改都需要重新编译,成本很高。设想一下,如果所有的功能都是使用C/C++实现的话,那么对开发人员来说简直是一场灾难。

  • 如何在游戏中使用Lua脚本?

这里就不理论一大堆了,直接手把手教。

  1. 进入Lua官方网站下载Source源代码
  2. 在Visual Studio在新建一个解决方案名为Lua2Game
  3. 在Lua2Game解决方案下新建一个空项目,命名为LuaDll,将从Lua官网下载的源代码src中除luac.c文件之外的源代码拷贝到LuaDll工程,配置项目属性,常规->配置类型为静态库(lib)然后编译LuaDll项目。(luac.c是编译器,lua.c是解释器也就是lua虚拟机)
  4. 在Lua2Game解决方案下新建一个空项目,命名为Game,配置项目属性,常规->配置类型为应用程序(.exe), 这就是游戏demo。在项目属性中,链接器-> 输入->附加依赖项中加入../Debug/LuaDll.lib
  5. 在项目Game中实现脚本引擎CLuaScript(实现C/C++与Lua脚本的互相访问)

LuaScript.h

 1 #ifndef __LUA_SCRIPT_H__
 2 #define __LUA_SCRIPT_H__
 3
 4 #include "GameDef.h"
 5
 6 class CLuaScript
 7 {
 8 public:
 9     CLuaScript();
10     ~CLuaScript();
11
12 public:
13         //实现C/C++对Lua脚本的调用
14     bool    LoadScript(const char* szFileName); //实现lua脚本加载和编译
15     //调用Lua函数
16     bool    CallFunction(char* cFuncName, int nResults, char* cFormat, va_list vlist);
17     bool    CallFunction(const char* cFuncName, int nResults, char* cFormat, ...);
18
19 private:
20     void    RegisterLuaLib(); //注册lua各种基础库
21     bool    RegisterFunctions(TLua_Funcs Funcs[], int n);//将游戏接口注册到lua脚本
22
23 private:
24     lua_State*    m_LuaState; //state 脚本和C\C++搞基就靠它了
25     bool        m_IsLoadScript;
26 };
27
28
29 #endif

LuaScript.cpp

  1 #include <iostream>
  2 #include "LuaScript.h"
  3
  4 CLuaScript::CLuaScript()
  5 {
  6     m_LuaState = luaL_newstate();
  7     if (!m_LuaState)
  8     {
  9         std::cout << "m_LuaState new state failed!" << std::endl;
 10         return;
 11     }
 12     RegisterLuaLib();//注册lua标准库
 13     RegisterFunctions(g_GameFunc, g_GetGameFuncSize());//注册c\c++脚本接口
 14     m_IsLoadScript = false;
 15 }
 16
 17 CLuaScript::~CLuaScript()
 18 {
 19     if (m_LuaState)
 20     {
 21         lua_close(m_LuaState);
 22         m_LuaState = NULL;
 23     }
 24     m_IsLoadScript = false;
 25 }
 26
 27 void CLuaScript::RegisterLuaLib()
 28 {
 29     if (!m_LuaState)
 30     {
 31         return;
 32     }
 33     luaL_openlibs(m_LuaState);
 34 }
 35
 36 bool CLuaScript::RegisterFunctions(TLua_Funcs Funcs[], int n)
 37 {
 38     if (!m_LuaState)
 39     {
 40         return false;
 41     }
 42     for (int i = 0; i < n; i++)
 43         lua_register(m_LuaState, Funcs[i].name, Funcs[i].func);
 44     return true;
 45 }
 46
 47 bool CLuaScript::LoadScript(const char* szFileName)
 48 {
 49     if (!szFileName || szFileName[0] == ‘\0‘)
 50     {
 51         std::cout << "Lua script file illegal!" << std::endl;
 52         return false;
 53     }
 54     if (!m_LuaState)
 55         return false;
 56
 57     m_IsLoadScript = (luaL_dofile(m_LuaState, szFileName) == LUA_OK);
 58     if (!m_IsLoadScript)
 59     {
 60         std::cout << "<LUA_LOAD_ERROR>"<< lua_tostring(m_LuaState, -1) << std::endl;
 61         lua_pop(m_LuaState, 1);
 62     }
 63     return m_IsLoadScript;
 64 }
 65
 66 bool CLuaScript::CallFunction(char* cFuncName, int nResults, char* cFormat, va_list vlist)
 67 {
 68     if (!m_LuaState || !m_IsLoadScript)
 69         return false;
 70
 71     double    nNumber    = 0;
 72     int nInteger    = 0;
 73     char*    cString    = NULL;
 74     void*    pPoint    = NULL;
 75     int i        = 0;
 76     int nArgnum    = 0;
 77     lua_CFunction CFunc = NULL;
 78
 79     lua_getglobal(m_LuaState, cFuncName); //在堆栈中加入需要调用的函数名
 80
 81     while (cFormat[i] != ‘\0‘)
 82     {
 83         switch (cFormat[i])
 84         {
 85         case ‘n‘://输入的数据是double形 NUMBER,Lua来说是Double型
 86         {
 87             nNumber = va_arg(vlist, double);
 88             lua_pushnumber(m_LuaState, nNumber);
 89             nArgnum++;
 90         }
 91         break;
 92
 93         case ‘d‘://输入的数据为整形
 94         {
 95             nInteger = va_arg(vlist, int);
 96             lua_pushinteger(m_LuaState, nInteger);
 97             nArgnum++;
 98         }
 99         break;
100
101         case ‘s‘://字符串型
102         {
103             cString = va_arg(vlist, char *);
104             lua_pushstring(m_LuaState, cString);
105             nArgnum++;
106         }
107         break;
108
109         case ‘N‘://NULL
110         {
111             lua_pushnil(m_LuaState);
112             nArgnum++;
113         }
114         break;
115
116         case ‘f‘://输入的是CFun形,即内部函数形
117         {
118             CFunc = va_arg(vlist, lua_CFunction);
119             lua_pushcfunction(m_LuaState, CFunc);
120             nArgnum++;
121         }
122         break;
123
124         case ‘v‘://输入的是堆栈中Index为nIndex的数据类型
125         {
126             nNumber = va_arg(vlist, int);
127             int nIndex1 = (int)nNumber;
128             lua_pushvalue(m_LuaState, nIndex1);
129             nArgnum++;
130         }
131         break;
132
133         }
134
135         i++;
136     }
137
138     int nRetcode = lua_pcall(m_LuaState, nArgnum, nResults, 0);
139
140     if (nRetcode != 0)
141     {
142         std::cout << "<LUA_CALL_FUNC_ERROR>" << lua_tostring(m_LuaState, -1) << std::endl;
143         lua_pop(m_LuaState, 1);
144         return false;
145     }
146
147     return    true;
148 }
149
150
151 bool CLuaScript::CallFunction(const char* cFuncName, int nResults, char* cFormat, ...)
152 {
153     bool bResult = false;
154     va_list vlist;
155     va_start(vlist, cFormat);
156     bResult = CallFunction((char*)cFuncName, nResults, cFormat, vlist);
157     va_end(vlist);
158     return bResult;
159 }

6,定义用于实现定义给lua脚本的游戏接口

GameDef.h

 1 #ifndef __GAME_DEF_H__
 2 #define __GAME_DEF_H__
 3
 4 extern "C"{
 5 #include "../../LuaDll/src/lua.h"
 6 #include "../../LuaDll/src/lauxlib.h"
 7 #include "../../LuaDll/src/lualib.h"
 8 }
 9
10
11 struct TLua_Funcs
12 {
13     const char *name;
14     lua_CFunction func;
15 };
16
17 extern TLua_Funcs g_GameFunc[];
18 extern int g_GetGameFuncSize();
19
20 #endif

GameDef.cpp

 1 #include "GameDef.h"
 2 #include <direct.h>
 3 #include <iostream>
 4 #include "Core.h"
 5 using namespace std;
 6
 7
 8 int LuaSayHello(lua_State* L)
 9 {
10     cout << "Lua call c/c++:SayHello()" << endl;
11     cout << "Hello Everyone!" << endl;
12     if (lua_gettop(L) < 3)
13         return 0;
14     const char* szName = lua_tostring(L, 1);
15     int nParam1 = lua_tonumber(L, 2);
16     int nParam2 = lua_tonumber(L, 3);
17     cout << "My name is " << szName << endl;
18     lua_pushnumber(L, nParam1 / nParam2);
19     return 1;
20 }
21
22 int LuaStopGame(lua_State* L)
23 {
24     cout << "Lua call c/c++:StopGame()" << endl;
25     cout << "Game is over!" << endl;
26     g_Core.SetRunState(false);
27     return 0;
28 }
29
30 //脚本接口
31 TLua_Funcs g_GameFunc[] = {
32     { "SayHello", LuaSayHello },
33     { "StopGame", LuaStopGame },
34 };
35
36 int g_GetGameFuncSize()
37 {
38     return sizeof(g_GameFunc) / sizeof(TLua_Funcs);
39 }

7,模拟游戏主逻辑

Core.h

 1 #ifndef __CORE_H__
 2 #define __CORE_H__
 3
 4 #include "GameDef.h"
 5 #include "LuaScript.h"
 6
 7 class CCore
 8 {
 9 public:
10     CCore();
11     ~CCore();
12
13 public:
14     bool    Initialize();
15     void    Uninitialize();
16     bool    Breathe();
17     void    SetRunState(bool bRunning);
18
19 private:
20     CLuaScript* m_Script;
21     bool        m_bIsRuning;
22 };
23
24 extern CCore g_Core;
25
26 #endif

Core.cpp

 1 #include "Core.h"
 2 #include <time.h>
 3 #include <iostream>
 4 using namespace std;
 5
 6 CCore g_Core;
 7
 8 CCore::CCore()
 9 {
10     m_Script = NULL;
11     m_bIsRuning = true;
12 }
13
14 CCore::~CCore()
15 {
16     if (m_Script)
17     {
18         delete m_Script;
19         m_Script = NULL;
20     }
21 }
22
23 bool CCore::Initialize()
24 {
25     //do something
26     return true;
27 }
28
29 void CCore::Uninitialize()
30 {
31     //do something
32 }
33
34 void CCore::SetRunState(bool bRunning)
35 {
36     m_bIsRuning = bRunning;
37 }
38
39 bool CCore::Breathe()
40 {
41     if (!m_bIsRuning)
42         return false;
43     static size_t c = 0;
44     size_t now = time(NULL);
45     if (now - c > 3)
46     {
47         c = now;
48         if (!m_Script)
49         {
50             m_Script = new CLuaScript;
51         }
52         if (m_Script)
53         {
54                 //游戏调用lua脚本
55             m_Script->LoadScript("./../test.lua");
56             //调用脚本函数,请参看下面第9点test.lua脚本
57             m_Script->CallFunction("main", 1, "sdd", "luaer", c, c / 18);
58         }
59         else
60         {
61             std::cout << "new CLuaScript failed!" << std::endl;
62             m_bIsRuning = false;
63         }
64     }
65     return true;
66 }

8,最后是实现mian函数(也就是游戏的服务器)

 1 #include <iostream>
 2 #include "Core.h"
 3 using namespace std;
 4
 5 int main(int argc, char* argv[])
 6 {
 7     if (!g_Core.Initialize())
 8     {
 9         g_Core.Uninitialize();
10         return 0;
11     }
12     std::cout << "-----------------Start game!!!-----------------" << std::endl;
13     while (1)
14     {
15         if (!g_Core.Breathe())
16             break;
17     }
18     std::cout << "-----------------Game over!!!-----------------" << std::endl;
19     g_Core.Uninitialize();
20
21     system("PAUSE");
22     return 1;
23 }

9,在工程目录下创建test.lua脚本给游戏调用

 1 function main(szName, num1, num2)
 2     print("main()", szName, num1, num2); --调用lua基础库函数
 3     local nRet = SayHello(szName, num1, num2); --调用游戏接口并返回结果
 4     print("nRet =", nRet);
 5     local nRand = math.random(100);
 6     print("nRand =", nRand)
 7     if nRand > 80 then
 8         StopGame(); --停止游戏
 9     end
10     return 1;
11 end

运行结果:

 1 -----------------Start game!!!-----------------
 2 main()  luaer   1410876602      78382033
 3 Lua call c/c++:SayHello()
 4 Hello Everyone!
 5 My name is luaer
 6 nRet =  18
 7 nRand = 1
 8 main()  luaer   1410876606      78382033
 9 Lua call c/c++:SayHello()
10 Hello Everyone!
11 My name is luaer
12 nRet =  18
13 nRand = 57
14 main()  luaer   1410876610      78382033
15 Lua call c/c++:SayHello()
16 Hello Everyone!
17 My name is luaer
18 nRet =  18
19 nRand = 20
20 main()  luaer   1410876614      78382034
21 Lua call c/c++:SayHello()
22 Hello Everyone!
23 My name is luaer
24 nRet =  18
25 nRand = 81
26 Lua call c/c++:StopGame()
27 Game is over!
28 -----------------Game over!!!-----------------
29 Press any key to continue . . .

Demo工程的完整版本可以通过github上获得。

时间: 2024-10-08 08:55:04

lua脚本在游戏中的应用的相关文章

【COCOS2DX-LUA 脚本开发之一】在Cocos2dX游戏中使用Lua脚本进行游戏开发(基础篇)并介绍脚本在游戏中详细用途!

[COCOS2DX-LUA 脚本开发之一]在Cocos2dX游戏中使用Lua脚本进行游戏开发(基础篇)并介绍脚本在游戏中详细用途! 分类: [Cocos2dx Lua 脚本开发 ] 2012-04-16 10:08 30803人阅读 评论(18) 收藏 举报 游戏脚本luaanimationpython 本站文章均为李华明Himi原创,转载务必在明显处注明:转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/iphone-cocos2dx/681.htm

在Unity3d中解析Lua脚本的方法

由于近期项目中提出了热更新的需求,因此本周末在Lua的陪伴下度过.对Lua与Unity3d的搭配使用,仅仅达到了一个初窥门径的程度,记录一二于此.水平有限,欢迎批评指正. 网络上关于Lua脚本和Unity3d的配合使用的资料不多,例子工程大多相同.大概了解到针对性的插件有uLua.UniLua.KopiLua三种.试用了前两种,抛开效率与安全性不说,感觉uLua试用起来比较简单,本文只介绍uLua的使用步骤. uLua的原理是在Unity3d中解析字符串形式的Lua脚本,让Lua与C#相互传递参

在redis中使用lua脚本

在实际工作过程中,可以使用lua脚本来解决一些需要保证原子性的问题,而且lua脚本可以缓存在redis服务器上,势必会增加性能. 不过lua也会有很多限制,在使用的时候要注意. 在Redis中执行Lua脚本有两种方法:eval和evalsha eval EVAL script numkeys key [key ...] arg [arg ...] 其中: <1> script:你的lua脚本 <2> numkeys:  key的个数 <3> key:redis中各种数据

Lua脚本编程:Lua语言入门

Lua是一门简单而强大的语言,其本身强大的扩展性使得这门语言在游戏设计等领域发挥着重要的作用.博主曾在Unity3D中使用过这门语言,并且针对Lua和Unity.C++等方面的内容进行了学习和讨论.最近因为在[游戏脚本高级编程]这本书中详细介绍了Lua脚本的相关内容,因此在这里记录下博主的读书心得,方便以后在需要的时候查阅. Lua系统构成 Lua系统由Lua链接库.Luac编译器.Lua解释器三部分构成. * Lua链接库主要由lua.lib和lua.h这两个文件组成.Lua链接库主要负责对自

《Redis设计与实现》学习笔记-Lua脚本

Redis从2.6开始支持Lua脚本,和事务的功能类似,可以通过Lua脚本原子的执行多个Redis命令.Redis提供了EVAL和EVALSHA命令执行lua脚本. 创建并修改Lua坏境 Redis在服务器内嵌了一个Lua坏境,并进行了一系列的修改,从而确保这个Lua坏境可以满足Redis服务器的需要,通过下列步骤创建并修改Lua坏境: 创建一个基础Lua坏境,通过调用Lua的C API函数lua_open. 载入多个函数库到Lua坏境中,让Lua脚本可以使用这些函数来进行数据操作.包括Lua核

Redis Lua 脚本使用

Lua语言提供了如下几种数据类型:booleans(布尔).numbers(数值).strings(字符串).tables(表格). 下面是一些 Lua 的示例,里面注释部分会讲解相关的作用: -- -- -- 拿客 -- 网站:www.coderknock.com -- QQ群:213732117 -- 三产 创建于 2017年06月15日 12:04:54. -- 描述: -- -- local strings website = "coderknock.com" print(we

Redis(六)Lua脚本的支持

Redis为什么需要Lua脚本的支持 当应用需要Redis完成一些Redis命令不支持的特性时,要么扩展Redis client或者更甚至编写c扩展Redis server.这都大大造成了应用的实现的难度.在此基础上,Redis通过内置Lua解释器,Redis client可以发起执行Lua脚本,完成特殊的功能需求. Redis中使用Lua脚本 在Redis中可以通过使用eval和evalsha命令提供对执行Lua的支持. eval语法: EVAL script numkeys key [key

在Unity中使用Lua脚本:语言层和游戏逻辑粘合层处理

前言:为什么要用Lua 首先要说,所有编程语言里面,我最喜欢的还是C#,VisualStudio+C#,只能说太舒服了.所以说,为什么非要在Unity里面用Lua呢?可能主要是闲的蛋疼.....另外还有一些次要原因: 方便做功能的热更新: Lua语言的深度和广度都不大,易学易用,可以降低项目成本. C#与Lua互相调用的方案 坦白来将,我并没有对现在C#与Lua互相调用的所有库进行一个仔细的调研,大概搜了一下,找到这样几个: slua:https://github.com/pangweiwei/

Java中使用Lua脚本语言(转)

Lua是一个实用的脚本语言,相对于Python来说,比较小巧,但它功能并不逊色,特别是在游戏开发中非常实用(WoW采用的就是Lua作为脚本的).Lua在C\C++的实现我就不多说了,网上随便一搜,到处都是这方面的介绍,我想说的是如何在Java下使用Lua以提高编程效率.增强你的程序可扩展性. 首先,要在Java上使用Lua脚本,必须有关于Lua脚本解释器以及Java程序可以访问这些脚本的相关API,即相关类库.我使用的是一个叫做LuaJava的开源项目,可以在: http://www.keple