Files
esengine/docs/entity-guide.md
2025-06-08 10:20:51 +08:00

465 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 实体使用指南
本指南详细介绍 ECS Framework 中实体Entity的所有功能和使用方法。
## 实体概述
实体Entity是 ECS 架构中的核心概念之一,它作为组件的容器存在。实体本身不包含游戏逻辑,所有功能都通过添加不同的组件来实现。
### 实体的特点
- **轻量级容器**:实体只是组件的载体,不包含具体的游戏逻辑
- **唯一标识**每个实体都有唯一的ID和名称
- **层次结构**:支持父子关系,可以构建复杂的实体层次
- **高性能查询**:基于位掩码的组件查询系统
- **生命周期管理**:完整的创建、更新、销毁流程
## 创建实体
### 基本创建方式
```typescript
import { Scene } from '@esengine/ecs-framework';
// 通过场景创建实体
const scene = new Scene();
const entity = scene.createEntity("Player");
console.log(entity.name); // "Player"
console.log(entity.id); // 唯一的数字ID
```
### 使用流式API创建
```typescript
import { Core } from '@esengine/ecs-framework';
// 使用ECS流式API
const entity = Core.ecsAPI
?.entity("Enemy")
.withComponent(new PositionComponent(100, 200))
.withComponent(new HealthComponent(50))
.withTag(2)
.build();
```
## 实体属性
### 基本属性
```typescript
// 实体名称 - 用于调试和标识
entity.name = "Player";
// 实体ID - 只读,场景内唯一
console.log(entity.id); // 例如: 1
// 标签 - 用于分类和快速查询
entity.tag = 1; // 玩家标签
entity.tag = 2; // 敌人标签
// 更新顺序 - 控制实体在系统中的处理优先级
entity.updateOrder = 0; // 数值越小优先级越高
```
### 状态控制
```typescript
// 启用状态 - 控制实体是否参与更新和处理
entity.enabled = true; // 启用实体
entity.enabled = false; // 禁用实体
// 激活状态 - 控制实体及其子实体的活跃状态
entity.active = true; // 激活实体
entity.active = false; // 停用实体
// 检查层次结构中的激活状态
if (entity.activeInHierarchy) {
// 实体在整个层次结构中都是激活的
}
// 检查销毁状态
if (entity.isDestroyed) {
// 实体已被销毁
}
```
### 更新间隔
```typescript
// 控制实体更新频率
entity.updateInterval = 1; // 每帧更新
entity.updateInterval = 2; // 每2帧更新一次
entity.updateInterval = 5; // 每5帧更新一次
```
## 组件管理
### 添加组件
```typescript
// 创建并添加组件
const healthComponent = entity.addComponent(new HealthComponent(100));
// 使用工厂方法创建组件
const positionComponent = entity.createComponent(PositionComponent, 100, 200);
// 批量添加组件
const components = entity.addComponents([
new PositionComponent(0, 0),
new VelocityComponent(50, 0),
new HealthComponent(100)
]);
```
### 获取组件
```typescript
// 获取单个组件
const health = entity.getComponent(HealthComponent);
if (health) {
console.log(`当前生命值: ${health.currentHealth}`);
}
// 获取或创建组件(如果不存在则创建)
const position = entity.getOrCreateComponent(PositionComponent, 0, 0);
// 获取多个同类型组件(如果组件可以重复添加)
const allHealthComponents = entity.getComponents(HealthComponent);
```
### 检查组件
```typescript
// 检查是否拥有指定组件
if (entity.hasComponent(HealthComponent)) {
// 实体拥有生命值组件
}
// 检查组件掩码(高性能)
const mask = entity.componentMask;
console.log(`组件掩码: ${mask.toString(2)}`);
```
### 移除组件
```typescript
// 移除指定组件实例
const healthComponent = entity.getComponent(HealthComponent);
if (healthComponent) {
entity.removeComponent(healthComponent);
}
// 按类型移除组件
const removedHealth = entity.removeComponentByType(HealthComponent);
// 批量移除组件
const removedComponents = entity.removeComponentsByTypes([
HealthComponent,
VelocityComponent
]);
// 移除所有组件
entity.removeAllComponents();
```
## 层次结构管理
### 父子关系
```typescript
// 创建父子实体
const player = scene.createEntity("Player");
const weapon = scene.createEntity("Weapon");
const shield = scene.createEntity("Shield");
// 添加子实体
player.addChild(weapon);
player.addChild(shield);
// 获取父实体
console.log(weapon.parent === player); // true
// 获取所有子实体
const children = player.children;
console.log(children.length); // 2
// 获取子实体数量
console.log(player.childCount); // 2
```
### 查找子实体
```typescript
// 按名称查找子实体
const weapon = player.findChild("Weapon");
// 递归查找子实体
const deepChild = player.findChild("DeepChild", true);
// 按标签查找子实体
const enemies = player.findChildrenByTag(2); // 查找所有敌人标签的子实体
// 递归按标签查找
const allEnemies = player.findChildrenByTag(2, true);
```
### 层次结构操作
```typescript
// 移除子实体
const removed = player.removeChild(weapon);
// 移除所有子实体
player.removeAllChildren();
// 获取根实体
const root = weapon.getRoot();
// 检查祖先关系
if (player.isAncestorOf(weapon)) {
// player 是 weapon 的祖先
}
// 检查后代关系
if (weapon.isDescendantOf(player)) {
// weapon 是 player 的后代
}
// 获取实体在层次结构中的深度
const depth = weapon.getDepth(); // 从根实体开始计算的深度
```
### 遍历子实体
```typescript
// 遍历直接子实体
player.forEachChild((child, index) => {
console.log(`子实体 ${index}: ${child.name}`);
});
// 递归遍历所有子实体
player.forEachChild((child, index) => {
console.log(`子实体 ${index}: ${child.name} (深度: ${child.getDepth()})`);
}, true);
```
## 实体生命周期
### 更新循环
```typescript
// 手动更新实体(通常由场景自动调用)
entity.update();
// 实体会自动调用所有组件的update方法
class MyComponent extends Component {
public update(): void {
// 组件更新逻辑
}
}
```
### 销毁实体
```typescript
// 销毁实体
entity.destroy();
// 检查是否已销毁
if (entity.isDestroyed) {
console.log("实体已被销毁");
}
// 销毁实体时会自动:
// 1. 移除所有组件
// 2. 从父实体中移除
// 3. 销毁所有子实体
// 4. 从场景中移除
```
## 性能优化
### 组件缓存
```typescript
// 预热组件缓存(提高后续访问性能)
entity.warmUpComponentCache();
// 清理组件缓存
entity.cleanupComponentCache();
// 获取缓存统计信息
const cacheStats = entity.getComponentCacheStats();
console.log(`缓存命中率: ${cacheStats.cacheStats.hitRate}`);
console.log(`组件访问统计:`, cacheStats.accessStats);
```
### 批量操作
```typescript
// 批量添加组件(比单个添加更高效)
const components = entity.addComponents([
new PositionComponent(0, 0),
new VelocityComponent(50, 0),
new HealthComponent(100)
]);
// 批量移除组件
const removed = entity.removeComponentsByTypes([
HealthComponent,
VelocityComponent
]);
```
## 调试和监控
### 调试信息
```typescript
// 获取详细的调试信息
const debugInfo = entity.getDebugInfo();
console.log("实体调试信息:", debugInfo);
// 调试信息包含:
// - 基本属性名称、ID、状态等
// - 组件信息(数量、类型、掩码等)
// - 层次结构信息(父子关系、深度等)
// - 性能统计(缓存命中率、访问统计等)
```
### 实体比较
```typescript
// 比较两个实体的优先级
const result = entity1.compareTo(entity2);
if (result < 0) {
// entity1 优先级更高
} else if (result > 0) {
// entity2 优先级更高
} else {
// 优先级相同
}
// 实体的字符串表示
console.log(entity.toString()); // "Entity[Player:1]"
```
## 最佳实践
### 1. 合理使用标签
```typescript
// 定义标签常量
const Tags = {
PLAYER: 1,
ENEMY: 2,
PROJECTILE: 3,
PICKUP: 4
} as const;
// 使用标签进行分类
player.tag = Tags.PLAYER;
enemy.tag = Tags.ENEMY;
```
### 2. 优化更新顺序
```typescript
// 设置合理的更新顺序
player.updateOrder = 0; // 玩家最先更新
enemy.updateOrder = 1; // 敌人其次
projectile.updateOrder = 2; // 投射物最后
```
### 3. 合理使用层次结构
```typescript
// 创建复合实体
const tank = scene.createEntity("Tank");
const turret = scene.createEntity("Turret");
const barrel = scene.createEntity("Barrel");
// 建立层次关系
tank.addChild(turret);
turret.addChild(barrel);
// 这样可以通过控制父实体来影响整个层次结构
tank.active = false; // 整个坦克都会被停用
```
### 4. 组件缓存优化
```typescript
// 对于频繁访问的组件,预热缓存
entity.warmUpComponentCache();
// 定期清理不常用的缓存
setInterval(() => {
entity.cleanupComponentCache();
}, 5000);
```
### 5. 避免内存泄漏
```typescript
// 确保正确销毁实体
if (entity.isDestroyed) {
return; // 避免操作已销毁的实体
}
// 在适当的时候销毁不需要的实体
if (enemy.getComponent(HealthComponent)?.isDead()) {
enemy.destroy();
}
```
## 常见问题
### Q: 实体可以在不同场景间移动吗?
A: 不可以。实体与场景紧密绑定,如果需要在场景间传递数据,应该序列化实体的组件数据,然后在新场景中重新创建。
### Q: 如何实现实体的位置、旋转、缩放?
A: 框架本身不提供这些属性,需要通过组件来实现:
```typescript
class TransformComponent extends Component {
public position = { x: 0, y: 0 };
public rotation = 0;
public scale = { x: 1, y: 1 };
}
const transform = entity.addComponent(new TransformComponent());
transform.position.x = 100;
transform.rotation = Math.PI / 4;
```
### Q: 实体的更新顺序如何影响性能?
A: 更新顺序主要影响游戏逻辑的执行顺序,对性能影响较小。但合理的更新顺序可以避免一些逻辑问题,比如确保输入处理在移动之前执行。
### Q: 如何处理大量实体的性能问题?
A:
1. 使用对象池重用实体
2. 合理使用组件缓存
3. 避免不必要的组件查询
4. 使用批量操作
5. 定期清理销毁的实体
```typescript
// 使用对象池
class EntityPool extends Pool<Entity> {
protected createObject(): Entity {
return scene.createEntity("PooledEntity");
}
protected resetObject(entity: Entity): void {
entity.removeAllComponents();
entity.active = true;
entity.enabled = true;
}
}
```