ToLua原理和使用教程

ToLua原理和使用教程

Tolua是用来实现C++程序中的类、变量、函数等绑定到lua程序中。绑定后lua代码可以直接调用C++程序中的类、变量、函数等等。

 

Tolua原理

Tolua做了哪些事

要想知道tolua原理,先了解tolua到底做了些什么事。先看一下下面“lua实现get/set方法”的例子。这个例子包含了三个文件luaBind.cpp文件、Main.cpp文件、a.lua文件。

luaBind.cpp文件

extern "C" {
#include "lua\lua.h"
#include "lua\lualib.h"
#include "lua\lauxlib.h"
}

const char *HandlesGetSet = "HandlesGetSet";

extern double a /*= 166*/;
int a_get(lua_State*L)
{
	lua_pushnumber(L, a);
	return 1;
}

int a_set(lua_State *L)
{
	if (lua_isnumber(L, 1))
	{
		a = lua_tonumber(L, 1);
	}

	return 0;
}

int index_event(lua_State*L)
{
	//从 registry  中获取变量的get set 方法
	luaL_getmetatable(L, HandlesGetSet);
	lua_pushvalue(L, 2);
	lua_rawget(L, -2);

	if (lua_isnil(L, -1))// 没有get set方法
	{
		lua_pop(L, 2);
		lua_rawget(L, -2);
	}
	else
	{
		lua_pushstring(L, "get");
		lua_rawget(L, -2);
		lua_pcall(L, 0, 1, 0);//调用get方法
	}
	return 1;
}

int newindex_event(lua_State *L)
{
	//从 registry  中获取变量的get set 方法
	luaL_getmetatable(L, HandlesGetSet);
	lua_pushvalue(L, 2);
	lua_rawget(L, -2);

	if (lua_isnil(L, -1))// 没有get set方法
	{
		lua_pop(L, 2);
		lua_rawset(L, -3);
	}
	else
	{
		lua_pushstring(L, "set");//调用set方法
		lua_rawget(L, -2);
		lua_pushvalue(L, 3);
		lua_pcall(L, 1, 0, 0);
	}
	return 0;
}

void BindLua(lua_State *L)
{

	//获取 _G表
#if defined (LUA_VERSION_NUM) && LUA_VERSION_NUM >= 502 /* after lua 5.2 */
	lua_pushvalue(L, LUA_REGISTRYINDEX); /* registry */
	lua_pushnumber(L, LUA_RIDX_GLOBALS); /* registry globalsindex */
	lua_rawget(L, -2);                  /* registry registry[globalsindex] */
	lua_remove(L, -2);                  /* registry */
#else
	lua_pushvalue(L, LUA_GLOBALSINDEX);
#endif

	//创建 _G表的元表
	lua_newtable(L);
	lua_pushstring(L, "__index");
	lua_pushcfunction(L, index_event);
	lua_rawset(L, -3);
	lua_pushstring(L, "__newindex");
	lua_pushcfunction(L, newindex_event);
	lua_rawset(L, -3);

	lua_setmetatable(L, -2);//设置 _G表的元表
	lua_pop(L, 1);

	//创建一张表存储在 registry  中
	luaL_newmetatable(L, HandlesGetSet);
	//注册 a 变量的get set方法
	lua_pushstring(L, "a");
	lua_newtable(L);
	lua_pushstring(L, "get");
	lua_pushcfunction(L, a_get);
	lua_rawset(L, -3);
	lua_pushstring(L, "set");
	lua_pushcfunction(L, a_set);
	lua_rawset(L, -3);
	lua_rawset(L, -3);
	lua_pop(L, 1);
}


Main.cpp文件

#include<iostream>
extern "C" {
#include "lua\lua.h"
#include "lua\lualib.h"
#include "lua\lauxlib.h"
}

using namespace std;
double a = 166;

void BindLua(lua_State *L);
int main()
{
	lua_State *L = luaL_newstate();
	luaL_openlibs(L);
	BindLua(L);
	a = 99;
	if (0 != luaL_dofile(L, "a.lua"))
	{
		printf("%s\n", lua_tostring(L, -1));
	}
	printf("a = %f\n\n\n", a);
	a = a+1000;
	if (0 != luaL_dofile(L, "a.lua"))
	{
		printf("%s\n", lua_tostring(L, -1));
	}
	printf("a = %f\n", a);
	lua_close(L);

	return 0;
}

 

a.lua文件

print(a)
a = a+1
print("c=", c)
b = "adas"
print("b=", b)

 

这个个例子实现了将C++程序中的double a变量绑定到了lua程序中。在C++程序中改变了变量a的值,lua程序中可以获取改变后的值,同样lua程序中对变量a赋值后,C++程序也可以获取赋值后的值,具体原理参见:Lua任意类型的get/set方法, 

三个文件的作用如下。

luaBind.cpp文件:  实现将变量double a 绑定到lua程序中

Main.cpp文件: C程序代码

a.lua文件:lua程序代码

 

可以看到为了绑定double a变量,luaBind.cpp中的代码并不少。为了简化这种绑定,我们可以使用tolua来实现绑定。Tolua可以帮我们生成luaBind.cpp文件,我们只需要将生成后的文件加入到程序中即可完成绑定。

