600 lines
17 KiB
Markdown
600 lines
17 KiB
Markdown
# 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框架在不同场景下都发挥最佳性能。 |