本篇博客是给读者介绍引擎底层如何与Lua进行结合,方便开发者直接使用脚本编程,给读者介绍的是最基本的C++与Lua的交互,引擎的封装会在下篇博客中具体讲解。
为什么选择Lua,通常开发者会使用Json,XML,txt等等。相比Lua有哪些优点呢?
a、除了Lua库,在没有使用其他库可以使用。
b、可以在文件中使用不同的公式,例如:some_variable = math.sqrt(2)* 2
c、它非常轻巧,速度快
d、它是在MIT许可下换句话说代码是开源的,因此我们可以以任何想要的方式使用它
e、它是用C语言编写的,几乎可以编译任何C编译器
f、可以使用表对数据进行分类,易于编辑和阅读
既然这么多优点,我们就采用Lua作为脚本使用,Lua与引擎的结合还是非常重要的。我们下面先从基础的讲起,慢慢给读者深入。先看看Lua语言编写的脚本:

player = {    pos = {         X = 20,         Y = 30,    },    filename = "res/images/player.png",    HP = 20,-- you can also have comments}

我们要使用Lua脚本中的内容,需要执行下面的语句:

LuaScript script("player.lua");
std::string filename = script.get("player.filename");
int posX = script.get("player.pos.X");

如何使用Lua与C++绑定,读者可以参考网址:
http://lua-users.org/wiki/BindingCodeToLua
接下来我们分析一下上面的Lua脚本,Player表是全局的,因此需要通过lua_getglobal方法获取它, 现在Player表将位于堆栈顶部,使用lua_getfield函数获取pos表,然后使用变量x,如下图所示:

对应的代码下载地址如下所示:
https://github.com/EliasD/unnamed_lua_binder
使用上面的代码时,不要忘记把Lua的库加进工程里面。我们可以使用Lua做很多事情,如下所示:

-- somefile.luasome_array = { 1, 2, 3, 4, 5, 6}
std::vector v = script.getIntVector("some_array")

如何清理Lua堆栈?我们可以使用lua_gettop函数返回数组中元素的数量,从而弄清楚我们必须弹出多少项。

void clean(){   int n = lua_gettop(L);  lua_pop(L, n);}

下面实现的接口:

std::vector LuaScript::getIntVector(const std::string& name){    std::vector<int> v;  lua_getglobal(L, name.c_str());    if(lua_isnil(L, -1)){       return std::vector();   }  lua_pushnil(L);   while(lua_next(L, -2)){       v.push_back((int)lua_tonumber(L, -1));    lua_pop(L, 1);  }  clean();   return v;}

它们是如何工作的?首先,我们获取全局表并检查是否找到它。 如果它是nil(尚未定义,或者…,nil),我们只返回一个空向量。
然后我们将nil值推到Lua堆栈的顶部, 这是因为lua_next的作用,它从堆栈中弹出键值,然后将键值对推送到堆栈。 如果数组中没有更多元素,我们清理堆栈并返回结果向量。
还可以创建一个函数来获取字符串或浮点数组, 这需要更改的是矢量类型和一些强制转换(请不要忘记将lua_tonumber更改为lua_tostring)
使用Lua脚本编程,因为它是脚本,对性能要求比较高的代码建议不要使用Lua脚本,直接使用C或者C++编程。
通过案例的方式给读者介绍,比如下面代码:

function sum(x, y)
return x + y
end

对应的C++代码如下所示:

int sum(int x, int y){   lua_State* L = luaL_newstate();   if (luaL_loadfile(L, "sum.lua") || lua_pcall(L, 0, 0, 0)){       std::cout<<"Error: failed to load sum.lua"<<std::endl;     return 0;    }    lua_getglobal(L, "sum");  lua_pushnumber(L, x); lua_pushnumber(L, y);    std::cout<<"loaded"<<std::endl;  lua_pcall(L, 2, 1, 0);   int result = (int)lua_tonumber(L, -1);    lua_pop(L, 1);   return result;}

接下来给读者分析一下,首先,我们创建新的Lua状态并加载文件。注意:这只是一个示例,我们应该将状态与加载的文件保持在某个位置,以防止每次使用函数时重新加载,因为这样做效率不高。
然后我们在Lua堆栈的顶部得到名为sum的全局函数。 使用lua_pushnumber函数然后我们推送2个变量,现在我们的堆栈看起来像这样:

第一个是lua_State,第二个是你要调用的函数中的参数个数, 第三是你希望返回的功能。 第四是错误代码(应该在Lua参考手册中阅读)
在我们调用一个函数之后,它会从它的参数中弹出堆栈。 堆栈中剩下的唯一东西是值sum函数返回,所以现在我们可以用lua_tonumber获取它的值并弹出它。
说了这么多,现在给读者介绍如何使用它们?
假设我们在游戏中实现NPC, 当玩家靠近NPC时,NPC会做不同的事情。
我们经常会安排玩家与NPC的一些对话,比如说“让我帮助你”,而另一个NPC只是说“你好”并且什么都不做。我们的交互代码可能如下所示:

if(isPressed(ACTIVATION_BUTTON))
{   Character* character = find_nearby_character(player);   if(character) {      character->interact(player);   }}

如何实现这个交互方法?我们很容易想到使用枚举,如下所示:

enum CharacterType { Player, Talker, Healer };
CharacterType type;

函数如下所示:

void Character::interact(Character* secondCharacter){   switch(type) {        case Character::Player:            break;        case Character::Talker:          say("Hello");          break;       case Character::Healer:        say("Let me help you");      heal(secondCharacter);       break;  }}

通过代码我们可以看出,这么设计非常不利于扩展,我们还可以想到使用另一种解决方案是使交互虚拟功能和使用继承, 但是我们会去实现每种类型的NPC,这种方案也是不可取的。
或者有读者可以使用更好的策略模式,用C ++编码的,必须重新编译代码,也是不可取的。
最终的解决方案就想到了Lua的编写,在使用Lua之前,我们首先要创建一个Character类,如下所示:

class Character
{
public:   Character(const char* name, int hp);
void say(const char* text);  void heal(Character* character);    const char* getName() { return name; }    int getHealth() { return health; }   void setHealth(int hp) { health = hp; }    // will be implemented later       void interact(Character* character);private:  const char* name;   int health;}; Character::Character(const char* name, int hp) {    this->name = name;    health = hp;}void Character::say(const char* text) {    std::cout << name << ":" << text << std::endl;} void Character::heal(Character* character) {    character->setHealth(100);}

我们遇到了一个问题, 如果Lua现在没有关于这种类型,我们如何将Character *作为参数传递? 我们如何在Lua中注册非静态成员函数并调用它们?Lua包装器可以参考网址:http://lua-users.org/wiki/BindingCodeToLua
决定使用LuaWrapper,它没有额外的依赖关系而且不需要构建, 只需将一个头文件复制到项目中即可开始使用。
使用LuaWrapper,函数的编写如下所示:

int Character_getName(lua_State* L){   Character* character = luaW_check<Character>(L, 1);        lua_pushstring(L, character->getName());  return 1;} int Character_getHealth(lua_State* L){  Character* character = luaW_check<Character>(L, 1);      lua_pushnumber(L, character->getHealth());    return 1;} int Character_setHealth(lua_State* L){   Character* character = luaW_check<Character>(L, 1);     int hp = luaL_checknumber(L, 2);    character->setHealth(hp);  return 0;}

从现在开始,我们将使用checknumber而不是tonumber。 它基本相同,但如果出现问题,它会抛出错误信息。
LuaWrapper提供了相同的方法,可以用它来获取C ++对象,还可以创建对象并调用它们的方法,如下所示:

player = Character.new(“Hero”, 100)player:getHealth()

使用luaW_check(L,1)可以获得玩家对象在C ++中使用它,余下的代码如下所示:

static luaL_Reg Character_table[] = {    { NULL, NULL }};
static luaL_Reg Character_metatable[] = {   { "getName", Character_getName },  { "getHealth", Character_getHealth },    { "setHealth", Character_setHealth }, { NULL, NULL }};static int luaopen_Character(lua_State* L) {   luaW_register<Character>(L, "Character", Character_table, Character_metatable, Character_new); return 1;}

Character_table用于静态函数, 我们在Character类中没有它们,所以这个结构是空的。
Character_metatable用于设置将在Lua中使用的函数名称。
luaopen_Character注册一个类, 第一个参数是lua_State *,第二个参数是如何在Lua脚本中命名我们的类。 其他参数是静态表,元表和构造函数。
我们的测试脚本如下所示:

player = Character.new("Hero", 100)
player:setHealth(80)
hp = player:getHealth()
name = player:getName()
print("Character name: "..name..". HP = "..hp)

最后代码下载地址如下所示:
链接:https://pan.baidu.com/s/1Rn3WwXYVLA-t79s0MFFarQ
提取码:mtgr

参考网址:https://eliasdaler.wordpress.com/2013/10/11/lua_cpp_binder/

手把手教你架构3D引擎高级篇系列八相关推荐

  1. 手把手教你架构3D引擎高级篇概述

    前几年写过一本书<手把手教你架构3D游戏引擎>电子工业出版社,主要内容讲的是固定流水线编程,目的是让读者理解第一代引擎是如何实现的,从本篇博客开始,给读者介绍关于使用可编程流水线自己搭建3 ...

  2. 手把手教你架构3d游戏引擎pdf_一个在游戏行业摸爬滚打了十几年的人,为何我对这本书情有独钟...

