在Unity3D里面使用Lua

来源:互联网 发布:hp服务器安装centos 编辑:程序博客网 时间:2024/05/16 04:13

Unity3D是个好引擎,但有一个缺憾是iOS下无法热更新。如果能用Lua写Unity3D的游戏逻辑,那就可以绕过苹果审核随意更新了。

有一种方法是用C#的基础上实现Lua。这个网上可以查到有几种版本。但这种运行效率没保障,据说比一般的Lua要慢10倍。还有一种方法是使用C版本的Lua,使用Unity3D Pro 的Plugin 功能,用PInvoke 让C# 与原生的Lua 引擎交互。

NLua

这里考虑使用原生Lua 引擎。在网上可以找到一个叫NLua 的东西。它实现了Lua 与 C# 交互的功能。它的结构分为3个层次:

  1. lua:这是一个为移动平台编译优化过的Lua。与标准Lua有什么区别我也没仔细看。
  2. KeraLua:基于前一个层次,将C API 进行简单的包装,使C# 可以方便使用 Lua C API。
  3. NLua:基于前一个层次,使用反射,动态导出C# 中的类、函数到 Lua 中。

NLua 里面还有个KopiLua组件,它是纯C#的Lua实现,可以替代KeraLua。不过这里不打算用它。

NLua放进Unity3D

NLua 主要是面向 Xamarin 开发环境的。为了能在Unity3D 中使用它,需要做些手脚。

将lua 编译成 Unity3D Plugin

Unity3D 的 Plugin 功能只能看官方说明,其它地方资料很少。但官方文档结构似乎没组织好,关键部分很容易漏掉。每个平台的Plugin 使用方式都有点差别:

  • Mac OS X:我现在只尝试了Mac OS X 下的插件。它的要点是要用Xcode 编译成 bundle,并且编译目标要选择 universal,如果只是x64,Unity3D 会提示找不到。bundle文件要放到 Assets/Plugins 目录下。
  • iOS:我只是看了下说明,还没试。要点是把源码直接放到 Assets/Plugins/iOS 目录下。
  • Android:随便看了下,貌似是要编译成jar放到 Assets/Plugins/Android 目录下。
  • (其它平台略)

将KeraLua 和 NLua 编译成dll

虽然直接将 KeraLua 和 NLua 代码塞到 Assets 里面也能正常工作, 不过编译成dll能让Assets 结构整洁一点,而且在 lua 中导入 assembly 时,不会导入过多的垃圾。

NLua 里面有一些用到System.Diagnostics.DebugSystem.Console 输出异常的部分,我统统把它们改用UnityEngin.Debug来实现相同的功能。

你可以这么用

using System;public class Test : Monobehaviour{void Awake (){using (var lua = new NLua.Lua()){lua.DoString ("luanet.load_assembly('UnityEngine')");lua.DoString ("Application = luanet.import_type('UnityEngine.Application')");lua.DoString ("Debug = luanet.import_type('UnityEngine.Debug')");lua.DoString ("Debug.Log(Application.unityVersion)");}}}

很方便啊有没有!

虽然这里的用法比较二,但是可以看出来Lua中调用C#中的功能是非常容易。 完全可以用纯 Lua 写所有逻辑了。

如果是要调用Assets 里面的东西,那么需要luanet.load_assembly('Assembly-CSharp')。 Unity3D 会把 Assets 里面的脚本都编译到Assembly-CSharp里。

继续幻想

热更新

Unity3D 工程里的 C# 部分,应该尽量的少而且与游戏内容无关。 因为在iOS平台上它是没有办法热更新的。 那么 C# 代码的内容应该包含:

  1. 游戏开始时启动Lua。
  2. 游戏崩溃时上传debug信息、修复客户端。
  3. 一个记录数据用的Monobehaviour组件 ,仅仅用来让 Prefeb 能记录一些附加信息,方便编辑。
  4. 一系列回调用的Monobehaviour组件,让Lua 可以注册一些 Update 之类的事件回调。

除了这些C# 程序之外,所有其它的游戏内容都能动态更新。 理论上一个游戏一热更新,可以变成任意的另一个游戏。

网络通信

既然都Lua 了,网络通信就不通过 C# 绕圈圈了,直接让 Lua 调用 C 的网络通信模块, 不过用什么库还没想好。

协议最好是弄个 protobuf 兼容的东西。用协议描述生成 Lua 代码。

性能优化

从PInvoke Performance这篇文章可以看出PInvoke 的效率很差。 如果在 Update 里面调用 Lua,又通过回调来使用 UnityEngine 的功能,这 PInvoke 的频率会相当高。 如果是界面部分,应该问题不大。 但是战斗部分(是个游戏就基本会战斗吧?)很可能会遇到性能瓶颈。 有一个解决方案是:每帧只有一次C#对Lua的调用,然后一次性返回这帧所有的指令。 不过这样就不能用NLua,需要自己再做一个C#导出操作指令接口给lua的一种东西。

在Update 和网络处理时,如果用面向对象的思路, 很可能会频率很高的产生大量的临时对象(Lua 中的 Table)。 这可能会让垃圾收集器过于繁忙产生顿卡。 高频率访问Table也有可能会有性能问题, 见这个文章关于uLua在Unity3D的性能测试。 优化方式是将协议数据、GameObject数据保存到C分配的连续内存中, 然后使用纯函数来批量处理数据,尽量避免大量的Lua Table 构建与操作。 Lua 操作原始内存数据的库可以参考lua-mmapfile和lua-void。

以上两个优化都运用了面向数据编程的思路, 即设计一个模块时,根据输入输出采用高效的批处理计算方式。 而面向对象编程经常会毫无意义的让代码结构看上去像业务模型,增加无用的层次,违背计算机的特性,阻碍批量处理数据。

为了更好的管理内存,可以考虑使用Rings开启多个Lua States, 比如每开一场战斗对应一个Lua State、 每个界面对应一个Lua State、 网络处理部分对应一个Lua State、本地数据缓存对应一个Lua State(也许太细了……)。 这样可以方便的知道每个部分的内存使用量,也能方便的完全释放内存。

其它参考

这里有个Q灵三国使用uLua的经验,可以看看。 uLua是个Unity插件,使用 LuaJIT + LuaInterface, 并且自己又做了某些优化,没源码。

0 0
原创粉丝点击