zoukankan      html  css  js  c++  java
  • 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 脚本和CC++搞基就靠它了
    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());//注册cc++脚本接口
     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] == '')
     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] != '')
     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上获得。

  • 相关阅读:
    Java学习二十九天
    Java学习二十八天
    47. Permutations II 全排列可重复版本
    46. Permutations 全排列,无重复
    subset ii 子集 有重复元素
    339. Nested List Weight Sum 339.嵌套列表权重总和
    251. Flatten 2D Vector 平铺二维矩阵
    217. Contains Duplicate数组重复元素
    209. Minimum Size Subarray Sum 结果大于等于目标的最小长度数组
    438. Find All Anagrams in a String 查找字符串中的所有Anagrams
  • 原文地址:https://www.cnblogs.com/borey/p/5622842.html
Copyright © 2011-2022 走看看