跳至正文

11 月:实体系统、UDS 与 UE5.1!

· 9 分钟读完
Gabriel • SyedMuhammad
lead developer™

实体系统、UDS 与 UE5.1!

重构发布​

正如上次博客新闻中所宣布的,我们正在对 API 进行一系列重构,以更好地标准化它。我们有一长串的更改,我正在逐步进行。

使我们能够在不断兼容性的情况下进行更多更改的主要一项是上次更新中发布的 compatibility_version。 Package.toml 中的此配置将确保包在发生破坏性更改的未来更新中继续工作。

此外,我们已经有第一个功能来尝试 compatibility_version 系统:新的 Events.SubscribeRemote 方法!此方法将仅订阅远程调用的方法,完全分离了订阅本地和远程事件的行为。此方法的更改导致了一个破坏性更改,因为所有现有的 Events.Subscribe 将不再触发远程事件。但 compatibility_version 确保它们继续工作,直到你将值更新到版本 1.22 或更新版本。

我也在着手进行几个静态类的重构,并更改一些方法的名称(例如 HTTP.RequestSync 将被标准化为仅 HTTP.Request,异步版本将变为 HTTP.RequestAsync)。

我再次强调,如果你对事件或方法名称等其他更改有建议,请不要犹豫,说出来!像这样的大规模重构机会很少见!

实体与继承系统​

这是一个非常特别的更新,我在过去几个月中一直在努力! 🥳

新的继承系统是一个完全改变创建脚本范式的补充!该系统允许你继承现有类来创建自己的自定义类,并像原生类一样生成它们!

-- 创建一个名为 "MyNewClass" 的新类,继承自 Prop
MyNewClass = Prop.Inherit("MyNewClass")

-- 使用默认构造函数生成它
local ent = MyNewClass(Vector(), Rotator(), "nanos-world::SM_Cube")

内置事件​

原生事件将返回你的类而不是原生类。例如:

-- 这将为所有 Prop 及其后代触发,
-- 并且 'prop' 的值将是子类本身
Prop.Subscribe("Destroy", function(prop)
-- 你可以检查实体是否来自继承的类类型!
-- IsA 递归检查是否有任何父类是来自该类型
if (NanosUtils.IsA(prop, MyCube)) then
-- prop 是 MyCube 类型
end
end)

自定义方法​

轻松向你的类添加新方法!

MyNewClass = Prop.Inherit("MyNewClass")

function MyNewClass:MyMethod()
-- 做一些事情
end

自定义构造函数​

该系统还允许创建个性化构造函数:

-- 创建一个名为 "MyCube" 的新类,继承自 Prop
MyCube = Prop.Inherit("MyCube")

-- 定义我的构造函数,参数随意
function MyCube:Constructor(loc, rot)
-- 在这里做任何逻辑
loc = loc + Vector(0, 0, 100)

-- 调用父类构造函数完成构建
self.Super:Constructor(loc, rot, "nanos-world::SM_Cube")
end

-- 使用你的自定义构造函数生成它
local cube = MyCube(Vector(123, 456, 100), Rotator())

多重继承​

我们也可以从其他继承类继承!

MyCube = Prop.Inherit("MyCube")
MyMiniCube = MyCube.Inherit("MyMiniCube")
MySuperMiniCube = MyMiniCube.Inherit("MySuperMiniCube")

自定义实体事件​

现在我们可以在自定义类上触发远程事件,以实现轻松的网络通信:

Client/Index.lua
function MyCube:OnMyAnotherEvent(my_param)
Console.Log(my_param)
end

MyCube.SubscribeRemote("MyAnotherEvent", MyCube.OnMyAnotherEvent)

local my_cube = MyCube()
my_cube:CallRemoteEvent("MyCustomEvent", 123)
Server/Index.lua
function MyCube:OnMyCustomEvent(player, my_param)
self:BroadcastRemoteEvent("MyAnotherCustomEvent", "hello")
end

MyCube.SubscribeRemote("MyCustomEvent", MyCube.OnMyCustomEvent)

全局注册表​

通过父类,我们可以获取该类的所有子类列表,从而获得所有现有类的全局注册表!

local children_classes = ToolGun.GetInheritedClasses()
for _, class in pairs(children_classes) do
-- 'class' 是自定义的继承类!我们可以生成它
local p = class()
end

进一步阅读​

还有更多内容!比如实体事件或调用父类方法!在专门文档页面阅读所有内容。

沙盒改进​

借助这个新的实体系统,我正在花时间对沙盒游戏模式进行一些改进,因为现在该系统允许更好地组织事物之间的交互方式。

实体​

我正在将几乎所有内容转换为这个实体系统,例如火炬(生成菜单中的近战武器)现在变成了一个继承自 Melee 的 Torch 实体:

Torch = Melee.Inherit("Torch")

function Torch:Constructor(location, rotation)
self.Super:Constructor(location or Vector(), rotation or Rotator(), "nanos-world::SM_Torch")
self:AddAnimationCharacterUse("nanos-world::AM_Mannequin_Torch_Attack")
self:SetImpactSound(SurfaceType.Flesh, "nanos-world::A_Punch_Cue")
self:SetDamageSettings(0.5, 0.25)
self:SetCooldown(1.5)
self:SetBaseDamage(25)

local light = Light(Vector(), Rotator(), Color(1, 0.7, 0.4), LightType.Point, 100, 1000)
light:AttachTo(self, AttachmentRule.SnapToTarget, "", 0)
light:SetRelativeLocation(Vector(0, 0, 75))

