主题
GameData 与运行时对象的创建
使用 SCE Unit/技能体系时,GameData 保存定义,运行时对象保存本局状态。本文说明创建方法与自定义派生类型的接线;其他路线见游戏架构路线。
定义与实例的区别
| 定义 | 运行时对象 | 创建入口 |
|---|---|---|
GameDataUnit | Unit,派生自 Entity | CreateUnit(...),仅服务端 |
GameDataItem | Item,单位上的物品组件 | CreateItem(...) |
GameDataAbility | Ability,单位上的技能对象 | CreateAbility(...) |
Item 和 Ability 不继承 Entity。物品可以通过单位承载其场景表现与交互,技能依附所属单位;不要把这三个运行时类型都当作独立实体。
同一份 GameData 可以创建多个实例。修改某个单位的当前状态,应操作该实例;修改共享定义不能当作修改单个实例。
图形化项目的类型和模板定义优先使用触发编辑器、数据编辑器;手写 GameData 类型属于纯代码扩展路线,见项目结构。
使用现有定义创建单位
以下片段中的 unitLink 是项目已有的 IGameLink<GameDataUnit>,player、spawnPoint 和 facing 是本次创建参数。
csharp
#if SERVER
var unit = unitLink.Data?.CreateUnit(player, spawnPoint, facing);
if (unit is null)
{
Game.Logger.LogError("创建单位失败: {Link}", unitLink);
return;
}
#endifGameDataUnit.CreateUnit 会约束坐标、构造 Unit、按参数添加默认 AI,并触发 EventUnitCreate。结果同时写入 ScopeScript.LastCreatedUnit;创建失败时记录错误并返回 null。
Link.Data 也可能未找到定义。先区分“定义未注册”和“实例创建失败”,再检查初始化顺序、场景与具体错误日志。
自定义 Unit 为什么没有被创建出来
声明 Hero : Unit 不会改变现有 GameDataUnit.CreateUnit 的行为。基础实现明确构造 new Unit(...),因此:
csharp
#if SERVER
// 即使项目声明了 Hero,基础 GameDataUnit 创建的对象也不会自动变成 Hero。
var unit = unitLink.Data?.CreateUnit(player, spawnPoint, facing);
var hero = unit as Hero; // 若定义仍使用基础工厂,这里为 null。
#endif如果希望通过数据定义的 CreateUnit 入口创建派生对象,需要让实际使用的定义类型重写该方法,并构造派生对象。只改变返回值类型转换、定义名称或 Link 名称都不会完成这一步。
重写前核对基础创建流程中的坐标约束、CreationContext、默认 AI、LastCreatedUnit、异常处理和创建事件,按需要保留相应行为。直接复制一个只含 new Hero(...) 的简化工厂,可能遗漏这些步骤。
这项接线要求针对“通过数据工厂创建派生实例”的路径;普通单位配置、添加技能/Buff 或组合业务逻辑不要求先派生 Unit。服务端工厂重写也不能直接证明客户端复制得到相同派生类型,应另外验证复制路径。
物品与技能的创建入口
GameDataItem.CreateItem(ScenePoint, Player?)会先创建承载物品的单位,再调用对应的物品创建入口;承载单位定义无效或创建失败会抛出InvalidDataException。不要套用CreateUnit的“失败返回 null”约定。GameDataAbility.CreateAbility(Unit, Item?)创建单位上的技能对象,具体技能定义可以重写创建方法。自定义技能需核对当前基类构造与技能类型自己的初始化流程。- 若只是调整已有单位、物品或技能的配置,复用已有定义与模板即可。