← 博客

2026 年 10 月版本更新

2026 年 10 月版本更新

2026-11-01
本页面为自动翻译,可能包含错误。如果发现问题,请通过“编辑此页面”链接帮助修正。

上个月我写道,2.x 只会引入兼容性更新,下一次破坏兼容性的变更将放在 3.0。这次变更在 10 月到来,同时也提供了迁移指南。

10 月,Turian 从一个带渲染器的编辑器,成长为一个真正可以用来制作游戏的引擎。物体现在会下落、会碰撞。摄像机只绘制它能看到的内容,一万个相同的箱子只需一次绘制调用。设计师可以用资源连接游戏逻辑。脚本无需协程就能等待两秒,游戏也能保存进度。同一个项目既能导出带窗口的客户端,也能导出完全没有窗口的服务器。而用于完成这一切的编辑器 Studio,终于有了一个操作方式与你熟悉的工具一致的场景视图。

采用 Material 3 主题的 Studio 3.8,场景视图中是示例 12 正在下落的箱子雨,帧统计 HUD 已显示,Inspector 中选中了一个物理刚体。

为什么是 3.0

Turian 3.0.0,相关 Issue:#35, #47, #62, #213

有两项工作无法在保持兼容性的前提下完成。渲染器必须移出引擎核心,服务器构建才能不再携带 Vulkan。主题也必须迁移到真正的样式表格式,因为旧的颜色表无法表达 Material 3 的需求。这两项工作在 2.0 发布时就已开始,继续推迟只会让以后需要迁移的 2.x 项目更多。

与 2.0 一样,这些破坏兼容性的变更集中在同一个版本中发布,并共用一份迁移指南:

变更 受影响的对象 升级步骤
Vulkan 渲染器独立为 brick 直接使用渲染器类型的代码;现有项目会自动添加该 brick 渲染器
设置和构建配置注册在项目资源中 所有项目,打开时自动转换 项目资源
glTF 成为内部网格格式,OBJ 导入改为可选 brick 含 .obj 模型的项目、导出的游戏、网格工具 模型
颜色主题改为 .pss 样式表 自定义主题、读取主题颜色的插件 主题
Gaya 设置通过类特性获取配置 带设置页面或标签栏控件的插件 Gaya
Guinevere 6 改变了绘图和样式机制 直接使用 Guinevere 绘图的代码 Guinevere

场景、预制体和 DataAsset 保留原有 ID,因此内容无需转换。3.0.0 之后的所有版本都保持了兼容性。

物体开始下落:3D 物理

Turian 3.5.0 和 3.6.0,相关 Issue:#64, #48, #111

物理功能曾是 Turian 与其他常被拿来比较的引擎之间最大的差距。这个缺口在本月补上了,而且与 Turian 的其他可选功能一样,它以 brick 的形式提供。

一共有两个 brick。org.mass4.turian.physics 包含组件、设置和后端接口约定,本身不执行模拟。org.mass4.turian.physics.bepu 则把 BepuPhysics v2 接入这套接口。Bepu 是完全用 C# 编写的高性能多线程物理库,不需要原生二进制文件,能在任何支持 .NET 的环境中运行。装好这两个 brick,物体就会下落。前端不直接引用 Bepu,因此以后可以更换后端,而不必修改场景。

如果你熟悉 Unity 的物理系统,就已经知道这套系统该怎么用了。它有意沿用了 Unity 的命名和行为:

  • 刚体:质量、阻尼、重力缩放、运动学模式、插值、连续碰撞检测、轴锁定和质心。施加力时使用与 Unity 一致的 ForceMode。
  • 碰撞体:盒体、球体、胶囊体、圆柱体、网格和高度场。同一刚体下的多个碰撞体会组成复合形状,没有刚体的碰撞体则为静态。
  • 关节:固定、铰链、滑动、球窝、距离和弹簧关节。铰链和滑动关节带有电机、限位和弹簧。
  • 角色控制器:提供 Move 和 SimpleMove,支持上台阶、爬坡和搭乘移动平台。
  • 车轮碰撞体:用于基于射线检测的车辆,提供悬挂、轮胎摩擦曲线、驱动与制动扭矩,以及转向。
  • 物理材质:以 DataAsset 形式设置摩擦、弹性和与 Unity 一致的材质组合模式。
  • 查询:射线检测、形状投射、重叠检测和穿透测试,可按月初随标签与层一起引入的物理层进行筛选。
  • 回调:任何实现处理接口的组件都能接收碰撞和触发器的 Enter、Stay、Exit 回调。
  • 布娃娃:RagdollBuilder.FromSkeleton 为骨架的骨骼匹配胶囊体和带限位的关节。骨架可以在跟随动画和受物理驱动下落之间切换,也可以恢复到绑定姿势。