    Big News!<游戏开发:世嘉新人培训教材>今日开始预售啦!经过漫长的等待,这次终于可以买到了.现在下单,你将在图书出印厂的第一时间收到书哦- 这本书由世嘉一线开发者执笔,并被选为世嘉 ...

  3. 手把手教你架构3d游戏引擎pdf_白鹭引擎团队即将发布 Egret Pro,并公布后续路线图...

    各位开发者好. 春节前,白鹭引擎团队发布了 Egret3D 1.4,引入了大量新特性.上周,白鹭引擎团队发布了 5.2.14 版本,修复了多个白鹭引擎2D渲染器相关的 BUG,接下来我们会在下周继续发 ...

  4. 手把手教你架构3d游戏引擎pdf_游戏开发中的算法

    游戏技术这条路,可深可浅.你可以满足于完成GamePlay玩法层面的东西,你也可以满足于架构和框架设计层面的东西,你也可以醉心于了解某一游戏引擎带来的掌控感.但是,我们不该止步于此,止步与目前所见或所 ...

  5. 一文搞定!手把手教你文字识别(识别篇:LSTM+CTC, CRNN, chineseocr方法)

    个人博客导航页(点击右侧链接即可打开个人博客):大牛带你入门技术栈 文字识别是AI的一个重要应用场景,文字识别过程一般由图像输入.预处理.文本检测.文本识别.结果输出等环节组成.   其中,文本检测. ...

  6. windows脚本编制引擎_手把手教你写脚本引擎(一)

    手把手教你写脚本引擎(一)--挑选语言的特性 陈梓瀚 华南理工大学软件本科05级 脚本引擎的作用在于增强程序的可配置性.从游戏到管理系统都需要脚本,甚至连工业级产品的Office.3DS Max以及A ...

  7. 手把手教你编写游戏模拟器 - Chip8篇(1)

    转自 http://www.cnblogs.com/YiranXie/p/3439934.html 手把手教你编写游戏模拟器 - Chip8篇(1) 手把手教你编写游戏模拟器 - Chip8篇 翻译整 ...

  8. 谷粒商城--认证中心--高级篇笔记八

    谷粒商城–认证中心–高级篇笔记八 1. 环境搭建 1.1 新建模块gulimall-auth-server 1.2 pom文件 上面没选好直接复制下面的pom文件,记得排除gulimall-commo ...

  9. 微信接口开发之高级篇系列【网页授权获取用户基本信息】

    PHP微信接口开发之高级篇之网页授权获取用户基本信息 二.WEB开发工具 转载于:https://www.cnblogs.com/tinywan/p/5860981.html

  10. 手把手教你爬虫requests实战演练——python篇

    文章目录 一.前言 二.实战 1)获取百度网页并打印 2)获取帅哥图片并下载到本地 4) 获取美女视频并下载到本地 5)搜狗关键词搜索爬取 6)爬取百度翻译 7)爬取豆瓣电影榜单 8)JK妹子爬取 总 ...

最新文章

  1. 河套酒业集团远程应用K/3系统案例解析
  2. 内存文件系统——sysfs
  3. Pixysoft.Framework.MemoryCache 开发实录
  4. python解析二维码_Python二维码生成识别实例详解
  5. 用《内网穿山甲》共享内网中的远程桌面服务
  6. The Furthest Distance In The World
  7. 【概率论】复习资料(手写复习)
  8. coreldraw错误代码14001_应用程序配置不正确,应用程序未能启动 提示14001错误代码解决方法...
  9. 创建Vue实例对象基础语法模板
  10. Lookahead、LazyOptimizer、MaskedAdamOptimizer、AdaBound
  11. bi工具有哪些,该怎么选择呢?
  12. 计算机专业简历教育背景怎么写,简历中教育背景怎么写?填写教育背景注意事项...
  13. 什么是七日年化收益率和万分收益?
  14. 读南师《金刚经说什么》有感
  15. 差分走线_HFSS学习笔记(2)
  16. 不讲一点数学知识,步步图解条理清晰,手把手带你理解DBSCAN算法
  17. 网络3共享网络2计算机打印机,如何添加本地打印机与共享网络上的打印机
  18. android加密墙,Android代码混淆加密配置(Proguard文件解析)
  19. 卷积神经网络分类实战:疫情期间戴口罩识别
  20. 西班牙中国鞋事件的思考【ZZ】

热门文章

  1. PCB制图 | Altium Designer 20下载与安装
  2. 2021-eclipse的安装及环境配置
  3. java xcap,xcap发包工具的简单使用2(发送报文)
  4. C#编程如何安装Nupkg包
  5. MATLAB/simulink_S函数
  6. Android开源项目及库汇总
  7. Win11缺少dll文件怎么办?Win11系统找不到dll文件修复方法
  8. python代码之飞机大战完整版(附带image素材资源)
  9. 【DOS BAT】Windows下微信双开、微信多开
  10. mySQL及可视化界面navicat在window的配置