◆ 持续开发中 · 当前为非稳定版本,如需抢先体验请通过 GitHub Issues 联系作者

通用属性 Attr

框架级、per-entity 的基础属性能力:一个属性清单作单一真源,代码生成类型安全的 AttrKey 枚举 + 读写 AttrBag,预设集自动初始化实体。

命名空间 HYC.Framework.Attr。仅 ECS;框架不认识 "HP/ATK" 等具体属性名,也不内置公式 / buff。

◈
实现状态:代码已实现(Runtime/Attr/ + Editor/Attr/),但沙箱禁 csc、尚未在 Unity 中编译验证。本文档即当前实现的事实说明,供评审;评审通过后提交。

设计目标

方向解决什么
单一属性清单属性定义散落各处、易漂移 → 一个 AttributeCatalog 作单一真源
内置代码生成器手写 key 字符串易拼错 → 由目录生成枚举 + 映射 + 类型安全 bag
预设 + 自动初始化每个实体手写初始化循环 → AttributeSetAsset + AttributeInitSystem 自动灌值
编辑器 + 校验没有校验/编辑器 → AttributeValidator + 两个 CustomEditor

用法

1. 定义属性清单

Project 右键 HYC Framework / Attributes / Attribute Catalog 建资产,加条目(key / type / defaultRaw / minRaw / maxRaw)。这是唯一需要手填的地方。也可从菜单 HYC Framework / Attributes / Attribute Catalog Editor 打开独立面板编辑——窗口把 entries 画成类型感知表格(Float 显示小数并自动 bits↔double 转换,Bool 只显示勾选框隐藏 min/max,整数显示整数),行首 ≡ 拖拽重排、行尾 ✕ 删除,底部「+ 添加属性」。

窗口顶部有两个分页:属性目录(上面这张表)与属性组(组装 AttributeSetAsset)。

2.(可选)做预设集

建 HYC Framework / Attributes / Attribute Set,绑定 catalog,勾选「这个实体要哪些属性」;保存时 OnValidate 自动把所选属性烘成 AttributeValue 列表。

3. 生成访问器

在 Catalog 编辑器点「生成属性访问器」,或菜单 HYC Framework / Attributes / Generate Accessor,选 catalog → 向导生成到 Assets/HYC/Generated/Attr/(生成代码统一总根下):

  • <prefix>AttrKey.cs:枚举(序数 = 目录顺序)
  • <prefix>AttrKeyMap.cs:static ulong[] Hash(= AttributeKey.Hash(key))+ AttributeType[] Type
  • <prefix>AttrBag.cs:实现 IAttrBag,暴露 GetInt(<prefix>AttrKey.ATK) 等友好重载

4. 代码里用枚举(零手写 key)

// em, e 来自你的 World
var bag = new GameAttrBag(em, e);
int atk = bag.GetInt(GameAttrKey.ATK);     // IntelliSense + 拼错编译期报错
bag.SetInt(GameAttrKey.ATK, atk + 5);

<prefix> 默认 Game,命名空间由「生成代码命名空间 root」派生为 {root}.Attr,可在向导里改。

5. 自动初始化

实体挂 AttributeSetRef { asset = 你的Set } → AttributeInitSystem 进 Play 后自动 AddBuffer<AttributeValue> 并灌入预设值;或手动 AttributeInitializer.InitFromSet(em, e, set)。

6. 校验

Catalog 编辑器点「校验目录」跑 AttributeValidator,命中空 key / 重名 / 非法标识符 / min>max / 默认值越界 / Float bits 为 NaN·Inf 时给出 AttributeIssue。

关键类

类型职责
AttributeTypeenum AttributeType : byte:Int / Float / Long / Bool,决定 raw 解释方式
AttributeValuestruct 袋元素:keyHash(FNV-1a) + type + raw(long,Float 存 IEEE754 bits)
AttributeKeyHash(string) → ulong(FNV-1a 64 位)
IAttrBagulong-key 类型安全读写契约(Job/Burst 外)
AttributeStore主线程 EntityManager 读写,按 keyHash 线性查找
AttributeInitializerInit<T> / InitFromSet:把默认值钳制后灌入 buffer
AttrBagreadonly struct 包 (EntityManager, Entity),转调 AttributeStore
AttributeCatalog单一真源;ToValue() 把条目烘成 AttributeValue
AttributeSetAssetOnValidate → Rebuild() 把所选属性烘成 List<AttributeValue>
AttributeInitSystempartial class SystemBase,每帧查「有 Ref 无 buffer」实体→ AddBuffer+灌值

取舍

  • 枚举是目录的快照:目录增删属性后必须重新 Generate,否则枚举与运行时袋数据序数错位。keyHash 按名算(非序数),同 key 改名不影响旧袋;但枚举序数变了要重生成。
  • 运行期 Set 不钳制:袋是运行时真相,配置边界只约束默认值;若需运行期 clamp,由使用方自己包一层。
  • AttributeSetRef 是托管 IComponentData(class):不走 Burst/Job,适合低频初始化,不进逐帧热路径。
  • 完全生成、无 #region USER:生成文件不要手改,改目录后重生成即可整体覆盖。
  • keyHash 命中失败是静默的:Get 返调用方默认值、Set 为 no-op(袋即「声明过的属性集合」,不自动扩容)。

坑(实现者须知)

  • 改目录后必须重生成:枚举版 AttrBag / AttrKeyMap 是生成物的硬编码副本,目录变了不同步会串号。
  • Float 存 bits:AttributeValue.raw 对 Float 走 BitConverter.DoubleToInt64Bits/Int64BitsToDouble,校验器对 Float 的 defaultRaw/minRaw/maxRaw 会按 double 解释,NaN/Inf 会被 AttributeValidator 拦下。
  • AttributeInitSystem 的查询用 CreateEntityQuery(typeof(AttributeSetRef), ComponentType.Exclude<AttributeValue>()),只处理「还没初始化」的实体(幂等)。
  • ConfigRowRef 已从 Talent 迁至 Runtime/ConfigRowRef.cs(HYC.Framework.Config),消除 Attr → Talent 反向依赖。