示例 12 的箱子雨,约有一万个箱子,物理统计浮层显示了活动和休眠的刚体数量。

在基准测试项目中,箱子雨每秒落下 200 个箱子,直到达到一万个。在我的工作站上,导出的游戏即使在全部箱子仍处于活动状态时,也能保持约 80 帧/秒。当箱子堆稳定下来后,所有箱子都会进入休眠,一次物理步进只需不到一毫秒。这是单台机器上的数据,并非性能承诺。不过,这些数字在整个月里持续改善,因为性能分析还发现并消除了引擎其他部分的内存分配。

示例 12 的 Ragdoll 场景,程序化生成的人偶正在下落,关节辅助图形清晰可见。

目前还缺少专门的层碰撞矩阵编辑器(Inspector 目前直接显示 32 个原始掩码),也不支持跨机器确定性。Bepu 只在同一台机器上具有确定性。

少画一点,画得更快

Turian 2.1.0, 3.0.0 和 3.2.0,相关 Issue:#8, #59, #104

最快的绘制,就是省掉不必绘制的内容。

视锥体剔除

摄像机只能看到近裁剪面和远裁剪面之间的视锥体内部。在本月之前,渲染器仍会把每个网格都提交给 GPU,再由 GPU 丢弃落到屏幕外的部分。现在,每个子网格都会缓存世界空间包围范围,不在当前摄像机视野内的内容,会在记录绘制命令、甚至查找材质之前就被跳过。这适用于经过旋转和缩放的节点、程序化模型,以及透视和正交摄像机。

在一条约含 1,600 个子网格的大型街道基准场景中,从五个摄像机视角测得:

摄像机 关闭剔除 开启剔除 已提交 已剔除 变化的像素数
主视角 2.43 ms 2.12 ms 1,322 269 0
全景 2.47 ms 2.43 ms 1,591 0 0
南侧 2.55 ms 2.48 ms 1,521 70 0
西侧 2.25 ms 1.76 ms 634 957 0
东侧 1.93 ms 1.05 ms 44 1,547 0

全景摄像机看得到整条街,因此没有可以跳过的内容。东侧摄像机只能看到一个角落,几乎所有内容都被剔除,帧耗时接近减半。所有情况下,图像都逐像素完全一致。

关闭剔除
开启剔除

GPU 实例化

绘制调用本身就有开销:无论网格多小,记录每次调用都要消耗 CPU 时间。现在,渲染器会把连续使用相同网格和材质的对象合并为一次实例化绘制。一万个相同道具过去需要 8,791 次绘制调用,现在只需一次,像素结果完全相同。每个副本仍可带有独立的颜色和四个自定义值,不会因此拆分批次。

对于植被、人群或碎片这类拥有大量同模型副本的系统,新的 Instanced Model 组件通过一个 GPU 缓冲区绘制所有实例,只上传修改过的实例,并对整个集合统一进行剔除判断。一万个彩色立方体仅需约 0.6 ms 的 CPU 时间。在场景视图中点击任意实例,仍然可以选中对应节点。

示例 09 的一万个彩色立方体,帧统计 HUD 显示一次绘制调用和一万个实例。

看清每一帧

无需性能分析器就能查看这些数据。Frame Statistics HUD 在 Scene 和 Game 视图上叠加显示帧耗时、绘制调用数、实例数,以及提交和剔除数量。无头截图也会报告相同的计数,因此 CI 任务同样能检测性能回退。Studio 本身现在也按需渲染:没有变化时就停止重绘,把 GPU 留给游戏。

世界中的线条、轨迹与文字

Turian 3.8.0,相关 Issue:#110

三个小组件:

  • LineRenderer 沿一组点绘制路径,可以是平面形式,也可以始终面向摄像机,支持沿长度变化的宽度与颜色曲线,以及可选的拉伸或平铺纹理。主要用于激光和绳索。
  • TrailRenderer 记录节点经过的位置,绘制随时间淡出的带状轨迹。适合剑刃挥动、轮胎印和彗星尾迹。
  • WorldTextComponent 使用 Guinevere 能加载的任意字体,在 3D 世界中放置文字。它采用有符号距离场绘制,在不同距离和角度下仍保持清晰。可用于伤害数字和路牌。

这三个组件都遵循渲染层设置,与透明几何体一起排序,并且在场景视图和游戏中均可使用。调试时也很实用:物理测试场景用它们绘制查询线、轨迹和浮动的碰撞数值。

同一帧中的移动球体尾迹、面向摄像机的渐细线条,以及世界空间中的浮动数字。

