Skip to content

服务端预烘焙模型碰撞 ​

系统文档:docs/systems/ServerPrebakedModelCollision.md。物理分层参考 docs/systems/PhysicsSystem.md。

先判断是否该用 ​

只在对象运行时才生成,并且其复杂几何无法用少量基本碰撞形状合理表达时使用。典型需求是动态建造房屋、运行时楼梯、可受力装饰物或多部件动态物体。

不适合:

  • 地形编辑器中的静态地形、楼梯、建筑和装饰:场景生成会自动烘焙到地形 / 场景物理数据。
  • 角色主碰撞:使用胶囊体;官方物理角色控制器已经包含。
  • 普通运行时刚体:优先盒、球、胶囊、圆柱等基本几何体或少量组合。
  • 客户端本地物理:客户端可直接加载模型 / Prefab 并据此生成碰撞。

模式选型:

模式使用条件
StaticTriangleMesh静态复杂几何,Mass = 0
ConvexHull合并整个资产为一个凸包,适合简单动态刚体
CompoundConvex每个独立 StaticModel 部件一个凸包,适合多部件动态刚体
BoundsBox / BoundsCapsule当前不支持;简单对象直接使用基础碰撞形状

CompoundConvex 不自动分解单个凹网格。凸包模式的 MaxHullVertices 合法范围为 4 至 4096。

固定架构 ​

text
客户端/编辑器资源链                    服务端运行时
GameDataModel.CollisionBake            GameLink<GameDataModel>
  -> Client-Resource                     -> HashCodeLocal
  -> ref/model_collision.json            -> block/model_collision/<hash>.bin
  -> 编辑器烘焙 .bin + .roles.dat        -> local 物理实例

原始 .mdl / .prefab 不上传服务端。运行时不读 registry 或 descriptor JSON。客户端不需要这条管线,因为它可以直接加载模型 / Prefab 并生成本地碰撞。

接入配方 ​

1. 配置 GameDataModel ​

csharp
private static readonly GameLink<GameDataModel, GameDataModel> Model =
    new("$GameEntry.Models.DynamicStair");

_ = new GameDataModel(Model)
{
    Asset = "deco/buildsystem/sm_buildsystem_stairs_f/model.prefab",
    CollisionBake = new()
    {
        Enabled = true,
        Mode = ModelCollisionBakeMode.StaticTriangleMesh,
    },
};

配置必须能被静态分析。不要运行时拼模型路径或动态构造 CollisionBake。

2. 生成产物 ​

运行编辑器“统计资源并生成”,或 MCP:

json
{ "tool": "resource_statistics_generate_animations", "arguments": {} }

必须检查:

text
ref/model_collision.json
block/model_collision/<hash>.bin
block/model_collision/<hash>.roles.dat

3. 服务端创建 ​

csharp
#if SERVER
var result = PrebakedModelCollision.ApplyPrebakedCollision(
    node,
    Model,
    new PrebakedCollisionOptions
    {
        Mass = 0f,
        CollisionLayer = 4,
        CollisionMask = uint.MaxValue,
    });

if (result != PrebakedModelCollisionApplyResult.Success)
{
    node.Destroy();
    Game.Logger.LogWarning(
        "Prebaked collision creation failed: {Result}",
        result);
}
#endif

API 仅存在于服务端。不要写 bool helper,也不要忽略枚举结果。

动态对象把模式改为 ConvexHull 或 CompoundConvex,并给 Mass 设置正值。凸包运行时会把 local RigidBody 和 CollisionShape 直接附着到传入的 Node;该 Node 应同时作为 replicated transform 和客户端 local Prefab 的父节点,且调用前不能已有物理组件。

4. 客户端表现分层 ​

碰撞创建不会自动显示模型。需要可见对象时:

  • 服务端复制语义 Node 或玩法状态。
  • 碰撞保持服务端 local。
  • 客户端从 Player.LocalPlayer.Scene.GetSceneGraph() 观察当前 SCE 场景节点。
  • 客户端识别 SyncVar 后,直接加载 Prefab 并挂 local 表现。

不要扫描 GetRegisteredSceneGraphs() 查找当前 SCE 场景。

结果处理 ​

结果下一步
Success继续创建业务对象
ModelDataUnavailable检查模型注册和 id
BakeConfigurationMissing / BakeDisabled检查 CollisionBake
UnsupportedBakeMode检查是否使用保留模式,以及 SDK / 运行时是否匹配
InvalidParentNode检查 Node 生命周期
CollisionArtifactOpenFailed检查生成、文件名和 server 发布过滤
InvalidCollisionArtifact重新烘焙并确保 SDK/运行时版本一致
PhysicsInstanceCreationFailed检查场景物理上下文和参数
UnknownNativeResult检查 SDK 与运行时是否错版
InvalidBakeConfiguration检查 MaxHullVertices 等烘焙参数
DynamicTriangleMeshNotSupported静态网格保持 Mass = 0;动态对象改用凸包模式

排错顺序 ​

  1. GameDataModel.CollisionBake 是否存在且 Enabled = true。
  2. ref/model_collision.json 是否包含该模型及正确 modelLinkHashLocal。
  3. block/model_collision/<hash>.bin 与 .roles.dat 是否成对存在。
  4. 正式发布是否把 block/model_collision 分发到 server。
  5. 调用是否位于 #if SERVER,结果枚举具体是什么。
  6. Node 是否仍有效,所在 SCE 场景是否已有物理世界,且目标 Node 是否已存在物理组件。

硬性约束 ​

  • 不在服务端加载原始模型或 Prefab。
  • 不在客户端调用预烘焙碰撞运行时 API。
  • 不为静态三角网格设置正质量。
  • 不把碰撞加载失败静默降级为无碰撞对象。
  • 不把 ref/model_collision.json 或额外 descriptor 当运行时索引。
  • 不期待 CompoundConvex 自动分解单个凹网格。

相关 Skill 路由 ​

  • 复杂模型烘焙配置、产物和结果枚举:继续使用本 Skill。
  • replicated Node、local RigidBody、发布图和客户端 transform 表现:使用 server-authoritative-3d-physics。
  • 玩家 / Unit 的 KCC、移动与跳跃:使用 3d-unit-game;不要把角色主碰撞改成预烘焙模型碰撞。
  • 查找有效 Prefab / 模型路径:先使用 sce-resource-picker。
  • 资源生成、编译、运行日志、截图和实际碰撞回归:最后使用 sce-preview-verify 完成验收。