lua和unity如何交互_【Lua与C#交互④】如何让Lua打印到Unity控制台
今天要讲的如何让Lua打印到Unity控制台?
相信用过tolua或者xlua的人都知道,在lua脚本里面只要写一行print就能打印到unity控制台效果类似Debug.Log。 如下:
print("This is a script from a utf8 file")
print("tolua: 你好! こんにちは! 안녕하세요!")
那么它们背后的原理是什么呢?如果自己实现一个类似的函数替换功能又该如何实现?
首先来看核心代码如下:
_L = LuaDLL.luaL_newstate();
LuaDLL.luaL_openlibs( _L );
LuaDLL.lua_pushcfunction( _L, Print );
LuaDLL.lua_setglobal( _L, "print" );
1. luaL_newstate
新建一个lua状态机,没什么好说的
2. luaL_openlibs
打开lua标准库,把库里的函数放到全局变量里
什么是lua标准库?
lua标准库指的是一些lua源码自带的库函数,包括debug、package、string、math等。相关代码在lua源码里的linit.c文件。如果没有这一步,后面调用setglobal时就会失败,因为在全局里面没有print这个函数。
3. lua_pushcfunction
将c#里的方法压入栈顶。这个方法在lua源码里是一个宏。
#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0)
所以如果直接build lua源码的dll,是调用不了这个方法的。在c#端需要做以下处理,或者自行修改lua源码。
[DllImport( LUADLL, CallingConvention = CallingConvention.Cdecl )]
public static extern void lua_pushcclosure( IntPtr L, LuaCSFunction f, int n );
public static void lua_pushcfunction( IntPtr L, LuaCSFunction f )
{
lua_pushcclosure( L, f, 0 );
}
LuaCSFuncton是一个参数为Intptr,返回值为int的委托。
我们需要把c#端另外实现一个Print方法,并且把lua端的print替换掉。
[MonoPInvokeCallbackAttribute( typeof( LuaCSFunction ) )]
private static int Print( IntPtr L )
{
try
{
int n = LuaDLL.lua_gettop( L );
var sb = new StringBuilder();
//获得当前运行的函数的上一个调用层的信息,返回行数,把调用层的名称入栈 int line = LuaDLL.jlua_where( L, 1 );
string filename = LuaDLL.lua_tostring( L, -1 );
LuaDLL.lua_settop( L, n );
int offset = filename[0] == '@' ? 1 : 0;
sb.Append( '[' ).Append( filename, offset, filename.Length - offset ).Append( ':' ).Append( line ).Append( "]:" );
for( int i = 1; i <= n; i++ )
{
if( i > 1 ) sb.Append( " " );
if( LuaDLL.lua_isstring( L, i ) == 1 )
{
sb.Append( LuaDLL.lua_tostring( L, i ) );
}
else if( LuaDLL.lua_isnil( L, i ) )
{
sb.Append( "nil" );
}
else if( LuaDLL.lua_isboolean( L, i ) )
{
sb.Append( LuaDLL.jlua_toboolean( L, i ) ? "true" : "false" );
}
else
{
IntPtr p = LuaDLL.lua_topointer( L, i );
if( p == IntPtr.Zero )
{
sb.Append( "nil" );
}
else
{
sb.Append( LuaDLL.luaL_typename( L, i ) ).Append( ":0x" ).Append( p.ToString( "X" ) );
}
}
}
Debug.Log( sb.ToString() );
return 0;
}
catch(Exception e )
{
throw e;
}
}
Print 函数需要打印出lua的调用栈信息。如一开始展示的那样,它能够打印出是哪个lua脚本的哪一行调用的print。
这里我的实现方式基本照抄tolua。唯一的区别是我使用的stringbuilder进行字符串拼接,tolua使用的CString,效果是一样的。
(1) 首先调用lua_gettop知道当前lua栈里有多少层
(2) 接着调用了一个lua_where,这是tolua在c端自己写的方法,我也模仿(照抄)了一个,源码如下:
LUA_API int jlua_where(lua_State *L, int level) {
lua_Debug ar;
if (lua_getstack(L, level, &ar)) {
lua_getinfo(L, "Sl", &ar);
if (ar.currentline > 0) {
lua_pushstring(L, ar.source);
return ar.currentline;
}
}
lua_pushliteral(L, "");
return -1;
}
这里有涉及到几个lua的API:int lua_getstack (lua_State L, int level, lua_Debugar):获取栈信息,level表示第几层
int lua_getinfo (lua_State L, const charwhat, lua_Debug *ar):根据what字符串的内容给ar填充信息'n': 填充 name 及 namewhat 域;
'S': 填充 source , short_src , linedefined , lastlinedefined ,以及 what 域;
'l': 填充 currentline 域;
't': 填充 istailcall 域;
'u': 填充 nups, nparams,及 isvararg 域; -
'f': 把正在运行中指定层次处函数压栈;
'L': 将一张表压栈,这张表中的整数索引用于描述函数中哪些行是有效行。 (有效行指有实际代码的行,即你可以置入断点的行。 无效行包括空行和只有注释的行。)
const char lua_pushliteral (lua_StateL, const char *s): 等同于pushstring
在Print里面还有一个方法lua_tostring也值得一讲。lua_tostring在lua源码里也是一个宏。不能被打到dll里面。
#define lua_tostring(L,i) lua_tolstring(L, (i), NULL)LUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len){...}
在c#端需要这么处理:
[DllImport( LUADLL, CallingConvention = CallingConvention.Cdecl )]
public static extern IntPtr lua_tolstring( IntPtr luaState, int index, out int len );
public static string lua_tostring( IntPtr luaState, int index )
{
int len = 0;
IntPtr str = lua_tolstring( luaState, index, out len );
if( str != IntPtr.Zero )
{
string result = Marshal.PtrToStringAnsi( str, len );
if( result == null )
{
byte[] buffer = new byte[len];
Marshal.Copy( str, buffer, 0, len );
return Encoding.UTF8.GetString( buffer );
}
return result;
}
return null;
}
lua_tolstring之所以需要out是因为lua源码里对应的方法会给len赋值。还有在lua_tostring将指针Intptr转成了字符串是因为c里面的char类型不能直接转成c#里的string类型。
(3)调用lua_settop把调用lua_where的时候往栈里放的东西扔掉。lua_settop是一个非常常用的API,作用是把第n层往上的栈里存的东西都舍弃。
(4)遍历,判断一个栈里的从1到n层存的都是什么数据类型
(5)异常处理,我这里因为偷懒,直接抛出异常。在tolua里面有一个luaExection类专门用来处理lua端的异常的。
4. lua_setglobal
最后一步调用lua_setglobal替换全局函数print就大功告成了。
总结,这次依旧讲的的lua的api调用以及lua和c#端进行交互的问题。并且从这次开始不再使用tolua等别人打包好的dll了。而是我自己拿lua源码打包。这些知识属于比较底层的东西,可能很多人觉得没有什么用,我之前也是这么觉得的。别人都已经造好轮子了,我还去研究轮子怎么造有用吗?最近也是因为工作需要不得不研究lua才发现,自己以前懂的连皮毛都算不上,都是停留在tolua,xlua怎么用的程度,一旦脱离了这些工具,自己什么也不会了。
单纯的使用工具并不能提高自己的技术。因为工具总是会变的,只有掌握好底层,才能以不变应万变。
关于作者:水曜日鸡,简称水鸡,ACG宅。曾参与索尼中国之星项目研发,具有2D联网多人动作游戏开发经验。
游戏行业交流学习群:891809847
lua和unity如何交互_【Lua与C#交互④】如何让Lua打印到Unity控制台相关推荐
- python硬件交互_对Python的交互模式和直接运行.py文件的区别详解
对Python的交互模式和直接运行.py文件的区别详解 看到类似C:\>是在Windows提供的命令行模式,看到>>>是在Python交互式环境下. 在命令行模式下,可以执行p ...
- java前后端数据交互_前后端数据交互(示例代码)
function ajax(option) { function objtostring(obj){ if(typeof obj==='object' && !Array.isArra ...
- lua运行外部程序_在C语言程序中嵌入Lua脚本
第一次知道Lua语言,是上个月在书城的时候,看到一本名为<魔兽世界编程宝典>的书.心想,魔兽世界还能编程?难道是自己编一个魔兽世界出来?翻开一看,原来是编写插件,用的是一种叫Lua的脚本语 ...
- lua游戏脚本实例源码_在nginx中使用强大的lua实现定向需求
前言 Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能.其设计目的是为了嵌入应用程序中,从而为应用程序提供 ...
- ArcGIS Engine环境下VS窗体与Unity虚拟现实地理空间的嵌套和交互
本文目录 一.前言 1.结果图片 2.相关系统与平台介绍 3.实践思路与基本流程 4.涉及环境与软件 二.前期操作介绍 1.Unity和Winform窗体的连接交互 1.1.Unity和Winform ...
- 《Lua游戏开发实践指南》一3.2游戏项目中的Lua
3.2游戏项目中的Lua 把脚本语言集成到游戏项目中可以提升团队的开发效率,并且可以很好地扩展原生编译语言的能力.Lua在游戏开发的许多基础领域中都表现得很出色. 在游戏开发团队中,可能有许多成员都使 ...
- lua文件迁移小工具(编辑器下直接使用,将.lua文件变成定制的AB包文件)
作用:将工程的某个文件夹下的所有.lua文件转换成txt文件,并将这些txt文件放到自定义文件夹下,并且统一修改为自定义的ab包名. 因为:AB包不支持.lua文件,所以需要转成txt文件. usin ...
- Lua移植到arm上 并实现在arm上 可以让lua脚本调c语言,C语言调用lua脚本
Lua移植到arm上 并实现在arm上 可以让lua脚本调c语言,C语言调用lua脚本 首先参考http://wiki.chumby.com/index.php?title=Lua&print ...
- c语言编程流水灯与交通灯实验,C51单片机实验报告_流水灯_交通灯_定时器_双机交互_时钟.doc...
C51单片机实验报告_流水灯_交通灯_定时器_双机交互_时钟 学 号: 班 级: 自动化10班 姓 名: 张 指导老师: 胡 2012.12 单片机核心板实验要求 流水灯实验 实验目的: 简单I/O引 ...
最新文章
- Git clone密码输入错误如何修改
- 手机可以阅读html吗,手机文档html能删除吗
- 计算机二级c语言考生文件夹在哪,2017年全国计算机二级C语言考试题
- pc端js获取当前经纬度_Swiper 免费开源、功能强大的触摸滑动 js 特效插件
- android 线性布局蒙层,Android开发 - 掌握ConstraintLayout(一)传统布局的问题
- LeetCode 1071. 字符串的最大公因子(字符串的最大公约数)
- ios文件连接服务器无法写入,iOS写入数据到文件中
- python数据库连接池neo4j_在python中操作neo4j数据库的方法
- 前端解析token中的数据_[前端基础]数据类型判定原理解析
- spring基于注解的 IOC 配置ioc实现crud
- ASP.NET MVC 学习第三天
- 最简单的Gif生成工具,ScreenToGif操作指南(一)
- 电源管理总线 (PMBus)
- 树莓派做微信公众号服务器,树莓派与微信公众号对接(python)
- typora的安装和使用
- 利用SPSS随机数轻松实现随机分组
- 图层蒙版和快速蒙版、路径
- matlab accuracy 存,matlab 绘制caffe accuracy与loss曲线
- 5G新通话新在哪儿?全新技术带来通话变革
- 用python生成个性二维码生成器_Python 生成个性二维码