设计师连接游戏逻辑

Turian 2.2.0 和 3.7.0,相关 Issue:#91, #55

9 月,我承诺让 DataAsset 成为设计师工作方式的核心,并介绍了 Ryan Hipple 在 2017 年关于 ScriptableObject 架构的演讲。10 月,我们实现了这个模式本身:演讲中的资源类型,以及用于连接和调试它们的 Studio 工具。

将事件、变量和监听器做成资源

新的 org.mass4.turian.ddd brick 面向 DataAsset-Driven Design(DataAsset 驱动设计),将演讲中的资源系列带到了 Turian:

  • 事件,可以携带数据,也可以不带数据,例如“玩家死亡”“收集金币”“门已打开”。
  • 变量与常量:玩家生命值是一个 FloatVariable 资源,血条、音频和存档系统都读取它,并在数值变化时收到通知。
  • 运行时集合:例如“当前所有存活的敌人”,由敌人在生成和死亡时自行维护。
  • 监听器与响应:无需脚本即可响应事件。
  • 带状态转换的枚举资源:用于菜单、游戏中、暂停等游戏状态。
  • 输入读取器:将输入动作转化为事件和变量。

DataAssetReference<T> 类型的组件字段既接受普通值,也接受变量资源,因此设计师无需请求修改代码,就能决定一个值是局部的还是共享的。UI brick 的按钮可以直接触发事件,并跟随 Enabled 和 Visible 变量。

触发事件不会分配内存,一个监听器时约需 4 ns,八个时约需 12 ns。这对游戏逻辑来说足够轻量,不过我仍会让每个 tick 和每次接触触发的引擎回调保持直接调用。

配套的 org.mass4.turian.ddd.studio brick 提供编辑工具。Assets 面板中的 Create → DDD 可以创建各类资源,View → DataAsset Overview 显示各资源由谁触发、监听和引用,并找出没有任何引用的资源。Validate wires 会检查缺失的目标和不匹配的数据类型。在 Play Mode 中,可以从编辑器触发事件、查看变量实时值并修改它。对于自定义数据类型,DDD Type Generator 会生成事件、变量和集合类。

尚未实现的包括:列表和字典资源、每个预制体实例独享的变量副本,以及变量存档。变量存档将使用存档 brick,而不是另设格式。

标签与层

节点现在可以带有多个字符串标签,通过 FindAllWithTag 等带索引的场景查询进行查找,并拥有各自独立、各含 32 个槽位的物理层和渲染层。摄像机和灯光可以按渲染层筛选,场景视图可以隐藏或锁定层。这些表保存在 Tags and Layers 设置 DataAsset 中,并使用稳定的 ID,因此重命名或重新排序不会破坏引用它们的场景或预制体。现有内容会作为 Untagged 加载到 Default 层,无需重写文件。这些表正在演进的设计值得单独一篇文章:将标签与层作为资源。

时间、等待与存档

Turian 2.1.0, 3.2.0 和 3.3.0,相关 Issue:#155, #88, #89

无需协程的等待

Unity 脚本使用协程等待,而大多数大型 Unity 项目最终会用 UniTask 取代它。Turian 直接使用 C# 自带的 async 和 await。每个组件都有一个 Async 属性,提供与组件生命周期绑定的等待:

public class Door : Component
{
    public bool IsBlocked { get; set; }

    public async ValueTask OpenAsync()
    {
        Node.Position += Vector3.UnitY;
        await Async.Seconds(2);           // 受时间缩放影响的游戏时间
        await Async.Until(() => !IsBlocked);
        Node.Position -= Vector3.UnitY;
    }
}

可以等待下一帧、下一个固定 tick、受缩放或不受缩放影响的秒数、实时时间、固定 tick 时间,也可以等待条件变为真或假。所有后续执行都会回到游戏循环,绝不会在工作线程上运行。组件销毁时,其未完成的等待会被取消,不会在已销毁对象上触发。Studio 的暂停会冻结游戏时间等待,而实时时间等待继续计时。未完成的等待不会分配内存,编译器还会标记游戏代码中的典型误用,例如 async void、Task.Delay 和保存 ValueTask。

可靠的时钟

TimeSettings 资源现在负责设置固定模拟频率,以及慢帧最多可以补跑多少个 tick。底层的共享模拟时钟统计已完成的 tick,并管理暂停与单步运行。在此之上,SimulationSession 为游戏提供权威世界状态:接受命令、去除重复命令、为 tick 编号,并维护具名且可复现的随机数流。Play Mode、无头播放器和导出的游戏共用相同的 tick 边界。测试世界在屏幕以 30、60 或 144 Hz 运行时,每个 tick 都会产生相同的状态哈希;保存并恢复后也一致。网络功能本身尚未实现,但这将是它的基础。

保存游戏

9 月的 RuntimeSave 用于记录模拟检查点。新的 org.mass4.turian.save brick 则让游戏把这些检查点和自身的存档数据保存到安全的位置:

  • 带版本的架构。 每份存档记录游戏的数据架构版本,加载时通过一系列迁移逐版本升级旧存档,不重写已保存的文件。
  • 可插拔的组成部分。 默认使用 JSON,也可接入任意序列化器(MemoryPack、MessagePack、protobuf)或容器格式,并提供可选的 AES-GCM 加密。
  • 保护已有数据。 先写入临时文件,完成后才替换存档槽。上一代文件作为备份保留,发现损坏会报告错误,而不是默默加载。
  • 检测冲突。 每次写入都声明预期替换的修订版本,因此两台机器或两个进程不会悄悄覆盖彼此的数据。同样的接口约定也适用于云存储,并提供了模拟云存档冲突的示例。

它不依赖渲染器或任何第三方序列化器,因此可用于客户端、专用服务器和测试。

示例 11 的存档演示,HUD 列出存档槽、修订版本,并显示云存档冲突提示。

场景视图走向成熟

Turian 2.1.0 至 3.0.0,相关 Issue:#82, #68

使用编辑器的大部分时间都花在场景视图中,因此 10 月 Studio 的工作也主要集中在这里。

辅助操纵器。 变换手柄经过重做:更粗的箭头、实心缩放方块、填充的双轴手柄,以及面向摄像机的旋转圆弧。悬停时高亮,拖动时显示已旋转的角度。旋转支持世界轴和局部轴,并带有角度吸附。组合工具可同时显示移动、旋转和缩放手柄。手柄始终显示在场景几何体前方,因此即使物体埋在墙里,也能移动它。

旧版变换手柄
组合变换工具

朝向。 角落里有一个紧凑的朝向控件:带轴标签的小立方体。点击某个轴即可沿该轴观察,点击中心则在透视和正交投影间切换,同时保持取景。参考网格现在能在摄像机处正确裁剪,默认位于 XZ 平面,并以彩色显示原点坐标轴。设置页面可以调整网格间距、强调程度和不透明度。

Scene 面板右上角的朝向操纵器,以及从斜角观察的参考网格。

导航。 按住鼠标右键控制视角时,地平线保持水平,不会翻转倒置。方向键通过可编辑的连续输入快捷键移动摄像机,Shift 用于加速。Scene 面板把工具、坐标空间和吸附设置集中在紧凑的图标工具栏中,Cap FPS 设置则能减少 Studio 空闲时给笔记本带来的发热。

多对象编辑。 层级面板和 Assets 面板支持 Ctrl+单击、Shift+单击范围选择及全选,场景视图支持框选。Inspector 会显示选中对象共有的组件,不同值显示为“—”。修改共有字段时,所有选中对象都会更新。只修改变换的一个轴,其他轴仍保留各自的值。批量删除、复制、更换父节点和移动,都可以各用一次撤销恢复。

层级面板中选中了三盏灯,Inspector 显示不同的强度和颜色值。

其他改进。 提交日志将这部分概括为“修复了 1001 个快把我逼疯的问题”,差不多就是这么回事。最容易注意到的有:

  • 临时场景。 File → New Scene(Ctrl+N)打开一个用于试验的场景,保存之前不会创建资源。
  • 重新设计的 Assets 面板。
  • 加载卡片会在项目打开时显示 Studio 正在导入和编译的内容。
  • 更快地重新打开项目。 没有变化时复用已导入的文件,Build & Run 只复制发生变化的资源。

全新外观

Turian 3.0.0,相关 Issue:#212, #213, #214, #215, #217

Studio 现在默认使用由 Guinevere 绘制的 Material 3,即 Google 当前的设计系统。

Material 3 外观
经典外观

底层外观分为四项独立选择:外观样式(每个控件的形状)、颜色主题、图标集和字体。主题采用 .pss 样式表,这是 Guinevere 对 CSS 的实现方式,编辑时支持实时重载。图标集提供 Mono 和 Color 两种版本。

外观样式和主题都是 brick,任何人都可以制作和分享。turian-cli 可以在命令行中把主题打包成 brick。Bricks 面板已移入 Gaya,并区分 Studio 自身的 brick 和项目的 brick。首批两个额外样式带来了一点怀旧感:Windows XP 和 Windows 9x,安装方式与其他 brick 相同。

Windows XP 外观
Windows 9x 外观

