9 月:维度!
维度!

维度 🌌

我自豪地宣布 nanos world 最受请求集成的功能之一已实现:维度! 🥳
在这个初始版本中,可能会出现漏洞、不同步和奇怪的事情,请立即告诉我!
维度是客户端中分开的世界,玩家和 Actor 在其中是分开的。默认情况下,所有玩家和实体都在维度 1 中。将玩家移到另一个维度后,所有不属于该维度的实体对该玩家来说都会被销毁,而新维度中的所有实体都会为他生成。
API 非常简单:
actor:SetDimension(number)
通过发送一个实体,它们的“子级”也会移动,例如:
- 发送一个 Actor 时,所有其他附加的 Actor 也会随之移动。
- 发送 Player 时,Character 会随之移动。
- 发送 Character 时,Player 不会一起移动。 Weapon 或 Prop(以及附加物)会移动。
- 发送 Vehicle 时,所有乘客也会被发送。
我还添加了一个新方法以便于使用事件:
Events.BroadcastRemoteDimension(dimension, event_name, params...)
此事件将发送给该维度中的所有玩家。
如果通过远程事件发送与玩家不在同一维度的实体,则参数将被替换为 nil,并显示警告。
新的瞄准阻挡动画
我重做了武器在面向障碍物时被阻挡的方式。之前它会将 AimMode 切换为未瞄准,导致非常糟糕的效果,甚至使准星稍微移动。
现在武器会程序化弯曲,且 AimMode 不会改变,大大改善了游戏体验!
服务器同步值
现在可以使用 Server.SetValue() 方法在服务器和客户端之间拥有全局同步值,新增了一个 bool 参数 sync_on_clients。这些值将像往常一样通过 Client.GetValue() 存在于所有客户端上。
蓝图改进
我们的蓝图功能得到了增强!
事件调度器

现在可以从 Lua 绑定到蓝图事件调度器了!使从蓝图到 Lua 的通信成为可能!
-- 生成蓝图
local blueprint = Blueprint(Vector(), Rotator(), "my-asset-pack::BP_AwesomeBlueprint",)
-- 订阅蓝图事件调度器
blueprint:BindBlueprintEventDispatcher("GorgeousDispatcher", function(self, arg1, arg2)
-- arg1 是字符串,arg2 是整数
Console.Log("从蓝图调用!", arg1, arg2)
end)
你可以在我们的新文档页面找到更多示例:蓝图通信!
函数返回值
现在也可以从蓝图函数在 Lua 中获取返回值了!
传递 Actor
我们可以通过参数在蓝图调用中传入和传出 Actor,在从 Lua 接收或发送时它们将被正确解析为对应的实体!例如你可以创建一个接收 Character 或 Actor 作为参数的蓝图事件,并通过 Lua 传递它,它将被自动解析并编组为正确的蓝图变量类型!
脚本事件变量
现在我们的内部变量使用 8 字节传递浮点数和整数,允许通过事件(包括网络事件)传递更大的值!
Tick 性能提升

