这几天整了一下lua,打算以此增加demo的灵活度,为以后实现热更新做准备。框架采用tolua,这框架该有的都有,但用起来就是没有Spring等等大型的框架好用,且其中的坑比之Spring有过之而无不及,简直梦回初学Spring的时候,一个问题能捣鼓一天,最后发现其实简单的不得了。这个系列的博客就来记录下我踩的tolua坑,避免以后再掉到相同的坑里。
我记得之前看见有人说过这个事,但我真正遇到的时候却忘了这茬。 tolua在向LuaState注入我们指定的类型后,调用方式一般是和在C#中一样的,类似于
instance = Thunder.UI.BaseUi()这样的调用(为什么Markdown连lua的高亮都没有??果然lua还是太小众了么,还是Notepad兼济天下),一般来说没什么问题,命名空间、类、属性等都是和C#一样通过句号(.)来获取,但是方法就不一样了,我们知道,方法是有静态和非静态之分的,通过static修饰符来区分。C#、Java等语言调用时都一视同仁,使用(.)来获取。lua则比较标新立异,非静态用(:),静态则用(.)。举个例子:
namespace Thunder.Vehicle { public class BattleShip { public static void Shoot() { Debug.Log("Shoot"); } public void Person Invade() { Debug.Log("Create Person"); return new Person() } } public class Person { public static void Melee() { Debug.Log("Melee!!"); } } }这个类被注入了lua的环境,那么需要按如下方式调用:
battleShip = Thunder.Vehicle.BattleShip() battleShip.Shoot() battleShip:Invade().Melee() -- Output: -- -- Shoot -- Create Person -- Melee!!可见BattleShip对它的静态方法Shoot使用(.)调用,对非静态方法Invade使用(:)调用。返回的Person使用(.)调用它的静态方法Melee。此外Thunder.Vehicle.BattleShip也使用(.)来进行命名空间的选择。
tolua对于泛型的支持可谓是十分稀烂,不过对于lua来说,或许本来也就没有什么好的方法来让它适配泛型,因为它本身就是弱类型的语言。但又要与C#这种强类型的语言进行交互,还是有处理这方面的必要的。先提个需求,看看tolua怎么处理这事:
有战舰(BattleShip)和人类(Person)两种对象,我们需要让战舰通过某种方式接收士兵。具体的实现方式是,在lua文件中定义一个方法,返回人类的list。类的定义如下:
namespace Thunder.Entity { public class BattleShip { public LuaState luaState; private List<Person> _Marine; public void ReceiveSoldiers() { _Marine = luaState.Invoke<List<Person>>("ReceiveSoldiers",true); } } public class Person { public int Id; public Person(int id) { Id = id; } } }重点,需要向CustomSettings.cs中的customTypeList添加:
_GT(typeof(System.Collections.Generic.List<Person>)), _GT(typeof(Thunder.Entity.BattleShip)), _GT(typeof(Thunder.Entity.Person))泛型类型是不能单独使用的,需要按照特例来添加。这就使得泛型这种优雅的语法完全迟钝化,不过这也是没办法的事。接下来是lua:
function ReceiveSoldiers() soldiers = System.Collections.Generic.List_Thunder_Entity_Person() for id=0,300,1 do soldiers:Add(Thunder.Entity.Person(id)) end return soldiers end可以看见,lua创建list对象时使用的名称是List_Thunder_Entity_Person,看到这里,大家应该很快就能发现:在lua中调用tolua包装好的泛型类,需要将(.)转换为(_),如果有多个类型参数,需要依次排列,顺序不能颠倒,而且需要使用FullName,也就是包含了namespace的名称,例如List_Thunder_Entity_Person_Thunder_Entity_BattleShip,不能省略namespace。 鉴于这种情况,我的解决方案就是设计接口的时候,能不用泛型就不用,尽量使用传统的语法来完成需求。不过吃惯了C#甜味十足的语法糖,现在要转换成这种死板的语法,实在是有些不适应。