游戏内 UI 也受益于此:使用 Vulkan 时,它现在由 GPU 渲染。

没有窗口的 Turian

Turian 3.0.0 至 3.8.0,相关 Issue:#35, #62, #292, #313, #344

并非每种游戏构建都需要屏幕。专用服务器或 CI 中的无头模拟用不到 GPU,也不应该被迫打包相关渲染支持。

  • 构建配置。 一个项目现在可以保存多个构建配置,每个配置拥有平台、客户端或服务器角色、构建设置,以及独立的编译符号和参数,通过 Build Profiles 窗口编辑。Assets/Project.dataasset 会注册这些配置,以及项目设置和最后保存项目时的引擎版本。
  • 无头服务器。 渲染器已经是 brick,服务器配置只需不包含它。无头播放器运行相同的场景和模拟 tick,并可通过 Ctrl+C 正常退出。
  • NativeAOT 服务器。 桌面服务器配置可以提前编译为单个原生可执行文件,无需安装 .NET 运行时,也无需等待 JIT 预热。编辑器会生成过去依赖反射的启动代码,如果缺少必要内容,会在导出时明确报错,而不是等到运行时。我们的测试服务器能够正常导出、运行和退出,且没有裁剪警告。
  • 更精简的 brick。 引擎继续向精简核心演进。摄像机、游戏内 UI、OBJ 导入、存档和 Windows 外观样式现在都有独立的 brick 仓库。本月,本地化也从核心和渲染器中移出,成为独立 brick。brick 还可以像 Bepu 后端那样声明所需的 NuGet 包,并依赖通过 Git 提交或压缩包校验和锁定的源码。

Build Profiles 窗口,包含一个 Windows 客户端配置和一个 Linux NativeAOT 服务器配置。

Guinevere 从 5.7 到 6.5

Studio 由 Guinevere 绘制,因此上文的许多功能也来自 Guinevere 的开发。本月的版本从 5.7 推进到 6.5。唯一包含破坏兼容性变更的是 6.0,它也是 Turian 3.0 新外观的基础:

  • 样式。 Excalibur 控件默认采用 Material 3,旧外观仍可通过 ExcaliburStyles.Classic 使用。样式表改为独立 MASS4.Guinevere.Styling 包中的 .pss 文件,支持值表达式、主题颜色函数、系统配色与强调色、文字阴影和发光,以及位图和九宫格背景。
  • GPU 绘图。 Silk.NET、OpenTK 和 Raylib 集成通过 Skia 在 GPU 上绘图,保留 CPU 回退路径,所有集成都默认按需渲染。
  • 动画。 通过键标识的弹簧和补间动画、属性过渡,以及带动画的状态变化。
  • 渲染效果。 有序效果栈可作用于整个面板,包括其中的文字、形状和图像。13 个内置滤镜涵盖模糊、发光、阴影、颜色调整、像素化、色彩分离和锐化,可通过 C# 或 .pss 配置。
  • 代码编辑器。 新的 TextEditor 包是一个采用视图虚拟化的源码和着色器编辑器,支持多光标、矩形选择、折叠、查找替换、补全、悬停文档和后台诊断,能够正常处理十万行文件。
  • 文本。 支持带样式片段、链接和内嵌图像的富文本,带语法高亮代码块的流式 Markdown 控件,排版控制(间距、制表位、两端对齐、字偶距),以及可选的连字和组合标记字形塑形。文本布局现在会被缓存,并由测量和绘制共享。
  • 图编辑。 新的 MASS4.Guinevere.Graphs 包提供节点图编辑器:可平移、缩放的画布上的节点、端口和连线,以及框选、可折叠分组、复制粘贴、撤销、缩略图和 .pss 样式设置。所有操作都可通过键盘完成;在 Linux 上,它还通过新的桌面无障碍桥接层向屏幕阅读器提供信息。
  • 游戏、手柄与触控。 手柄焦点导航、语义化控制器动作、带可选手势的多点触控、适配手机安全区域的视口,以及游戏内 UI 的 HUD 基础元素。
  • 细节改进。 由可替换部件组成的颜色选择器、可搜索的多选下拉框、支持标志位的枚举编辑器,以及显示停靠落点的标签页。

打开着色器的 TextEditor 示例,展示语法高亮、折叠代码块、补全和行内诊断。

有你相助

还有许多修复、调整和增强未在这里列出。完整列表请查看更新日志。文档涵盖了 brick、渲染、辅助操纵器和主题。如果遇到问题,请在 GitHub 告诉我。如果愿意资助下一次更新,可以通过 Patreon 或 Ko-fi 支持开发。

#release#engine#studio#physics#dotnet