组件与存储
本文档涵盖 ECS 框架中的组件架构和存储系统,包括组件设计模式、存储优化策略、类型注册和性能考虑。组件作为纯数据容器,通过专门的存储系统进行高效管理。
有关实体生命周期和管理的信息,请参见《实体与实体管理》。有关系统如何处理组件的详细信息,请参见《系统与处理》。
组件设计基础
本 ECS 框架中的组件是轻量级数据容器,遵循组合优于继承的原则。每个组件代表游戏状态的特定方面,除了数据访问之外包含最少的逻辑。
组件基类结构
框架提供了一个基础 Component 类,所有组件都必须继承此类。该基类处理核心功能,如实体附加、生命周期管理和更新协调。
API 参考:
- Component - 组件基类
- ComponentStorage - 组件存储管理
组件生命周期与附加
组件通过明确定义的附加点和更新周期与实体生命周期紧密集成。框架通过存储系统自动管理组件-实体关系。
生命周期方法 | 用途 | 调用时机 |
---|---|---|
onAddedToEntity() | 初始化组件 | 组件添加到实体时 |
onRemovedFromEntity() | 清理资源 | 组件从实体移除时 |
onEnabled() | 激活行为 | 组件启用时 |
onDisabled() | 暂停行为 | 组件禁用时 |
update() | 每帧逻辑 | 实体更新周期 |
API 参考:
- Component.onAddedToEntity() - 组件添加回调
- Component.onRemovedFromEntity() - 组件移除回调
- Component.onEnabled() - 组件启用回调
- Component.onDisabled() - 组件禁用回调
- Component.update() - 组件更新方法
组件注册与类型管理
框架使用复杂的类型注册系统来管理组件类型、分配唯一标识符,并通过位掩码实现高效查询。
ComponentRegistry 架构
API 参考:
- ComponentRegistry - 组件注册管理器
- ComponentStorage - 组件存储系统
类型注册过程
ComponentRegistry 在首次遇到组件类型时自动为其分配唯一的位索引。这使得能够使用位运算进行快速组件查询。
注册过程支持最多 64 种组件类型,并包括自动缓存计算出的位掩码以进行性能优化。每个组件类型都会获得一个唯一的位位置,可以与其他类型组合以创建复合查询掩码。
API 参考:
- ComponentRegistry.register() - 注册组件类型
- ComponentRegistry.getBitIndex() - 获取位索引
- ComponentRegistry.createComponentMask() - 创建组件掩码
存储策略
框架提供两种针对不同用例优化的主要存储策略:标准实体-组件映射和结构数组(SoA)优化,适用于高性能场景。
标准组件存储
默认的 ComponentStorage<T> 使用混合方法,将实体到索引的映射与紧密数组相结合,以最小化内存碎片,同时保持快速访问模式。
存储特性 | 实现方式 | 性能优势 |
---|---|---|
实体映射 | Map<number, number> | O(1) 组件查找 |
索引管理 | 带空闲列表的紧密数组 | 最小内存碎片 |
组件数组 | (T | null)[] | 缓存友好的迭代 |
压缩 | 手动碎片整理 | 优化内存布局 |
存储系统通过空闲索引列表自动管理内存,该列表重用已移除组件的槽位,防止数组过度增长。
API 参考:
- ComponentStorage - 组件存储类
- ComponentStorage.addComponent() - 添加组件
- ComponentStorage.getComponent() - 获取组件
- ComponentStorage.removeComponent() - 移除组件
结构数组(SoA)优化
对于具有高迭代频率的组件,框架提供 SoA 存储,将组件字段分离到各个类型数组中,显著提高批量操作的缓存性能。
API 参考:
- enableSoA - 启用 SoA 优化
存储选择逻辑
ComponentStorageManager 根据组件类型注释自动选择适当的存储策略。使用 @EnableSoA 装饰的组件将获得优化的基于数组的存储,而其他组件则使用标准存储。
API 参考:
- ComponentStorageManager - 组件存储管理器
性能优化
组件访问模式
存储系统提供多种针对不同用例优化的访问模式,从单个组件访问到跨越数千个实体的批量操作。
访问模式 | 方法 | 用例 | 性能 |
---|---|---|---|
单个访问 | getComponent(entityId) | 单实体操作 | O(1) |
批量迭代 | forEach(callback) | 系统处理 | 缓存友好 |
紧密数组 | getDenseArray() | 批操作 | 最小间接性 |
字段数组 | getFieldArray(fieldName) | SoA 批量操作 | SIMD 就绪 |
框架自动处理稀疏到紧密的转换以进行迭代,同时通过实体 ID 映射保持快速的单个访问。
API 参考:
- ComponentStorage.getComponent() - 获取组件
- ComponentStorage.foreach() - 批量迭代
- ComponentStorage.getDenseArray() - 获取紧密数组
内存管理与压缩
组件存储包含内置的内存管理功能,具有自动槽位重用和手动压缩能力。系统跟踪碎片化级别并提供优化内存布局的工具。
API 参考:
- ComponentStorage.getStats() - 获取存储统计信息
组件装饰器与类型安全
ECSComponent 装饰器
框架提供了 @ECSComponent 装饰器,以确保即使在代码压缩后也能保持一致的类型命名。此装饰器对于在生产构建中可靠的组件识别至关重要。
@ECSComponent('Position')
class PositionComponent extends Component {
x: number = 0;
y: number = 0;
}
@ECSComponent('Health')
class HealthComponent extends Component {
@HighPrecision health: number = 100;
@Int32 maxHealth: number = 100;
}
装饰器系统使用 Symbol 键存储类型名称以避免命名冲突,并为未装饰的组件提供回退机制。
API 参考:
- ECSComponent - ECS组件装饰器
- COMPONENT_TYPE_NAME - 组件类型名称
类型安全与解析
类型系统通过复杂的类型解析机制在保持运行时灵活性的同时提供编译时安全性。
API 参考:
- ComponentType - 组件类型定义
- getComponentTypeName - 获取组件类型名称
- getComponentInstanceTypeName - 获取组件实例类型名称
压缩保护
装饰器系统专门解决 JavaScript 压缩挑战,其中类名变得不可靠。通过使用基于 Symbol 的存储和显式命名,框架在开发和生产环境中保持一致的行为。
环境 | 类名 | 装饰器名称 | 解析 |
---|---|---|---|
开发环境 | PositionComponent | 'Position' | 使用装饰器 |
生产环境(压缩后) | a | 'Position' | 使用装饰器 |
未装饰 | SomeComponent | undefined | 使用类名 |
API 参考:
- getComponentTypeName - 获取组件类型名称
- getComponentInstanceTypeName - 获取组件实例类型名称
位掩码组件标识
系统使用 64 位掩码来高效跟踪每个实体拥有的组件,实现快速的组件查询和系统匹配。
位掩码操作
BitMask64Utils 类为组件掩码提供优化的 64 位操作:
操作 | 用途 | 性能 |
---|---|---|
create(bitIndex) | 创建单位掩码 | O(1) |
hasAll(entityMask, queryMask) | 检查所有组件是否存在 | O(1) |
hasAny(entityMask, queryMask) | 检查任意组件是否存在 | O(1) |
orInPlace(target, source) | 原地组合掩码 | O(1) |
popCount(mask) | 计算设置的位数 | O(1) |
实体组件缓存
实体维护多层缓存以优化组件访问。
API参考:
内存优化特性
组件系统包含多种针对高性能场景的内存优化策略。
稀疏集合集成
ComponentSparseSet 提供了将组件存储与实体索引相结合的替代存储方法:
特性 | ComponentStorage | ComponentSparseSet |
---|---|---|
实体查找 | 基于Map的O(1) | 稀疏集合O(1) |
组件迭代 | 紧密数组 | 紧密数组+掩码 |
内存布局 | 分离式 | 集成式 |
多组件查询 | 外部实现 | 内置支持 |
对象池化
系统使用对象池来减少内存分配开销:
内存统计信息
两种存储实现都提供详细的内存使用统计:
指标 | 描述 | 用途 |
---|---|---|
totalSlots | 总分配存储 | 容量规划 |
usedSlots | 当前占用槽位 | 利用率跟踪 |
freeSlots | 可用槽位 | 碎片检测 |
fragmentation | 内存碎片率 | 性能监控 |
API参考: