继承类
如何继承 nanos world 类。 nanos world 提供了一种内置的继承内置类的方法
此功能目前仍处于实验性阶段,您可以在其正式发布前进行体验并提供反馈!
继承类
继承 nanos world 类非常简单,为此您只需要在想要继承的类上使用静态方法 Inherit:
-- 创建一个继承自 Prop 的新类,命名为 "MyNewClass"
-- 并将其存储在变量 MyNewClass 中
MyNewClass = Prop.Inherit("MyNewClass")
-- 使用默认构造函数生成它
local my_new_class_instance = MyNewClass(Vector(), Rotator(), "nanos-world::SM_Cube")
多重继承
您也可以继承自其他已继承的类:
-- 创建一个继承自 MyNewClass 的新类,命名为 "MyNewSubClass"
MyNewSubClass = MyNewClass.Inherit("MyNewSubClass")
-- 使用默认构造函数生成它
local instance = MyNewSubClass(Vector(), Rotator(), "nanos-world::SM_Cube")
重写构造函数
您可以为您的实体创建自定义构造函数,为此您需要定义 Constructor 方法:
-- 使用您想要的任何参数定义我的构造函数
function MyNewClass:Constructor(location, rotation)
-- 在此处执行任何逻辑
location = location + Vector(0, 0, 100)
-- 调用 Super 构造函数来完成构建
-- 这是原始构造函数(在此例中来自 Prop)
-- 这是必填的,如果不调用它,将会抛出错误
-- 只有在调用它之后(当类完全生成时),
-- 您才能访问原始方法和您自己的类方法
self.Super:Constructor(location, rotation, "nanos-world::SM_Cube")
-- 现在允许调用类方法
self:SetMaterialColorParameter("Tint", Color.RED)
end
-- 使用您的自定义构造函数生成它
local my_new_class_instance = MyNewClass(Vector(123, 456, 100), Rotator())
在调用 self.Super:Constructor() 之前,实体尚未完全生成。 只有在调用它之后,您才能调用其他实体方法。
全局注册表
通过父类,我们可以获取该类的所有子类列表,从而获得所有现有类的全局注册表!
local children_classes = ToolGun.GetInheritedClasses()
for _, class in pairs(children_classes) do
-- 'class' 是自定义的继承类!我们可以生成它
local p = class()
end
添加新方法
为新类添加新方法非常直接,假设我们想为 MyNewClass 添加一个新方法,只需这样做:
function MyNewClass:Explode()
-- 生成粒子
Particle(self:GetLocation(), Rotator(), "nanos-world::P_Explosion")
-- 销毁自身
self:Destroy()
end
在您编写的方法内部,您可以使用 self 来访问被调用的实体实例。
然后您就可以像往常一样调用它:
my_new_class_instance:Explode()
重写现有方法
除了创建新方法,还可以重写现有方法,只需重新定义它们即可:
function MyNewClass:SetLocation(new_location)
-- 在此处执行任何逻辑
new_location = new_location + Vector(0, 0, 100)
-- 调用 Super 设置父类 Prop 的位置
self.Super:SetLocation(new_location)
end
调用原生方法
要调用原生类方法,您可以使用特殊变量 self.Super,这将允许您直接访问原生和原始方法:
function MyNewClass:GetRotation()
-- 调用原始的 GetRotation 并在 yaw 上增加 90
return self.Super:GetRotation() + Rotator(0, 90, 0)
end
调用父级方法
除了使用 self.Super 调用原始/原生方法外,如果您有嵌套继承,还可以调用父级方法。
为此,您必须使用特殊的 Lua 语法 PARENT_CLASS.METHOD_NAME(self, ...),例如:
在 Lua 中,在使用 . 调用方法时将值作为第一个参数传递,会使该值在被调用方法内部呈现为 self(如果该方法是用 : 定义的)。
-- 继承 Prop
MyNewClass = Prop.Inherit("MyNewClass")
function MyNewClass:SetScale(scale)
-- 执行某些逻辑
scale = scale * 2
-- 调用 Super(原始 Prop 方法)
self.Super:SetScale(scale) * 2
end
-- 继承 MyNewClass
MyNewSubClass = MyNewClass.Inherit("MyNewSubClass")
function MyNewSubClass:SetScale(scale)
-- 执行某些逻辑
scale = scale + Vector(2, 2, 2)
-- 使用特殊语法调用父级 MyNewClass 方法
MyNewClass.SetScale(self, scale)
end
此规则同样适用于调用继承的构造函数!
重写 __newindex
也可以在继承类上添加自定义的 __newindex 元方法。
__newindex 元方法是一个当您尝试在实体中设置一个值时触发的函数。 例如:my_entity.something = 123。
为此,我们只需添加一个名为 newindex 的自定义方法:
方法名必须是 newindex 而不是 __newindex,因为 __newindex 是内部用于使继承正常工作的原生方法。
function MyNewClass:newindex(key, value)
Console.Log("设置一个 %s 值:%s = %s", tostring(self), key, tostring(value))
end
使用 __newindex 的一种有用方法是将其重写为自动调用 SetValue:
function MyNewClass:newindex(key, value)
self:SetValue(key, value)
end
-- 示例用法
local my_entity = MyNewClass()
my_entity.amazing_value = 123
重写 __index
__index 元方法是一个当您尝试从实体中获取一个值时触发的函数。 例如:local value = my_entity.something。
为此,我们只需添加一个名为 index 的自定义方法:
方法名必须是 index 而不是 __index,因为 __index 是内部用于使继承正常工作的原生方法。
function MyNewClass:index(key)
Console.Log("获取 %s 值:%s", tostring(self), key)
-- ... 执行某些操作
return some_value
end
您还可以使用 __index 返回一个方法:
function MyNewClass:index(key)
Console.Log("未找到 %s 键:%s", tostring(self), key)
-- 在重定向的方法内部,您将获得最初传递的所有参数
return function(self, param1, param2...)
-- ... 执行某些操作
return "triggered!"
end
-- 或者您甚至可以重定向到其他成员函数
return MyClass.SetLocation
end
local my_entity = MyNewClass()
my_entity:NonExistentMethod(123, "456")
使用 __index 的一种有用方法是将其重写为自动调用 GetValue:
function MyNewClass:index(key)
return self:GetValue(key)
end
local my_entity = MyNewClass()
local amazing_value = my_entity.amazing_value
重写 __tostring
您也可以像平时一样重写 __tostring:
function MyNewClass:__tostring()
return "My Incredible Class!"
end
原生事件
在继承类上触发的所有事件都只会在该类及其父类中触发,传递的参数是自定义实体本身,例如:
Prop.Subscribe("Spawn", function(self)
Console.Log("Spawned Prop: %s", tostring(self))
end)
MyNewClass.Subscribe("Spawn", function(self)
Console.Log("Spawned MyNewClass: %s", tostring(self))
end)
local my_entity = MyNewClass()
local my_prop = Prop()
local my_other_entity_inherited_from_prop = MyOtherClass()
-- 将输出:
-- Spawned Prop: MyNewClass
-- Spawned MyNewClass: MyNewClass
-- Spawned Prop: Prop
-- Spawned Prop: MyOtherClass
另一种订阅方式是将定义和订阅分离,这样您就不再需要第一个 self 参数:
function MyNewClass:OnSpawn()
-- self 会自动在此上下文中可用
Console.Log("Spawned MyNewClass: %s", tostring(self))
end
MyNewClass.Subscribe("Spawn", MyNewClass.OnSpawn)
多重父类示例:
MyNewClass = Prop.Inherit("MyNewClass")
MyNewSubClass = MyNewClass.Inherit("MyNewSubClass")
MyNewOtherSubClass = MyNewClass.Inherit("MyNewOtherSubClass")
Prop.Subscribe("Spawn", function(self)
Console.Log("Spawned Prop: %s", tostring(self))
end)
MyNewClass.Subscribe("Spawn", function(self)
Console.Log("Spawned MyNewClass: %s", tostring(self))
end)
MyNewSubClass.Subscribe("Spawn", function(self)
Console.Log("Spawned MyNewSubClass: %s", tostring(self))
end)
MyNewOtherSubClass.Subscribe("Spawn", function(self)
Console.Log("Spawned MyNewOtherSubClass: %s", tostring(self))
end)
local my_entity = MyNewSubClass()
-- 将输出:
-- Spawned Prop: MyNewClass
-- Spawned MyNewClass: MyNewClass
-- Spawned MyNewSubClass: MyNewClass
请注意,Prop 以及所有父类仍会为所有子类触发事件!
客户端/服务端同步
如果您在客户端和服务端都定义了实体,它们将以同步的方式正常工作! 示例:
MyNewClass = Prop.Inherit("MyNewClass")
MyNewClass.Subscribe("Spawn", function(self)
Console.Log("Spawned MyNewClass: %s", tostring(self))
end)
local my_entity = MyNewClass()
-- 将输出:
-- Spawned MyNewClass: MyNewClass
MyNewClass = Prop.Inherit("MyNewClass")
MyNewClass.Subscribe("Spawn", function(self)
-- 它在服务端生成,并会正常在客户端作为 MyNewClass 生成
Console.Log("Spawned MyNewClass: %s", tostring(self))
end)
-- 将输出:
-- Spawned MyNewClass: MyNewClass
自定义远程事件
也可以在您的类的远程实例上触发自定义事件,其工作原理类似于 Events 类。 使用以下方法:
-
:CallRemoteEvent() -
:CallRemotePlayersEvent() -
:BroadcastRemoteEvent() -
:BroadcastRemoteInRadiusEvent()
-- 继承类
MyNewClass = Prop.Inherit("MyNewClass")
-- 定义自定义方法
function MyNewClass:OnMyCustomRemoteEvent(a, b)
Console.Log("OnMyCustomRemoteEvent!", tostring(self), a, b)
self:CallRemoteEvent("AnotherRemoteEvent", Reliability.Reliable, 456, "def")
end
-- 订阅自定义远程事件
MyNewClass.SubscribeRemote("MyCustomRemoteEvent", MyNewClass.OnMyCustomRemoteEvent)
-- 继承类
MyNewClass = Prop.Inherit("MyNewClass")
-- 请注意,服务端接收到的远程事件将 'player' 作为第一个参数
function MyNewClass:OnAnotherRemoteEvent(player, a, b)
Console.Log("OnAnotherRemoteEvent!", tostring(self), tostring(player), a, b)
end
-- 订阅自定义远程事件
MyNewClass.SubscribeRemote("AnotherRemoteEvent", MyNewClass.OnAnotherRemoteEvent)
-- 生成一个实体并对该实体调用自定义远程事件
local p = MyNewClass(...)
p:BroadcastRemoteEvent("MyCustomRemoteEvent", Reliability.Reliable, 123, "abc")
类自定义默认值
在创建继承类时,可以为其设置一组默认值,只需将其作为第二个参数传递给 Inherit:
-- 继承类
MyNewClass = Prop.Inherit("MyNewClass", {
name = "My Name",
category = "breakable",
my_custom_param = 123
})
Console.Log(MyNewClass.category)
-- 输出 "breakable"
类注册事件
当您继承一个新类时,将在父类上触发 ClassRegister 事件,从而允许 Package 了解新类何时被注册。
Prop.Subscribe("ClassRegister", function(class)
-- 在这里我们可以看到一个有关默认值的实用用例
-- 因为我们可以在这里访问它
Console.Log(MyNewClass.name) -- 输出 "My Name"
-- 现在我们可以执行某些操作(例如将其添加到生成菜单中?)
end)
-- 继承类
MyNewClass = Prop.Inherit("MyNewClass", {
name = "My Name",
category = "breakable",
my_custom_param = 123
})