主题
自动挂载模型表现
自动挂载模型表现用于把一个额外模型跟随显示在已有单位或物品持有者身上,例如手持物、背部装饰、披风、盾牌、头饰或临时外观覆盖。本文介绍 Actor 上层:服务端仍负责物品、属性、技能和状态等玩法逻辑,客户端按数编配置自动创建和销毁可见模型。
“附加模型”是被挂载的外观模型,“自动挂载模型表现”是管理这种模型的上层功能。 GameGraph 附加模型挂载是此功能的下钻接口,复用相同的挂载、骨骼跟随与可选宿主网格裁剪机制。上层通过 GameDataActorAdditionModel + ActorArray 自动维护生命周期;下钻后通过具体 AnimatedModelComponent.AttachAdditionModel(...) 挂载,由调用方保存句柄并解除。ActorAdditionModel 的归属与上层配置方式保持不变。
> 对外统一称“附加模型”;Actor 路线可称“自动挂载模型表现”或“附加模型 Actor”,GameGraph 路线可称“附加模型挂载”或“自动挂载模型表现的下钻接口”。
什么时候用
| 需求 | 推荐方式 |
|---|---|
| 单位出生时一直带着某个外观 | 在 GameDataUnit.ActorArray 中配置附加模型 Actor |
| 物品被携带或装备时给主人显示外观 | 在 GameDataItemMod.Modifications[ItemSlotType.*].ActorArray 中配置附加模型 Actor |
| Buff、技能或效果持续期间显示额外外观 | 在对应 ActorArray 中配置附加模型 Actor |
| 直接 GameGraph 角色、换装预览或需要代码控制挂载时机 | 使用下钻接口,显式选择主骨骼宿主并管理挂载句柄 |
| 只播放一次的命中特效、音效或提示圈 | 使用 transient Actor / PlayTransient(...) |
| 需要服务端权威碰撞、判定或同步节点 | 使用 Unit、物理或服务端 GameGraph 发布图,不要把表现 Actor 当逻辑对象 |
核心数据
附加模型使用 GameDataActorAdditionModel 定义。通常只需要填 Model。这条自动挂载路径不需要、也不能通过数编填写挂接槽位;即使填写 Socket 也不会改变挂载结果。对齐关系主要由美术资源本身决定。
| 字段 | 说明 |
|---|---|
Model | 要显示的模型资源,指向一个 GameDataModel |
Socket | 不需要填写;在自动挂载模型表现中填写也无效 |
Offset / Rotation / Scale | 不要作为此功能的对齐方案;自动挂载模型表现应通过资源制作保证位置、朝向和比例 |
InheritRotation / InheritScale | 不要作为此功能的对齐方案 |
CullHostMesh | 公开配置字段。附加模型会覆盖宿主主体时可设为 true,用于减少宿主与附加模型重叠造成的穿插或重叠渲染 |
CullHostMesh 只解决“附加模型覆盖宿主外观”的显示问题。普通手持物、背饰、挂件通常保持 false;只有整套外观、披风/外套类模型明显遮住宿主时才考虑开启。
美术资源制作约定
自动挂载模型表现的关键在资源本身:程序侧只决定“引用哪个模型、什么时候显示”,不负责指定手、背、头等挂接槽位。美术交付的模型 / prefab 应该是按目标宿主骨架制作的表现资源,而不是只带局部偏移的普通静态模型。
交付前建议确认:
- 使用目标宿主的原始骨架制作要自动挂载的模型表现,例如手持物、衣服、披风或背饰。
- 在宿主 Bind Pose 下把模型放到正确位置。
- 把模型蒙皮到宿主骨骼:刚性手持物通常对手骨或表现用骨骼做 100% 权重;衣服、披风等按身体骨骼正常蒙皮。
- 可以只保留实际使用的骨骼及必要父骨骼,但骨骼名称必须和宿主完全一致,避免 DCC 工具自动添加命名空间或改名。
- Bind Pose、坐标轴、单位和骨骼初始变换必须与宿主一致,否则可能出现漂移、变形,甚至影响宿主蒙皮。
- 导出带 Skin 和骨骼的 FBX,不要导成纯静态模型;通常不需要动画。
- 如果同一个外观要适配不同体型、职业或骨架,优先分别制作资源,或在数编中拆成不同
GameDataActorAdditionModel,不要依赖数编里的Socket字段切换。 - 如果附加模型会大面积覆盖宿主,例如外套、披风、整套替换外观,需要和程序确认是否开启
CullHostMesh,并在实际角色上预览穿插和遮挡效果。 - 如果只是手持物、背饰、挂件、头饰,通常不需要开启
CullHostMesh。
程序侧接入时,只把美术交付的资源路径登记为 GameDataModel.Asset,再让 GameDataActorAdditionModel.Model 指向它。
物品携带/装备时自动显示
物品外观通常挂在 GameDataItemMod.Modifications 的槽位配置里。客户端会根据物品当前槽位和启用状态自动维护表现:
- 物品进入携带或装备槽位。
- 框架读取该槽位
ItemUnitModificationData.ActorArray。 - 客户端在物品持有者身上创建这些 Actor。
- 物品移出、丢弃、失效或换槽位时,客户端销毁旧的附属 Actor。
示例:
csharp
using GameCore.ActorSystem.Data;
using GameCore.Container;
using GameCore.ItemSystem.Data;
using GameCore.ResourceType.Data;
using GameData;
public static class MyItemVisuals
{
public static readonly GameLink<GameDataModel, GameDataModel> HandHeldLanternModel =
new("$GameEntry.Models.HandHeldLantern"u8);
public static readonly GameLink<GameDataActor, GameDataActorAdditionModel> HandHeldLanternActor =
new("$GameEntry.Actors.HandHeldLantern"u8);
public static readonly GameLink<GameDataItem, GameDataItemMod> LanternItem =
new("$GameEntry.Items.Lantern"u8);
public static void Register()
{
_ = new GameDataModel(HandHeldLanternModel)
{
Asset = "deco/example/lantern/model.prefab"u8,
};
_ = new GameDataActorAdditionModel(HandHeldLanternActor)
{
Model = HandHeldLanternModel,
// 无需填写挂接槽位;填写 Socket 也不会生效。
// 位置、朝向和骨骼跟随由美术资源本身决定。
CullHostMesh = false,
};
_ = new GameDataItemMod(LanternItem)
{
DisplayName = "提灯",
Unit = MyItemUnits.LanternOnGround,
Modifications = new()
{
[ItemSlotType.Equip] = new()
{
ActorArray = [HandHeldLanternActor],
},
},
StackStart = 1,
StackMax = 1,
};
}
}如果同一个物品在不同槽位需要不同表现,可以为不同 ItemSlotType 配不同 ActorArray。框架会按当前槽位重建表现。
单位出生时自带外观
单位也可以直接通过 GameDataUnit.ActorArray 配置附加模型。适合出生就固定存在的外观,例如默认背饰、宠物跟随挂点、角色身上的额外部件。
csharp
_ = new GameDataUnit(MyUnits.Guard)
{
Model = MyModels.GuardBody,
ActorArray = [MyActors.GuardBackBanner],
};这种表现跟随单位默认 ActorScope 生命周期。它不是物品驱动的,不会因为背包槽位变化自动切换。
与地面物品外观的区别
GameDataItem.Unit 决定物品掉在场景中时作为“地面物品”的单位表现;GameDataItemQuality.ActorArray 可以给地面物品加品质光效。 GameDataItemMod.Modifications[slot].ActorArray 决定物品被携带或装备后显示在持有者身上的表现。
两者不要混用:
| 场景 | 配置位置 |
|---|---|
| 地上掉落物自己的模型、碰撞、拾取范围 | GameDataItem.Unit 指向的 GameDataUnit |
| 地上掉落物的品质光效 | GameDataItemQuality.ActorArray |
| 物品在持有者身上的模型或光效 | GameDataItemMod.Modifications[slot].ActorArray |
常见问题
| 症状 | 常见原因 | 处理 |
|---|---|---|
| 装备后没有显示模型 | ActorArray 没配在当前槽位,或物品未启用 | 检查 Modifications[ItemSlotType.*].ActorArray、需求条件和当前槽位 |
| 模型显示在脚下或原点 | 资源不是按宿主骨架制作的自动挂载模型,或导成了纯静态模型 | 不要尝试通过 Socket 修复;重新检查 Skin、骨骼、Bind Pose 和导出设置 |
| 不同角色上位置差异很大 | 资源只适配了某一种体型或骨架 | 为不同体型/职业准备不同附加模型资源或不同 Actor 配置 |
| 模型朝向或位置不对 | Bind Pose、坐标轴、单位、骨骼初始变换或蒙皮权重不匹配 | 回到美术资源检查;不要把 Offset / Rotation / Scale 当成主要对齐方案 |
| 宿主和附加模型严重重叠 | 附加模型覆盖了宿主主体 | 对该附加模型设置 CullHostMesh = true,并在实际机型上检查显示效果 |
| 服务端想读取附加模型位置做判定 | 表现 Actor 不是玩法对象 | 用 Unit、ScenePoint、碰撞体或服务端 GameGraph 节点做逻辑判定 |
| 切换装备后旧模型还在 | 运行时或 SDK 版本不匹配,或自定义代码绕过了物品槽位生命周期 | 优先走背包/物品槽位 API;排查客户端日志和物品槽位同步 |