local particle = Particle(Vector(), Rotator(), "nanos-world::P_Fire", false)
particle:AttachTo(self, AttachmentRule.SnapToTarget, "", 0)
particle:SetRelativeLocation(Vector(0, 0, 50))
end

我们可以这样生成它:

local my_torch = Torch(Vector(), Rotator())

生成菜单​

我正在尝试摆脱 "AddSpawnMenuItem" 函数。并取而代之,用继承注册系统替换所有内容,你只需继承一个类,它就会像魔法一样出现在生成菜单中!

例如,我们能够 Melee.GetInheritedClasses() 并获取此包中定义的所有自定义近战类(如 Torch),并自动在生成菜单中显示它们!

工具​

现在我们将有一个名为 ToolGun 的工具基类。这样我们只需继承这个类来创建新工具,它们将自动显示在生成菜单中并作为工具枪工作!

看看气球枪现在是如何定义的:

BalloonGun = ToolGun.Inherit("BalloonGun")

function BalloonGun:Constructor(location, rotation)
-- 调用父类 ToolGun 构造函数
ToolGun.Constructor(self, location, rotation, Color.VIOLET)
end

function BalloonGun:OnSpawnBalloon(player, location, ...)
-- 在这里做原始逻辑...
...

-- 生成一个气球实体!
local balloon = Balloon(location, rotation, ...)
end

BalloonGun.SubscribeRemote("SpawnBalloon", BalloonGun.OnSpawnBalloon)

在客户端,我们只需定义它并设置一些属性,这些属性将从工具枪和生成菜单中读取:

BalloonGun = ToolGun.Inherit("BalloonGun")

-- 定义 ToolGun 和 SpawnMenu 使用的一些属性
BalloonGun.name = "气球"
BalloonGun.image = "package://sandbox/Client/Tools/BalloonGun.webp"
BalloonGun.tutorials = {
{ key = "左键点击", text = "生成气球" },
{ key = "撤销", text = "撤销生成" },
{ key = "上下文菜单", text = "气球设置" },
}

function BalloonGun:OnFire(shooter)
-- 在这里做原始逻辑...
...

-- 调用远程以生成气球
self:CallRemoteEvent("SpawnBalloon", location, rotation, ...)
end

BalloonGun.Subscribe("Fire", BalloonGun.OnFire)

大功告成!不再需要调用任何 "AddToSpawnMenu" 函数了!我们只需继承它的一个已知类!

提示

注意我们定义了一个名为 Balloon 的类,这是气球的新实现。 😉

上下文菜单​

此外,我正在对上下文菜单进行一些改进,使其更加模块化。现在它允许动态添加新条目,我将把它与 ToolGun 系统集成以允许工具自定义,看看这个使用气球枪的示例:

这个新的上下文菜单允许通过 Lua 100% 自定义它,无需在其上添加新的硬编码 HTML/JS 条目。

虚幻引擎 5.1​

本月,Epic Games 发布了虚幻引擎 5.1。这对我们来说是一个期待已久的版本,特别是预期的修正。

升级进展顺利,这些升级中最困难的部分是迫使每个人也升级他们的资产和项目。

物理与缆绳​

自 5.0 以来,缆绳渲染和物理约束完全损坏,我甚至向 Epic 报告了这个错误,他们修复了这两个问题,但将发布推迟到了 5.1。

但终于我们可以再次为气球物理完美工作而高兴了! 😎

Nanite 与 Lumen​

两个系统自 5.0 以来都获得了巨大的升级,但使用最新技术的一个缺点是,我们需要强制游戏客户端使用 DirectX 12,因为这是 Nanite 和某些 Lumen 功能工作的要求。

我仍会研究是否应该强制使用 DX12,我不想通过做出这样的决定而在未来产生不可预见的后果。

Ultra Dynamic Sky​

Ultra Dynamic Sky 是虚幻引擎市场上最好的资产包之一,它为游戏提供了动态天空和天气解决方案!

我决定将这个天空系统原生地包含在 nanos world 中。它将用一个新的静态类:Sky 替换我们现有的 DefaultSun(在 World 类上),该类将很快™ 与虚幻引擎 5.1 更新一起发布!

如果你还不了解它,请观看这个产品功能视频:

结语​

新实体系统的更新是巨大的!我对结果非常满意,它的可用性变得非常简单,这开启了太多新的可能性!

这一变化将使 nanos world 在脚本能力方面提升到另一个水平。我正在研究更多方法,以提供更简单的方法来创建自定义脚本扩展,例如一种创建实体并在包上动态加载的方法,而无需在原始包中通过 Package.Require 手动加载它们。

仅凭现在我们可以简单地定义一个继承自另一个类的新类,并且这足以让它在沙盒的生成菜单中显示,就已经很棒了!

我希望完成仍在进行的静态类重构,并开始重命名一些非标准化的类方法。兼容模式功能将帮助我们很多人在这些更改中保持事物正常工作。并尽快实现我向所有人发布 UE 5.1 更新的愿望,这最后一个将是最痛苦的,因为它需要我们重新烘焙和更新商店中的所有资产。 😜

此外,我希望在 Discord 上开始关于扩展类未来的讨论,它目前无论新的继承系统如何都能工作,但它有几个问题,我想听听你们的意见来决定它的未来。

希望你喜欢!感谢反馈!下个月见!