众所周知,Lua作为一种很小却很灵活的脚本语言,如今广泛应用于各种网游的热更新。毕竟是解释型语言嘛,不需要提前编译。我们可以把它打成AssetBundle,现场解压运行。
但是,相信很多做游戏开发的童鞋,从C#等强类型语言转去写Lua代码的时候,可能都感叹过,Lua真难用!!!找个引用要找半天,还经常打错字段,更重要的是,它不支持面向对象啊!我本来就是单身,你还要剥夺我new对象的权利吗?╥﹏╥
不过,随着对Lua的深入♂了解,我发现它还是很迷人的,而且,以上问题前辈们早就有了解决方案。
本文就来谈谈Lua如何实现面向对象。
以C#为例,我是这么理解class的,class是一个数据结构,它将状态(字段)和操作(方法或函数)组合在一个单元中,我们可以使用这个类去动态创建实例(instance),这些实例也被成为对象(object)。
比如,我们定义了一个class bird,
public class Bird
{
public int color;
public bool shitOnPeople;
public void Fly()
{
}
}
再通过这个鸟模板new了好几只鸟,这些鸟的字段可能不同,有彩色的鸟,有黑色的鸟,还有些鸟喜欢往人头上拉屎... 但是它们都具有鸟的能力,比如飞翔。
那么,Lua中有没有类似class的东西呢,让我们看看,number不对,string不对,table...这个好像行。那我们来写下试试:
Bird = {}
Bird.color = Color.black
Bird.shitOnPeople = true
Bird.Fly = function() end
好像不太对啊,这样子写出来就是一个实例了。这就是一只白色的,喜欢往人头上拉屎的鸟,而不是泛指鸟类。
那么怎么用lua实现类呢?我们先来思考下C#类的特性:
类支持继承和多态性。
我们暂时不管多态,先来看下继承。现在定义一只喜鹊,继承自Bird:
public class Swallow : Bird
{
public void Sing()
{
}
}
虽然我们没写color字段,没写Fly方法,但通过继承的特性,我们用喜鹊还是可以访问到这些东西的。也就是说,如果子类访问自己未定义的方法(或字段),就会去找父类中的方法(或字段)。
等等,好熟悉的感觉,这不就是Lua中的元表吗?!
lua查找表中的元素时规则如下:
1. 在表中查找,如果找到,返回该元素,找不到则继续
2. 判断该表是否有元表,如果没有元表,返回nil,有元表则继续
3. 判断元表有没有__index方法,如果__index方法为nil,则返回nil;如果__index方法是一个表,则重复1、2、3;如果__index方法是一个函数,则返回该函数的返回值
完美,我们来试一下。
Bird = {}
Bird.color = white
Bird.shitOnPeople = false
Bird.Fly = function()
print('i can fly~')
end
Swallow = {}
Swallow.color = black
Swallow.Sing = function()
print('i can sing~~')
end
setmetatable(Swallow, {__index = Bird})
Swallow.Fly()
运行结果如下:
我们封装一下,不要每次都写setmetatable这句话了,让它变的更像C#。
Class = function(class)
local proto = {}
setmetatable(proto, {__index = class})
proto.New = function()
return setmetatable({}, {__index = proto})
end
return proto
end
Swallow = Class(Bird)
Swallow.color = black
Swallow.Sing = function()
print('i can sing~~')
end
-- setmetatable(Swallow, {__index = Bird})
Swallow1 = Swallow.New()
Swallow2 = Swallow.New()
Swallow1.Fly()
Swallow2.Sing()
运行一下:
Perfect!
稍微解释下上面一段代码,我们用了两个setmetatable,第一个用在了proto身上,proto就相当于c#中的Class。我们为它增加了一个new方法,在new方法中我们又返回了一个以proto作为元表的空表,这相当于c#中的实例。
不过,这样还不够面向对象,我们还少了构造函数,来加一下吧
Class = function(base)
local proto = {}
setmetatable(proto, {__index = base})
proto.base = base
proto.New = function(...)
local obj = setmetatable({}, {__index = proto})
-- 递归的执行父类构造函数
local create
create = function(c, ...)
if c.base then
create(c.base,...)
end
if c.Ctor then
c.Ctor(obj,...)
end
end
create(proto, ...)
return obj
end
return proto
end
顺便给proto增加一个__classtype字段,用来实现C#中is的功能:
proto.__classtype = proto
到这里,基本上就达成目的了。
附上源码地址:
https://github.com/787786815/LuaOOP.githttps://github.com/787786815/LuaOOP.git