这几天整了一下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#甜味十足的语法糖,现在要转换成这种死板的语法,实在是有些不适应。