0x00
请配合我的代码食用Github
一知半解系列最后一篇,撒花。
ps:零散的知识点 - Asset 使用。
0x01 Coroutines
学习过 Lua 的语法的小伙伴们都知道,在 Lua 中原生提供了协程,开箱即用。除非你在“sandbox”模式下故意移除 coroutines 模块……
Usages
CLR 代码创建协程,可以通过 script 对象的 CreateCoroutine 方法创建,参数只需要为 function 的 DynValue。
1 | string code = @" |
平时的我会说,我看的懂这个程序,我给大家理理,但是现在的我矜持,你肯定看得懂这是一个怎样的流程:从 string 脚本中载入了一个协程函数,之后,在 C# 中创建这个协程,并在循环中打印返回的数字……所以我就不说了~
也可以写像迭代器一样调用协程:
1 | string code = @" |
这里在得到 lua 中带有 yield 返回的方法后,以迭代器的方式调用。
这里最后一行的断言代码,是使用了 Microsoft 的 MSTEst.TestFramework 框架,来检测最终结果,如果不符将会抛出异常,使用的方法:
1 | Install-Package MSTest.TestFramework |
安装包,即可使用(好敷衍……),文档
当然也可以使用 Debug.Asset…参数是条件和失败消息。
Caveats
原生 Lua 中,协程不能产生嵌套调用。
如果在 Lua 中调用了一个 C# 函数,而在这个 C# 函数中又调用脚本,这时就不能使用 yield 恢复到这个 C# 调用外部的协程(等会……Emmm)。
有一个解决方法是返回 TailCallRequest DynValue
1 | return DynValue.NewTailCallReq(luafunction, arg1, arg2...); |
这只在你需要自己实现 load,pcall 或者 coroutine.resume 等 API 时需要注意。
在 MoonSharp 中,即使没有调用 coroutine.yeild 也可以暂停协程(如果要限制脚本的 CPU 时间,又不破坏脚本执行),栗子环节:
1 | public static void PreemptiveCoroutines() |
这里使用没有 yield 指令的函数创建了一个协程,通过参数 AutoYieldCounter 设置一个大于 0 的数(官推 1000 开调),指的是执行的指令数量。这里使用一个 for 循环判断返回值是否为我们需要的,调用 coroutine.Coroutine.Resume(…) 或 coroutine.Coroutine.Resume() 方法继续执行协程,直到得到真正的返回值。
0x02 hardwire descriptors
就是通过一些手段避免使用反射。
0x03 Sandboxing
用来限制 Lua 脚本能做的事情。如果在运行时加载脚本,就存在安全性问题,而沙盒(sandboxing)可以理解为对 Lua 可以使用的功能的剪裁,比如作为 mod 语言,开发者应该不想让用户可以访问“os”、“io”或是“file”模块。
像上面说的“os”、“io”以及“file”模块是危险的,当然根据代码的不同也可能包含更多。
一条中要的建议是永远不要使用 InteropRegistrationPolicy.Automatic。
对不应暴露的类型或成员进行检查,避免直接暴露 .NET 框架类型或是 Unity 类型,如果需要应该使用 Proxy objects。
删除危险 API 的方法,是使用接受 CoreModules 枚举的 Script 构造函数,CoreModules 的例子放在附录中。
0x04 Unity3d tips & tricks
支持平台:旨在支持全部平台,但……
- 全面支持的:Standalone Windows, Linux and OS/X, Android, iOS, tvOS
- 会尽量支持的(可能会有小问题,不支持的功能):WebGL, Windows Store Applications, Windows Phone
另外,不要暴露 Unity 类型,如果需要请用 Proxy Objects;
在使用 IL2CPP 或在 AOT 平台上运行时使用 hardwire;
不要暴露 struct。
最后,使用一个更明确的构造函数初始化脚本加载器,比如 UnityAssetsScriptLoader 的显式构造函数来注册所有脚本文件。通过在项目的最开始执行,就不需要将 Unity 自己的库添加到 link.xml 以在 IL2CPP 平台上运行。
1 | Dictionary<string, string> scripts = new Dictionary<string, string>(); |
0xff
附录I
1 | /// <summary> |
附录II
- How can I redirect the output of the print function ?
script.Options.DebugPrint = s => { Console.WriteLine(s); }
- How can I redirect the input to the Lua program ?
script.Options.DebugInput = () => { return Console.ReadLine(); }
- How can I redirect the IO streams of a Lua program ?
IoModule.SetDefaultFile(script, Platforms.StandardFileType.StdIn, myInputStream);
IoModule.SetDefaultFile(script, Platforms.StandardFileType.StdOut, myOutputStream);
IoModule.SetDefaultFile(script, Platforms.StandardFileType.StdErr, myErrorStream);