文档及教程更新
This commit is contained in:
600
docs/use-cases.md
Normal file
600
docs/use-cases.md
Normal file
@@ -0,0 +1,600 @@
|
||||
# ECS框架使用场景示例
|
||||
|
||||
本文档展示ECS框架在不同类型游戏中的具体应用案例。
|
||||
|
||||
## 目录
|
||||
|
||||
1. [小型休闲游戏](#小型休闲游戏)
|
||||
2. [中型动作游戏](#中型动作游戏)
|
||||
3. [大型策略游戏](#大型策略游戏)
|
||||
4. [MMO游戏](#mmo游戏)
|
||||
|
||||
## 小型休闲游戏
|
||||
|
||||
### 场景:简单的飞机大战游戏
|
||||
|
||||
```typescript
|
||||
import {
|
||||
Scene,
|
||||
EntityManager,
|
||||
Entity,
|
||||
Component,
|
||||
EntitySystem,
|
||||
Matcher
|
||||
} from '@esengine/ecs-framework';
|
||||
|
||||
// 组件定义
|
||||
class PositionComponent extends Component {
|
||||
constructor(public x: number = 0, public y: number = 0) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
class VelocityComponent extends Component {
|
||||
constructor(public x: number = 0, public y: number = 0) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
class PlayerComponent extends Component {}
|
||||
class EnemyComponent extends Component {}
|
||||
class BulletComponent extends Component {}
|
||||
|
||||
// 游戏管理器
|
||||
class PlaneWarGame {
|
||||
private scene: Scene;
|
||||
private entityManager: EntityManager;
|
||||
|
||||
constructor() {
|
||||
this.scene = new Scene();
|
||||
this.entityManager = new EntityManager();
|
||||
this.setupGame();
|
||||
}
|
||||
|
||||
private setupGame(): void {
|
||||
// 创建玩家
|
||||
const player = this.entityManager.createEntity("Player");
|
||||
player.addComponent(new PositionComponent(400, 500));
|
||||
player.addComponent(new VelocityComponent(0, 0));
|
||||
player.addComponent(new PlayerComponent());
|
||||
player.tag = 1; // 玩家标签
|
||||
|
||||
// 创建敌人
|
||||
this.spawnEnemies(5);
|
||||
|
||||
// 添加系统
|
||||
this.scene.addEntityProcessor(new MovementSystem());
|
||||
this.scene.addEntityProcessor(new CollisionSystem());
|
||||
this.scene.addEntityProcessor(new CleanupSystem());
|
||||
}
|
||||
|
||||
private spawnEnemies(count: number): void {
|
||||
const enemies = this.scene.createEntities(count, "Enemy");
|
||||
enemies.forEach((enemy, index) => {
|
||||
enemy.addComponent(new PositionComponent(
|
||||
Math.random() * 800,
|
||||
-50
|
||||
));
|
||||
enemy.addComponent(new VelocityComponent(0, 100));
|
||||
enemy.addComponent(new EnemyComponent());
|
||||
enemy.tag = 2; // 敌人标签
|
||||
});
|
||||
}
|
||||
|
||||
public update(): void {
|
||||
this.scene.update();
|
||||
}
|
||||
}
|
||||
|
||||
// 移动系统
|
||||
class MovementSystem extends EntitySystem {
|
||||
constructor() {
|
||||
super(Matcher.empty().all(PositionComponent, VelocityComponent));
|
||||
}
|
||||
|
||||
protected process(entities: Entity[]): void {
|
||||
const movingEntities = this.scene.querySystem.queryAll(
|
||||
PositionComponent,
|
||||
VelocityComponent
|
||||
);
|
||||
|
||||
movingEntities.entities.forEach(entity => {
|
||||
const pos = entity.getComponent(PositionComponent);
|
||||
const vel = entity.getComponent(VelocityComponent);
|
||||
|
||||
pos.x += vel.x * Time.deltaTime;
|
||||
pos.y += vel.y * Time.deltaTime;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
## 中型动作游戏
|
||||
|
||||
### 场景:2D平台跳跃游戏
|
||||
|
||||
```typescript
|
||||
// 更复杂的组件
|
||||
class HealthComponent extends Component {
|
||||
constructor(
|
||||
public maxHealth: number = 100,
|
||||
public currentHealth: number = 100
|
||||
) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
class AnimationComponent extends Component {
|
||||
constructor(
|
||||
public currentAnimation: string = "idle",
|
||||
public frameIndex: number = 0,
|
||||
public frameTime: number = 0
|
||||
) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
class PhysicsComponent extends Component {
|
||||
constructor(
|
||||
public mass: number = 1,
|
||||
public friction: number = 0.8,
|
||||
public isGrounded: boolean = false
|
||||
) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
// 平台游戏管理器
|
||||
class PlatformGame {
|
||||
private scene: Scene;
|
||||
private entityManager: EntityManager;
|
||||
|
||||
constructor() {
|
||||
this.scene = new Scene();
|
||||
this.entityManager = new EntityManager();
|
||||
this.setupGame();
|
||||
}
|
||||
|
||||
private setupGame(): void {
|
||||
// 创建玩家
|
||||
this.createPlayer();
|
||||
|
||||
// 创建敌人
|
||||
this.createEnemies(10);
|
||||
|
||||
// 创建平台
|
||||
this.createPlatforms();
|
||||
|
||||
// 添加系统(按更新顺序)
|
||||
this.scene.addEntityProcessor(new InputSystem()).updateOrder = 10;
|
||||
this.scene.addEntityProcessor(new PhysicsSystem()).updateOrder = 20;
|
||||
this.scene.addEntityProcessor(new AnimationSystem()).updateOrder = 30;
|
||||
this.scene.addEntityProcessor(new CombatSystem()).updateOrder = 40;
|
||||
this.scene.addEntityProcessor(new RenderSystem()).updateOrder = 50;
|
||||
}
|
||||
|
||||
private createPlayer(): void {
|
||||
const player = this.entityManager.createEntity("Player");
|
||||
player.addComponent(new PositionComponent(100, 300));
|
||||
player.addComponent(new VelocityComponent(0, 0));
|
||||
player.addComponent(new HealthComponent(100));
|
||||
player.addComponent(new AnimationComponent("idle"));
|
||||
player.addComponent(new PhysicsComponent(1, 0.8));
|
||||
player.tag = 1;
|
||||
}
|
||||
|
||||
private createEnemies(count: number): void {
|
||||
const enemies = this.scene.createEntities(count, "Enemy");
|
||||
enemies.forEach((enemy, index) => {
|
||||
enemy.addComponent(new PositionComponent(
|
||||
200 + index * 100,
|
||||
300
|
||||
));
|
||||
enemy.addComponent(new VelocityComponent(0, 0));
|
||||
enemy.addComponent(new HealthComponent(50));
|
||||
enemy.addComponent(new AnimationComponent("patrol"));
|
||||
enemy.addComponent(new PhysicsComponent(0.8, 0.9));
|
||||
enemy.tag = 2;
|
||||
});
|
||||
}
|
||||
|
||||
private createPlatforms(): void {
|
||||
const platforms = this.scene.createEntities(5, "Platform");
|
||||
platforms.forEach((platform, index) => {
|
||||
platform.addComponent(new PositionComponent(
|
||||
index * 200,
|
||||
400 + Math.random() * 100
|
||||
));
|
||||
platform.tag = 3; // 平台标签
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
## 大型策略游戏
|
||||
|
||||
### 场景:即时战略游戏
|
||||
|
||||
```typescript
|
||||
// 策略游戏组件
|
||||
class UnitComponent extends Component {
|
||||
constructor(
|
||||
public unitType: string,
|
||||
public playerId: number,
|
||||
public level: number = 1
|
||||
) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
class AIComponent extends Component {
|
||||
constructor(
|
||||
public state: string = "idle",
|
||||
public target: Entity | null = null,
|
||||
public lastDecisionTime: number = 0
|
||||
) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
class ResourceComponent extends Component {
|
||||
constructor(
|
||||
public gold: number = 0,
|
||||
public wood: number = 0,
|
||||
public food: number = 0
|
||||
) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
// 策略游戏管理器
|
||||
class StrategyGame {
|
||||
private scene: Scene;
|
||||
private entityManager: EntityManager;
|
||||
private players: Map<number, Entity> = new Map();
|
||||
|
||||
constructor() {
|
||||
this.scene = new Scene();
|
||||
this.entityManager = new EntityManager();
|
||||
this.setupGame();
|
||||
}
|
||||
|
||||
private setupGame(): void {
|
||||
// 创建玩家
|
||||
this.createPlayers(4);
|
||||
|
||||
// 为每个玩家创建初始单位
|
||||
this.players.forEach((player, playerId) => {
|
||||
this.createInitialUnits(playerId, 10);
|
||||
});
|
||||
|
||||
// 添加系统
|
||||
this.scene.addEntityProcessor(new AISystem()).updateOrder = 10;
|
||||
this.scene.addEntityProcessor(new CombatSystem()).updateOrder = 20;
|
||||
this.scene.addEntityProcessor(new ResourceSystem()).updateOrder = 30;
|
||||
this.scene.addEntityProcessor(new UnitManagementSystem()).updateOrder = 40;
|
||||
}
|
||||
|
||||
private createPlayers(count: number): void {
|
||||
for (let i = 0; i < count; i++) {
|
||||
const player = this.entityManager.createEntity(`Player_${i}`);
|
||||
player.addComponent(new ResourceComponent(1000, 500, 100));
|
||||
player.tag = 10 + i; // 玩家标签从10开始
|
||||
this.players.set(i, player);
|
||||
}
|
||||
}
|
||||
|
||||
private createInitialUnits(playerId: number, count: number): void {
|
||||
const units = this.scene.createEntities(count, `Unit_${playerId}`);
|
||||
|
||||
units.forEach((unit, index) => {
|
||||
unit.addComponent(new PositionComponent(
|
||||
playerId * 200 + Math.random() * 100,
|
||||
playerId * 200 + Math.random() * 100
|
||||
));
|
||||
unit.addComponent(new UnitComponent("warrior", playerId));
|
||||
unit.addComponent(new HealthComponent(100));
|
||||
unit.addComponent(new AIComponent());
|
||||
unit.tag = 20 + playerId; // 单位标签
|
||||
});
|
||||
}
|
||||
|
||||
// 批量单位操作
|
||||
public createArmy(playerId: number, unitType: string, count: number): Entity[] {
|
||||
const units = this.scene.createEntities(count, `${unitType}_${playerId}`);
|
||||
|
||||
// 批量配置组件
|
||||
units.forEach(unit => {
|
||||
unit.addComponent(new UnitComponent(unitType, playerId));
|
||||
unit.addComponent(new HealthComponent(100));
|
||||
unit.addComponent(new PositionComponent(
|
||||
Math.random() * 1000,
|
||||
Math.random() * 1000
|
||||
));
|
||||
unit.tag = 20 + playerId;
|
||||
});
|
||||
|
||||
return units;
|
||||
}
|
||||
|
||||
// 查询玩家的所有单位
|
||||
public getPlayerUnits(playerId: number): Entity[] {
|
||||
return this.entityManager
|
||||
.query()
|
||||
.withAll(UnitComponent)
|
||||
.withTag(20 + playerId)
|
||||
.execute();
|
||||
}
|
||||
|
||||
// 查询特定类型的单位
|
||||
public getUnitsByType(unitType: string): Entity[] {
|
||||
return this.entityManager
|
||||
.query()
|
||||
.withAll(UnitComponent)
|
||||
.where(entity => {
|
||||
const unit = entity.getComponent(UnitComponent);
|
||||
return unit && unit.unitType === unitType;
|
||||
})
|
||||
.execute();
|
||||
}
|
||||
}
|
||||
|
||||
// AI系统
|
||||
class AISystem extends EntitySystem {
|
||||
constructor() {
|
||||
super(Matcher.empty().all(AIComponent, UnitComponent));
|
||||
}
|
||||
|
||||
protected process(entities: Entity[]): void {
|
||||
const aiUnits = this.entityManager
|
||||
.query()
|
||||
.withAll(AIComponent, UnitComponent)
|
||||
.execute();
|
||||
|
||||
aiUnits.forEach(unit => {
|
||||
this.processAI(unit);
|
||||
});
|
||||
}
|
||||
|
||||
private processAI(unit: Entity): void {
|
||||
const ai = unit.getComponent(AIComponent);
|
||||
const unitComp = unit.getComponent(UnitComponent);
|
||||
|
||||
if (!ai || !unitComp) return;
|
||||
|
||||
// 简单AI逻辑
|
||||
switch (ai.state) {
|
||||
case "idle":
|
||||
this.findTarget(unit);
|
||||
break;
|
||||
case "attack":
|
||||
this.attackTarget(unit);
|
||||
break;
|
||||
case "move":
|
||||
this.moveToTarget(unit);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private findTarget(unit: Entity): void {
|
||||
const unitComp = unit.getComponent(UnitComponent);
|
||||
if (!unitComp) return;
|
||||
|
||||
// 查找敌方单位
|
||||
const enemies = this.entityManager
|
||||
.query()
|
||||
.withAll(UnitComponent)
|
||||
.where(entity => {
|
||||
const enemyUnit = entity.getComponent(UnitComponent);
|
||||
return enemyUnit && enemyUnit.playerId !== unitComp.playerId;
|
||||
})
|
||||
.execute();
|
||||
|
||||
if (enemies.length > 0) {
|
||||
const ai = unit.getComponent(AIComponent);
|
||||
if (ai) {
|
||||
ai.target = enemies[0];
|
||||
ai.state = "attack";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private attackTarget(unit: Entity): void {
|
||||
// 攻击逻辑
|
||||
}
|
||||
|
||||
private moveToTarget(unit: Entity): void {
|
||||
// 移动逻辑
|
||||
}
|
||||
}
|
||||
|
||||
## MMO游戏
|
||||
|
||||
### 场景:大型多人在线游戏
|
||||
|
||||
```typescript
|
||||
// MMO特有组件
|
||||
class NetworkComponent extends Component {
|
||||
constructor(
|
||||
public playerId: string,
|
||||
public isLocal: boolean = false,
|
||||
public lastSyncTime: number = 0
|
||||
) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
class InventoryComponent extends Component {
|
||||
public items: Map<string, number> = new Map();
|
||||
|
||||
addItem(itemId: string, count: number): void {
|
||||
const current = this.items.get(itemId) || 0;
|
||||
this.items.set(itemId, current + count);
|
||||
}
|
||||
}
|
||||
|
||||
class GuildComponent extends Component {
|
||||
constructor(
|
||||
public guildId: string,
|
||||
public rank: string = "member"
|
||||
) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
// MMO游戏管理器
|
||||
class MMOGame {
|
||||
private scene: Scene;
|
||||
private entityManager: EntityManager;
|
||||
private localPlayerId: string;
|
||||
|
||||
constructor(localPlayerId: string) {
|
||||
this.scene = new Scene();
|
||||
this.entityManager = new EntityManager();
|
||||
this.localPlayerId = localPlayerId;
|
||||
this.setupGame();
|
||||
}
|
||||
|
||||
private setupGame(): void {
|
||||
// 添加MMO特有系统
|
||||
this.scene.addEntityProcessor(new NetworkSyncSystem()).updateOrder = 5;
|
||||
this.scene.addEntityProcessor(new PlayerSystem()).updateOrder = 10;
|
||||
this.scene.addEntityProcessor(new NPCSystem()).updateOrder = 15;
|
||||
this.scene.addEntityProcessor(new GuildSystem()).updateOrder = 20;
|
||||
this.scene.addEntityProcessor(new InventorySystem()).updateOrder = 25;
|
||||
}
|
||||
|
||||
// 创建玩家角色
|
||||
public createPlayer(playerId: string, isLocal: boolean = false): Entity {
|
||||
const player = this.entityManager.createEntity(`Player_${playerId}`);
|
||||
player.addComponent(new PositionComponent(0, 0));
|
||||
player.addComponent(new HealthComponent(1000));
|
||||
player.addComponent(new NetworkComponent(playerId, isLocal));
|
||||
player.addComponent(new InventoryComponent());
|
||||
player.tag = isLocal ? 1 : 2; // 本地玩家标签1,远程玩家标签2
|
||||
|
||||
return player;
|
||||
}
|
||||
|
||||
// 批量创建NPC
|
||||
public createNPCs(count: number): Entity[] {
|
||||
const npcs = this.scene.createEntities(count, "NPC");
|
||||
|
||||
npcs.forEach((npc, index) => {
|
||||
npc.addComponent(new PositionComponent(
|
||||
Math.random() * 2000,
|
||||
Math.random() * 2000
|
||||
));
|
||||
npc.addComponent(new HealthComponent(500));
|
||||
npc.addComponent(new AIComponent("patrol"));
|
||||
npc.tag = 3; // NPC标签
|
||||
});
|
||||
|
||||
return npcs;
|
||||
}
|
||||
|
||||
// 查询附近的玩家
|
||||
public getNearbyPlayers(centerX: number, centerY: number, radius: number): Entity[] {
|
||||
return this.entityManager
|
||||
.query()
|
||||
.withAll(PositionComponent, NetworkComponent)
|
||||
.where(entity => {
|
||||
const pos = entity.getComponent(PositionComponent);
|
||||
if (!pos) return false;
|
||||
|
||||
const distance = Math.sqrt(
|
||||
Math.pow(pos.x - centerX, 2) +
|
||||
Math.pow(pos.y - centerY, 2)
|
||||
);
|
||||
return distance <= radius;
|
||||
})
|
||||
.execute();
|
||||
}
|
||||
|
||||
// 查询公会成员
|
||||
public getGuildMembers(guildId: string): Entity[] {
|
||||
return this.entityManager
|
||||
.query()
|
||||
.withAll(GuildComponent, NetworkComponent)
|
||||
.where(entity => {
|
||||
const guild = entity.getComponent(GuildComponent);
|
||||
return guild && guild.guildId === guildId;
|
||||
})
|
||||
.execute();
|
||||
}
|
||||
|
||||
// 获取在线玩家统计
|
||||
public getOnlinePlayerStats(): any {
|
||||
const allPlayers = this.entityManager.getEntitiesWithComponent(NetworkComponent);
|
||||
const localPlayers = this.entityManager.getEntitiesByTag(1);
|
||||
const remotePlayers = this.entityManager.getEntitiesByTag(2);
|
||||
|
||||
return {
|
||||
total: allPlayers.length,
|
||||
local: localPlayers.length,
|
||||
remote: remotePlayers.length
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 网络同步系统
|
||||
class NetworkSyncSystem extends EntitySystem {
|
||||
constructor() {
|
||||
super(Matcher.empty().all(NetworkComponent));
|
||||
}
|
||||
|
||||
protected process(entities: Entity[]): void {
|
||||
const networkEntities = this.entityManager.getEntitiesWithComponent(NetworkComponent);
|
||||
|
||||
networkEntities.forEach(entity => {
|
||||
const network = entity.getComponent(NetworkComponent);
|
||||
if (!network || network.isLocal) return;
|
||||
|
||||
// 同步远程实体数据
|
||||
this.syncRemoteEntity(entity);
|
||||
});
|
||||
}
|
||||
|
||||
private syncRemoteEntity(entity: Entity): void {
|
||||
// 网络同步逻辑
|
||||
const network = entity.getComponent(NetworkComponent);
|
||||
if (!network) return;
|
||||
|
||||
const currentTime = Date.now();
|
||||
if (currentTime - network.lastSyncTime > 100) { // 100ms同步一次
|
||||
// 发送同步数据
|
||||
network.lastSyncTime = currentTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
## 性能优化建议
|
||||
|
||||
### 小型游戏(< 1000实体)
|
||||
- 使用简单的查询方法
|
||||
- 不需要复杂的优化
|
||||
- 重点关注代码可读性
|
||||
|
||||
### 中型游戏(1000-10000实体)
|
||||
- 使用标签查询优化性能
|
||||
- 实现基础的对象池
|
||||
- 缓存频繁查询的结果
|
||||
|
||||
### 大型游戏(10000-100000实体)
|
||||
- 使用时间分片处理大量实体
|
||||
- 实现空间分区优化邻近查询
|
||||
- 使用批量操作减少单次调用开销
|
||||
|
||||
### MMO游戏(100000+实体)
|
||||
- 实现分区管理,只处理相关区域的实体
|
||||
- 使用异步处理避免阻塞主线程
|
||||
- 实现智能缓存和预加载机制
|
||||
|
||||
## 总结
|
||||
|
||||
ECS框架的灵活性使其能够适应各种规模的游戏开发需求:
|
||||
|
||||
1. **小型游戏**:简单直接,快速开发
|
||||
2. **中型游戏**:平衡性能和复杂度
|
||||
3. **大型游戏**:充分利用优化特性
|
||||
4. **MMO游戏**:处理海量实体和复杂交互
|
||||
|
||||
选择合适的架构模式和优化策略,可以让ECS框架在不同场景下都发挥最佳性能。
|
||||
Reference in New Issue
Block a user