今天要讲的如何让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替换掉。

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控制台相关推荐

  1. python硬件交互_对Python的交互模式和直接运行.py文件的区别详解

    对Python的交互模式和直接运行.py文件的区别详解 看到类似C:\>是在Windows提供的命令行模式,看到>>>是在Python交互式环境下. 在命令行模式下,可以执行p ...

  2. java前后端数据交互_前后端数据交互(示例代码)

    function ajax(option) { function objtostring(obj){ if(typeof obj==='object' && !Array.isArra ...

  3. lua运行外部程序_在C语言程序中嵌入Lua脚本

    第一次知道Lua语言,是上个月在书城的时候,看到一本名为<魔兽世界编程宝典>的书.心想,魔兽世界还能编程?难道是自己编一个魔兽世界出来?翻开一看,原来是编写插件,用的是一种叫Lua的脚本语 ...

  4. lua游戏脚本实例源码_在nginx中使用强大的lua实现定向需求

    前言 Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能.其设计目的是为了嵌入应用程序中,从而为应用程序提供 ...

  5. ArcGIS Engine环境下VS窗体与Unity虚拟现实地理空间的嵌套和交互

    本文目录 一.前言 1.结果图片 2.相关系统与平台介绍 3.实践思路与基本流程 4.涉及环境与软件 二.前期操作介绍 1.Unity和Winform窗体的连接交互 1.1.Unity和Winform ...

  6. 《Lua游戏开发实践指南》一3.2游戏项目中的Lua

    3.2游戏项目中的Lua 把脚本语言集成到游戏项目中可以提升团队的开发效率,并且可以很好地扩展原生编译语言的能力.Lua在游戏开发的许多基础领域中都表现得很出色. 在游戏开发团队中,可能有许多成员都使 ...

  7. lua文件迁移小工具(编辑器下直接使用,将.lua文件变成定制的AB包文件)

    作用:将工程的某个文件夹下的所有.lua文件转换成txt文件,并将这些txt文件放到自定义文件夹下,并且统一修改为自定义的ab包名. 因为:AB包不支持.lua文件,所以需要转成txt文件. usin ...

  8. Lua移植到arm上 并实现在arm上 可以让lua脚本调c语言,C语言调用lua脚本

    Lua移植到arm上 并实现在arm上 可以让lua脚本调c语言,C语言调用lua脚本 首先参考http://wiki.chumby.com/index.php?title=Lua&print ...

  9. c语言编程流水灯与交通灯实验,C51单片机实验报告_流水灯_交通灯_定时器_双机交互_时钟.doc...

    C51单片机实验报告_流水灯_交通灯_定时器_双机交互_时钟 学 号: 班 级: 自动化10班 姓 名: 张 指导老师: 胡 2012.12 单片机核心板实验要求 流水灯实验 实验目的: 简单I/O引 ...

最新文章

  1. Git clone密码输入错误如何修改
  2. 手机可以阅读html吗,手机文档html能删除吗
  3. 计算机二级c语言考生文件夹在哪,2017年全国计算机二级C语言考试题
  4. pc端js获取当前经纬度_Swiper 免费开源、功能强大的触摸滑动 js 特效插件
  5. android 线性布局蒙层,Android开发 - 掌握ConstraintLayout(一)传统布局的问题
  6. LeetCode 1071. 字符串的最大公因子(字符串的最大公约数)
  7. ios文件连接服务器无法写入,iOS写入数据到文件中
  8. python数据库连接池neo4j_在python中操作neo4j数据库的方法
  9. 前端解析token中的数据_[前端基础]数据类型判定原理解析
  10. spring基于注解的 IOC 配置ioc实现crud
  11. ASP.NET MVC 学习第三天
  12. 最简单的Gif生成工具,ScreenToGif操作指南(一)
  13. 电源管理总线 (PMBus)
  14. 树莓派做微信公众号服务器,树莓派与微信公众号对接(python)
  15. typora的安装和使用
  16. 利用SPSS随机数轻松实现随机分组
  17. 图层蒙版和快速蒙版、路径
  18. matlab accuracy 存,matlab 绘制caffe accuracy与loss曲线
  19. 5G新通话新在哪儿?全新技术带来通话变革
  20. 用python生成个性二维码生成器_Python 生成个性二维码

热门文章

  1. 第七讲 入侵检测技术
  2. Lucene基本语法
  3. fabs和abs的区别
  4. linux 服务器带宽测试工具
  5. 图解Git:一个软件配置管理工具(简介)
  6. 点石互动--石头之:SEO是狗屎还是科学?
  7. Eclipse开发工具介绍
  8. 计算机网络西安邮电大学,计算机网络实验报告西安邮电大学.doc
  9. 自己家过得一地鸡毛,满身负债,老公却还想着创业,怎么办?
  10. electron重启后更新_Electron~增量更新和全量更新