lua脚本在游戏中的应用
- 为什么要在游戏中使用脚本语言?
要解释这个问题首先我们先来了解一下脚本语言的特性:
- 学习门槛低,快速上手
- 开发成本低,可维护性强
- 动态语言,灵活性高
相对于C/C++这类高复杂性、高风险的编译型语言来说,Lua脚本做为一种轻量级的动态语言,简单的语言特性,精简的核心和基础库,使得语言的学习门槛大大的降低,即使是没有任何游戏经验的人都能快速上手,开发游戏功能。实际上游戏设计是一种十分繁杂的工作,C/C++虽然给我们带来极大的高效性,但同时也不能忽视其复杂性,极易产生BUG,而且对于开发人员的要求非常高。从语言的的抽象层面来说C/C++的抽象低更加适合于底层逻辑的支持,而Lua脚本抽象层次高,更加适合游戏逻辑的实现。脚本语言运行在虚拟机之上,而虚拟机运行在游戏逻辑之上,作为一种解释型语言,我们可以随时修改并及时体现在游戏之中,快速完成开发。C/C++却做不到,对一个巨大的游戏工程,每次修改都需要重新编译,成本很高。设想一下,如果所有的功能都是使用C/C++实现的话,那么对开发人员来说简直是一场灾难。
- 如何在游戏中使用Lua脚本?
这里就不理论一大堆了,直接手把手教。
- 进入Lua官方网站下载Source源代码
- 在Visual Studio在新建一个解决方案名为Lua2Game
- 在Lua2Game解决方案下新建一个空项目,命名为LuaDll,将从Lua官网下载的源代码src中除luac.c文件之外的源代码拷贝到LuaDll工程,配置项目属性,常规->配置类型为静态库(lib)然后编译LuaDll项目。(luac.c是编译器,lua.c是解释器也就是lua虚拟机)
- 在Lua2Game解决方案下新建一个空项目,命名为Game,配置项目属性,常规->配置类型为应用程序(.exe), 这就是游戏demo。在项目属性中,链接器-> 输入->附加依赖项中加入../Debug/LuaDll.lib
- 在项目Game中实现脚本引擎CLuaScript(实现C/C++与Lua脚本的互相访问)
LuaScript.h
#ifndef __LUA_SCRIPT_H__
#define __LUA_SCRIPT_H__ #include "GameDef.h" class CLuaScript
{
public:
CLuaScript();
~CLuaScript(); public:
//实现C/C++对Lua脚本的调用
bool LoadScript(const char* szFileName); //实现lua脚本加载和编译
//调用Lua函数
bool CallFunction(char* cFuncName, int nResults, char* cFormat, va_list vlist);
bool CallFunction(const char* cFuncName, int nResults, char* cFormat, ...); private:
void RegisterLuaLib(); //注册lua各种基础库
bool RegisterFunctions(TLua_Funcs Funcs[], int n);//将游戏接口注册到lua脚本 private:
lua_State* m_LuaState; //state 脚本和C\C++搞基就靠它了
bool m_IsLoadScript;
}; #endif
LuaScript.cpp
#include <iostream>
#include "LuaScript.h" CLuaScript::CLuaScript()
{
m_LuaState = luaL_newstate();
if (!m_LuaState)
{
std::cout << "m_LuaState new state failed!" << std::endl;
return;
}
RegisterLuaLib();//注册lua标准库
RegisterFunctions(g_GameFunc, g_GetGameFuncSize());//注册c\c++脚本接口
m_IsLoadScript = false;
} CLuaScript::~CLuaScript()
{
if (m_LuaState)
{
lua_close(m_LuaState);
m_LuaState = NULL;
}
m_IsLoadScript = false;
} void CLuaScript::RegisterLuaLib()
{
if (!m_LuaState)
{
return;
}
luaL_openlibs(m_LuaState);
} bool CLuaScript::RegisterFunctions(TLua_Funcs Funcs[], int n)
{
if (!m_LuaState)
{
return false;
}
for (int i = ; i < n; i++)
lua_register(m_LuaState, Funcs[i].name, Funcs[i].func);
return true;
} bool CLuaScript::LoadScript(const char* szFileName)
{
if (!szFileName || szFileName[] == '\0')
{
std::cout << "Lua script file illegal!" << std::endl;
return false;
}
if (!m_LuaState)
return false; m_IsLoadScript = (luaL_dofile(m_LuaState, szFileName) == LUA_OK);
if (!m_IsLoadScript)
{
std::cout << "<LUA_LOAD_ERROR>"<< lua_tostring(m_LuaState, -) << std::endl;
lua_pop(m_LuaState, );
}
return m_IsLoadScript;
} bool CLuaScript::CallFunction(char* cFuncName, int nResults, char* cFormat, va_list vlist)
{
if (!m_LuaState || !m_IsLoadScript)
return false; double nNumber = ;
int nInteger = ;
char* cString = NULL;
void* pPoint = NULL;
int i = ;
int nArgnum = ;
lua_CFunction CFunc = NULL; lua_getglobal(m_LuaState, cFuncName); //在堆栈中加入需要调用的函数名 while (cFormat[i] != '\0')
{
switch (cFormat[i])
{
case 'n'://输入的数据是double形 NUMBER,Lua来说是Double型
{
nNumber = va_arg(vlist, double);
lua_pushnumber(m_LuaState, nNumber);
nArgnum++;
}
break; case 'd'://输入的数据为整形
{
nInteger = va_arg(vlist, int);
lua_pushinteger(m_LuaState, nInteger);
nArgnum++;
}
break; case 's'://字符串型
{
cString = va_arg(vlist, char *);
lua_pushstring(m_LuaState, cString);
nArgnum++;
}
break; case 'N'://NULL
{
lua_pushnil(m_LuaState);
nArgnum++;
}
break; case 'f'://输入的是CFun形,即内部函数形
{
CFunc = va_arg(vlist, lua_CFunction);
lua_pushcfunction(m_LuaState, CFunc);
nArgnum++;
}
break; case 'v'://输入的是堆栈中Index为nIndex的数据类型
{
nNumber = va_arg(vlist, int);
int nIndex1 = (int)nNumber;
lua_pushvalue(m_LuaState, nIndex1);
nArgnum++;
}
break; } i++;
} int nRetcode = lua_pcall(m_LuaState, nArgnum, nResults, ); if (nRetcode != )
{
std::cout << "<LUA_CALL_FUNC_ERROR>" << lua_tostring(m_LuaState, -) << std::endl;
lua_pop(m_LuaState, );
return false;
} return true;
} bool CLuaScript::CallFunction(const char* cFuncName, int nResults, char* cFormat, ...)
{
bool bResult = false;
va_list vlist;
va_start(vlist, cFormat);
bResult = CallFunction((char*)cFuncName, nResults, cFormat, vlist);
va_end(vlist);
return bResult;
}
6,定义用于实现定义给lua脚本的游戏接口
GameDef.h
#ifndef __GAME_DEF_H__
#define __GAME_DEF_H__ extern "C"{
#include "../../LuaDll/src/lua.h"
#include "../../LuaDll/src/lauxlib.h"
#include "../../LuaDll/src/lualib.h"
} struct TLua_Funcs
{
const char *name;
lua_CFunction func;
}; extern TLua_Funcs g_GameFunc[];
extern int g_GetGameFuncSize(); #endif
GameDef.cpp
#include "GameDef.h"
#include <direct.h>
#include <iostream>
#include "Core.h"
using namespace std; int LuaSayHello(lua_State* L)
{
cout << "Lua call c/c++:SayHello()" << endl;
cout << "Hello Everyone!" << endl;
if (lua_gettop(L) < )
return ;
const char* szName = lua_tostring(L, );
int nParam1 = lua_tonumber(L, );
int nParam2 = lua_tonumber(L, );
cout << "My name is " << szName << endl;
lua_pushnumber(L, nParam1 / nParam2);
return ;
} int LuaStopGame(lua_State* L)
{
cout << "Lua call c/c++:StopGame()" << endl;
cout << "Game is over!" << endl;
g_Core.SetRunState(false);
return ;
} //脚本接口
TLua_Funcs g_GameFunc[] = {
{ "SayHello", LuaSayHello },
{ "StopGame", LuaStopGame },
}; int g_GetGameFuncSize()
{
return sizeof(g_GameFunc) / sizeof(TLua_Funcs);
}
7,模拟游戏主逻辑
Core.h
#ifndef __CORE_H__
#define __CORE_H__ #include "GameDef.h"
#include "LuaScript.h" class CCore
{
public:
CCore();
~CCore(); public:
bool Initialize();
void Uninitialize();
bool Breathe();
void SetRunState(bool bRunning); private:
CLuaScript* m_Script;
bool m_bIsRuning;
}; extern CCore g_Core; #endif
Core.cpp
#include "Core.h"
#include <time.h>
#include <iostream>
using namespace std; CCore g_Core; CCore::CCore()
{
m_Script = NULL;
m_bIsRuning = true;
} CCore::~CCore()
{
if (m_Script)
{
delete m_Script;
m_Script = NULL;
}
} bool CCore::Initialize()
{
//do something
return true;
} void CCore::Uninitialize()
{
//do something
} void CCore::SetRunState(bool bRunning)
{
m_bIsRuning = bRunning;
} bool CCore::Breathe()
{
if (!m_bIsRuning)
return false;
static size_t c = ;
size_t now = time(NULL);
if (now - c > )
{
c = now;
if (!m_Script)
{
m_Script = new CLuaScript;
}
if (m_Script)
{
//游戏调用lua脚本
m_Script->LoadScript("./../test.lua");
//调用脚本函数,请参看下面第9点test.lua脚本
m_Script->CallFunction("main", , "sdd", "luaer", c, c / );
}
else
{
std::cout << "new CLuaScript failed!" << std::endl;
m_bIsRuning = false;
}
}
return true;
}
8,最后是实现mian函数(也就是游戏的服务器)
#include <iostream>
#include "Core.h"
using namespace std; int main(int argc, char* argv[])
{
if (!g_Core.Initialize())
{
g_Core.Uninitialize();
return ;
}
std::cout << "-----------------Start game!!!-----------------" << std::endl;
while ()
{
if (!g_Core.Breathe())
break;
}
std::cout << "-----------------Game over!!!-----------------" << std::endl;
g_Core.Uninitialize(); system("PAUSE");
return ;
}
9,在工程目录下创建test.lua脚本给游戏调用
function main(szName, num1, num2)
print("main()", szName, num1, num2); --调用lua基础库函数
local nRet = SayHello(szName, num1, num2); --调用游戏接口并返回结果
print("nRet =", nRet);
local nRand = math.random();
print("nRand =", nRand)
if nRand > then
StopGame(); --停止游戏
end
return ;
end
运行结果:
-----------------Start game!!!-----------------
main() luaer
Lua call c/c++:SayHello()
Hello Everyone!
My name is luaer
nRet =
nRand =
main() luaer
Lua call c/c++:SayHello()
Hello Everyone!
My name is luaer
nRet =
nRand =
main() luaer
Lua call c/c++:SayHello()
Hello Everyone!
My name is luaer
nRet =
nRand =
main() luaer
Lua call c/c++:SayHello()
Hello Everyone!
My name is luaer
nRet =
nRand =
Lua call c/c++:StopGame()
Game is over!
-----------------Game over!!!-----------------
Press any key to continue . . .
Demo工程的完整版本可以通过github上获得。
lua脚本在游戏中的应用的更多相关文章
- 【COCOS2DX-LUA 脚本开发之一】在Cocos2dX游戏中使用Lua脚本进行游戏开发(基础篇)并介绍脚本在游戏中详细用途!
[COCOS2DX-LUA 脚本开发之一]在Cocos2dX游戏中使用Lua脚本进行游戏开发(基础篇)并介绍脚本在游戏中详细用途! 分类: [Cocos2dx Lua 脚本开发 ] 2012-04-1 ...
- lua学习:游戏中的Lua
lua作为一种脚本语言,可以快速地开发游戏的原型.提高游戏的开发效率. 在游戏中,lua可以用来完成下面这些工作: ●编辑游戏的用户界面 ●定义.存储和管理基础游戏数据 ●管理实时游戏事件 ●创建和维 ...
- 在Unity3d中解析Lua脚本的方法
由于近期项目中提出了热更新的需求,因此本周末在Lua的陪伴下度过.对Lua与Unity3d的搭配使用,仅仅达到了一个初窥门径的程度,记录一二于此.水平有限,欢迎批评指正. 网络上关于Lua脚本和Uni ...
- 在redis中使用lua脚本
在实际工作过程中,可以使用lua脚本来解决一些需要保证原子性的问题,而且lua脚本可以缓存在redis服务器上,势必会增加性能. 不过lua也会有很多限制,在使用的时候要注意. 在Redis中执行Lu ...
- Redis学习笔记六:独立功能之 Lua 脚本
Redis 2.6 开始支持 Lua 脚本,通过在服务器环境嵌入 Lua 环境,Redis 客户端中可以原子地执行多个 Redis 命令. 使用 eval 命令可以直接对输入的脚本求值: 127.0. ...
- Redis进阶实践之八Lua的Cjson在Linux下安装、使用和用C#调用Lua脚本
一.引言 学习Redis也有一段时间了,感触还是颇多的,但是自己很清楚,路还很长,还要继续.上一篇文章简要的介绍了如何在Linux环境下安装Lua,并介绍了在Linux环境下如何编写L ...
- Redis进阶实践之十九 Redis如何使用lua脚本
一.引言 redis学了一段时间了,基本的东西都没问题了.从今天开始讲写一些redis和lua脚本的相关的东西,lua这个脚本是一个好东西,可以运行在任何平台上,也可以嵌入 ...
- uLua学习之读取外部Lua脚本(四)
前言 上节说到了Lua脚本与unity3d中C#脚本的数据交互,但是我感觉上节中的数理方式不太好,因为我们是把Lua脚本以字符串形式粘贴到C#脚本中的,如果读取配置数据都这样做的话,那就太可怕了.想想 ...
- 用C#调用Lua脚本
用C#调用Lua脚本 一.引言 学习Redis也有一段时间了,感触还是颇多的,但是自己很清楚,路还很长,还要继续.上一篇文章简要的介绍了如何在Linux环境下安装Lua,并介绍了在Linux环境下如何 ...
随机推荐
- linux就该这么学,第十天了
今天老师主要让要考试的提前预习课程了,提前预习, 今天讲了,防火墙,iptable.firewall-config,firewall-cmd 防火墙和网卡的配置方法,四种,1配置文件方法,主要开启 ...
- android 可以在程序代码中设置样式:style
<style name="text_style"> <item name="android:textStyle">bold</it ...
- 别人的Linux私房菜(4)安装CentOS7
linux磁盘分区参考: 添加磁盘分区(总30G). BIOS boot 2MB 系统自定义文件系统 分区格式为主要分区 /boot 1GB 文件系统为xfs 主要分区 / 10GB 文件系统为x ...
- 结合OPENSIFT源码详解SIFT算法
平台:win10 x64 +VS 2015专业版 +opencv-2.4.11 + gtk_-bundle_2.24.10_win32 参考博客:https://www.cnblogs.com/cql ...
- 解决刚刚安装完mysql 远程连接不上问题
解决远程连接mysql错误1130 远程连接Mysql服务器的数据库,错误代码是1130,ERROR 1130: Host xxx.xxx.xxx.xxx is not allowed to con ...
- Linux下 刚安装完mysql 修改密码
在Centos中安装MySQL后默认的是没有root密码的,默认的是回车, 那么为了方便需要修改密码. 没有密码为MYSQL加密码: mysql -uroot -p 回车 提示输入密码,为空回车 up ...
- JAVA 8 主要新特性 ----------------(四)Lambda函数式接口
一.什么是函数式接口 只包含一个抽象方法的接口,称为函数式接口. 你可以通过 Lambda 表达式来创建该接口的对象.(若 Lambda 表达式抛出一个受检异常,那么该异常需要在目标接口的抽象方法 ...
- 字符串相似度算法——Levenshtein Distance算法
Levenshtein Distance 算法,又叫 Edit Distance 算法,是指两个字符串之间,由一个转成另一个所需的最少编辑操作次数.许可的编辑操作包括将一个字符替换成另一个字符,插入一 ...
- 六.使用python操作mysql数据库
数据库的安装和连接 pymysql的安装 pip install PyMySQL python连接数据库 import pymysql db = pymysql.connec ...
- Python学习第2章
1.字符串: python中创建字符串我们可以使用引号''或"'. python访问子字符串,可以使用方括号来截取字符串: var="hello world!" var2 ...