如何生成lua绑定文件

要生成绑定文件,首先我们需要一个tolua++.exe的程序,然后还需要一个.pkg文件。.pkg文件是一个代码文件,代码格式与C++类似。我们首先需要编写一个pkg文件,有了这个文件之后,然后使用tolua++程序对pkg程序进行转换,转换后就就会生成相应的绑定文件。具体操作,请看后续文章。

 

Tolua使用

Tulua官网http://webserver2.tecgraf.puc-rio.br/~celes/tolua/,更新到tolua-5.2.4,支持lua5.2.4。官网只提供了tolua的源代码,没有提供任何程序。

下载代码解压后,在src目录下有三个目录,如下:

Bin目录中包含了生成tolua++.exe转换程序的代码,我以使用VS2015编译,文章最后有下载地址。

Lib目录包含了程序使用tolua时需要包含的库的代码,因为代码比较少,我一般直接将lib文件夹中的代码直接包含到程序中。

Tests目录包含了tolua的一些例子,这些例子我以全部使用VS2015进行了编译,文章最后有下载地址。

 

VS2015编译tolua++.exe程序

在Bin目录下有两个.c文件,tolua.c、toluabind.c,将这两个文件加入到项目中,另外项目中还须要lua5.2.4代码和Lib目录下的代码。如下:

 

编写pkg文件

pkg文件语法,这里不讲解,具体情况tolua官网:http://webserver2.tecgraf.puc-rio.br/~celes/tolua/tolua-3.2.html。语法规则不多,与C++非常接近,很容易看懂。

 

这里编写一个绑定double a变量的方法。

 

Bindlua.pkg文件

$extern double a;

double a;

 

生成绑定文件

Tolua++.exe支持的命令参数。

 

执行以下命令生成绑定文件。

 

使用绑定文件

将生成的绑定文件Bindlua.cpp添加到项目中,另外还需要添加tolua源代码src/lib目录下的文件到项目中。如下:

 

更多有关tolua的例子请参见官方的例子,官方提供我已经转换成VS2015项目,文章结尾由相应的下载地址。

补充:

文中的代码,以及提到到tolua学习资源下载地址:https://pan.baidu.com/s/1boWtfw7

 

 

 

### TOlua框架的热更新实现原理 TOlua框架的热更新实现主要依赖于Lua与C#之间的交互机制。通过TOluaLua脚本可以调用C#中的方法对象,同时C#也可以调用Lua中的函数。这种双向调用机制是实现热更新的核心基础[^1]。 #### 1. Lua与C#对象的交互 在TOlua中,C#对象并不会直接暴露给Lua。相反,这些对象会被注册到一个名为`ObjectTranslator`的组件中,并返回一个索引值(类似于Windows编程中的句柄)。这个索引值会被包装成一个`userdata`传递给Lua,并且设置相应的元表(metatable),从而允许Lua通过元表操作C#对象[^2]。 #### 2. 初始化过程 在TOlua的初始化阶段,会加载一个名为`tollua.lua`的文件。该文件通过`require`引入了对Unity性能优化后的类型定义,例如`Vector3`、`Vector2`、`Bound`等。这些类型的Lua方法会被缓存到C#中,以便后续快速调用[^3]。 #### 3. 热更新的具体实现 热更新的核心思想是将游戏逻辑从C#代码中分离出来,转移到Lua脚本中。这样可以在不重新编译C#代码的情况下,仅通过替换Lua脚本实现功能的修改或新增。具体实现步骤如下: - **脚本加载**:通过LuaState加载Lua脚本文件。这通常通过`DoFile`方法完成。 - **动态绑定**:使用TOlua提供的API将Lua函数绑定到C#中。例如,可以通过`LuaFunction`类来调用Lua中的函数。 - **运行时替换**:当需要更新逻辑时,只需重新加载Lua脚本文件,而无需重启游戏或重新编译C#代码。 #### 4. 性能优化 为了提高性能,TOlua会对一些常用的Unity类型进行重写优化。例如,对于`Vector3`的操作,TOlua会在Lua层面上提供更高效的实现方式,避免频繁的跨语言调用带来的性能开销[^3]。 ```csharp // 示例:通过TOlua调用Lua函数 using XLua; public class Example : MonoBehaviour { private LuaEnv luaEnv = new LuaEnv(); void Start() { // 加载Lua脚本 luaEnv.DoString("function Test() print('Hello from Lua!') end"); // 调用Lua函数 var func = luaEnv.Global.GetInPath<LuaFunction>("Test"); if (func != null) { func.Call(); func.Dispose(); } } void OnDestroy() { luaEnv.Dispose(); } } ``` #### 5. 注意事项 尽管TOlua提供了强大的热更新能力,但在实际项目中需要注意以下几点: - **兼容性问题**:确保Lua脚本与C#代码之间的接口保持一致,避免因版本差异导致错误。 - **性能问题**:虽然TOlua对部分类型进行了优化,但频繁的跨语言调用仍可能导致性能瓶颈。 - **调试困难**:相较于纯C#开发,Lua脚本的调试相对复杂,建议使用专门的调试工具。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值