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

- 为什么是 3.0
- 物体开始下落:3D 物理
- 少画一点,画得更快
- 世界中的线条、轨迹与文字
- 设计师连接游戏逻辑
- 时间、等待与存档
- 场景视图走向成熟
- 全新外观
- 没有窗口的 Turian
- Guinevere 从 5.7 到 6.5
- 有你相助
为什么是 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为骨架的骨骼匹配胶囊体和带限位的关节。骨架可以在跟随动画和受物理驱动下落之间切换,也可以恢复到绑定姿势。

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

目前还缺少专门的层碰撞矩阵编辑器(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 时间。在场景视图中点击任意实例,仍然可以选中对应节点。

看清每一帧
无需性能分析器就能查看这些数据。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 加密。
- 保护已有数据。 先写入临时文件,完成后才替换存档槽。上一代文件作为备份保留,发现损坏会报告错误,而不是默默加载。
- 检测冲突。 每次写入都声明预期替换的修订版本,因此两台机器或两个进程不会悄悄覆盖彼此的数据。同样的接口约定也适用于云存储,并提供了模拟云存档冲突的示例。
它不依赖渲染器或任何第三方序列化器,因此可用于客户端、专用服务器和测试。

场景视图走向成熟
Turian 2.1.0 至 3.0.0,相关 Issue:#82, #68
使用编辑器的大部分时间都花在场景视图中,因此 10 月 Studio 的工作也主要集中在这里。
辅助操纵器。 变换手柄经过重做:更粗的箭头、实心缩放方块、填充的双轴手柄,以及面向摄像机的旋转圆弧。悬停时高亮,拖动时显示已旋转的角度。旋转支持世界轴和局部轴,并带有角度吸附。组合工具可同时显示移动、旋转和缩放手柄。手柄始终显示在场景几何体前方,因此即使物体埋在墙里,也能移动它。
朝向。 角落里有一个紧凑的朝向控件:带轴标签的小立方体。点击某个轴即可沿该轴观察,点击中心则在透视和正交投影间切换,同时保持取景。参考网格现在能在摄像机处正确裁剪,默认位于 XZ 平面,并以彩色显示原点坐标轴。设置页面可以调整网格间距、强调程度和不透明度。

导航。 按住鼠标右键控制视角时,地平线保持水平,不会翻转倒置。方向键通过可编辑的连续输入快捷键移动摄像机,Shift 用于加速。Scene 面板把工具、坐标空间和吸附设置集中在紧凑的图标工具栏中,Cap FPS 设置则能减少 Studio 空闲时给笔记本带来的发热。
多对象编辑。 层级面板和 Assets 面板支持 Ctrl+单击、Shift+单击范围选择及全选,场景视图支持框选。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 当前的设计系统。
底层外观分为四项独立选择:外观样式(每个控件的形状)、颜色主题、图标集和字体。主题采用 .pss 样式表,这是 Guinevere 对 CSS 的实现方式,编辑时支持实时重载。图标集提供 Mono 和 Color 两种版本。
外观样式和主题都是 brick,任何人都可以制作和分享。turian-cli 可以在命令行中把主题打包成 brick。Bricks 面板已移入 Gaya,并区分 Studio 自身的 brick 和项目的 brick。首批两个额外样式带来了一点怀旧感:Windows XP 和 Windows 9x,安装方式与其他 brick 相同。
游戏内 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 提交或压缩包校验和锁定的源码。

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 基础元素。
- 细节改进。 由可替换部件组成的颜色选择器、可搜索的多选下拉框、支持标志位的枚举编辑器,以及显示停靠落点的标签页。

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