我成功大幅提升了生成数千个实体时的性能(大多数是由关卡 StaticMesh 引起的。我实现了智能算法,根据距离和实体的实际 tick 需求来改进某些实体的 Tick 计算执行时机。
在某些情况下,这将 CPU 使用率提升了近 10 倍!
客户端网络主控
新增的一项实验性功能是,如果玩家是该实体的网络主控,则允许在客户端调用方法。
例如,现在可以从客户端调用 TranslateTo、RotateTo、SetForce、AddImpulse、SetWeaponAimMode 和 SetViewMode!例如,这可以大大改善 Sandbox 物理枪的行为!

WebUI 原生表
现在你不再需要对表使用 JSON.stringify() 来传递给 WebUI 事件了!你可以直接将它们作为参数传递,它将被原生解析,这意味着传递大表时性能更高,而且你不再需要担心字符串化或解析它们,因为它们已经以原生表的形式作为参数传入!
File JSON 解析器
我们的 File 实体获得了一个新方法,可以原生加载 JSON 文件并将其表获取到 Lua 中:
File.ReadJSONAsync()
File.ReadJSON()
与将其作为原始字符串读取并使用 Lua JSON 库相比,我们获得了巨大的性能提升:
加载一个大 JSON 文件(9.5 MB)的基准比较
使用 Lua JSON 库解析:耗时 9964ms。
使用原生 JSON 解析器解析:耗时 1057ms。
CEF 改进
加载屏幕
我完成了将 CEF 集成到加载屏幕的工作,现在 Ultralight 已完全从 nanos world 中移除。
我们还为加载屏幕添加了一项新功能:能够停止主菜单音乐!为此你需要从 JS 调用一个事件:
Events.Call("StopMenuMusic");
更好的错误处理
错误处理也得到了改进,之前我们在加载无效文件时没有错误反馈,现在有了。
Character 混合空间
现在可以将自定义运动混合空间导入到你的 Character 中。你可以覆盖站立、下蹲和卧倒动画。还可以设置它们之间的过渡动画!
我们为此添加了新的 API 方法:
Character.SetAnimationIdleWalkRunStanding(anim_path, enable_tip)
Character.SetAnimationIdleWalkRunCrouching(anim_path, enable_tip)
Character.SetAnimationIdleWalkRunProning(anim_path)
Character.SetAnimationsTransitionStandingCrouching(anim_standing_to_crouching, anim_crouching_to_standing)
Character.SetAnimationsTransitionCrouchingProning(anim_crouching_to_proning, anim_proning_to_crouching)
你可以在我们的新文档页面找到更多示例:Character 运动动画!
预缓存资产
现在可以预缓存资产使其在加载屏幕中加载(避免游戏过程中的 FPS 峰值),为此你只需调用:
Assets.Precache(asset_path)
所有资产在引用后都会自动预缓存,所以这个新方法在需要预缓存将来使用或动态加载的资产时很有用。
更好的穿墙射击
我们的射击穿墙效果不能正常工作,我决定重做它。现在穿射水体的距离远达 10 倍,造成的伤害也更大(之前子弹无法穿透水体)。
我们还添加了一个新的调试设置来在世界中绘制武器的子弹轨迹,对调试非常有用:

菜单改进
帧率
我们的主菜单得到了一些改进,FPS 限制被硬编码提升到 120(之前是 60),加载期间提升到 60(之前是 30),这仍然不够好,因为我想让它自动限制到显示器刷新率。
标签
我们的标签页获得了新标签以改善用户体验,主要是在创建新游戏时。现在无需进入那些菜单就能知道你已经选择了多少依赖项:
新游戏
此外,在新游戏菜单中,现在所有配置(包括依赖项)都从磁盘的 Config.toml 文件加载。所以我们不必每次启动服务器时都重新配置它!

调试设置
我们获得了 3 个新的调试设置:
Visualize Bullet TracesVisualize Vehicle DoorsVisualize Character Capsule

杂项改进
本月我们还获得的其他一些改进和小型新功能:
- 载具现在会接收
TakeDamage事件! - 现在可以通过新的
SetShowFlags()方法切换一系列 SceneCapture 渲染功能。 - 我们的控制台在客户端侧获得了多项改进。
- 现在可以将材质、WebUI 和 SceneCapture 绘制到
Canvas中(实验性)! - 我在脚本 API 中实现了一种支持多个返回值的方式,因此一些方法将开始支持这一特性!
- 现在如果发生任何失败,数据库将返回一个错误参数。
- 所有角色交互(抓取道具、拾取武器、进入载具)都已重做,现在与大物体交互时的追踪是 100% 精确的。此外,我还移除了为此所需的额外组件,这将提升在生成过多可交互物体时的性能。
结语
本月我们取得了许多重大进展!蓝图改进对我来说是一项巨大成就,因为我不得不对虚幻引擎进行庞大的逆向工程,并使用多种内存技巧来拦截事件。
维度的加入是一项呼声极高的功能,极大地扩展了游戏创作的限制!
性能改进总是非常受欢迎的,我也乐于进行这些改进,我们仍有很大的进一步提升空间!
此外,我投入了一些时间进行内部的大规模重构,部分代码开始变得臃肿混乱,并且没有使用最现代的 C++20 技术。有时候这些重构对于重新掌控我们自己的代码非常有帮助。 😄
在接下来的更新中,我计划重新组织 API(正如我们在上次 #poll 中讨论的那样),我们添加的方法开始在静态类中积累,而它们之间缺乏凝聚力。我打算将它们分开,并为每个创建新的静态类,使理解和搜索 API 中的方法变得更加容易。
我开始实现的另一项改进是实体支持多个构造函数。这将使我们能够以更精简的方式生成实体。例如,如果我们想生成 2D 或 3D 声音,这将非常有用(2D 声音不需要位置参数)。
这就是本月的全部内容,下个月我将带来更多改进和新闻!非常感谢! 💖
