From b06174926d223f2ae01a45b5f25ade8a9b7c7abd Mon Sep 17 00:00:00 2001 From: YHH <359807859@qq.com> Date: Tue, 10 Jun 2025 13:22:28 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=BF=AB=E9=80=9F=E5=85=A5?= =?UTF-8?q?=E9=97=A8=E6=8C=87=E5=8D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- docs/getting-started.md | 624 +++++++++++++++++++--------------------- 2 files changed, 295 insertions(+), 331 deletions(-) diff --git a/README.md b/README.md index fc0864c0..2ff1af7e 100644 --- a/README.md +++ b/README.md @@ -312,7 +312,7 @@ ecs-framework/ ### 🎯 新手入门 - **[📖 新手教程完整指南](docs/beginner-tutorials.md)** - 完整学习路径,从零开始 ⭐ **强烈推荐** -- [🚀 快速入门](docs/getting-started.md) - 详细的入门教程 +- **[🚀 快速入门](docs/getting-started.md)** - 详细的入门教程,包含Laya/Cocos/Node.js集成指南 ⭐ **平台集成必读** - [🧠 技术概念详解](docs/concepts-explained.md) - 通俗易懂的技术概念解释 ⭐ **推荐新手阅读** - [🎯 位掩码使用指南](docs/bitmask-guide.md) - 位掩码概念、原理和高级使用技巧 - [💡 使用场景示例](docs/use-cases.md) - 不同类型游戏的具体应用案例 diff --git a/docs/getting-started.md b/docs/getting-started.md index 2c4f8feb..421c70ff 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -2,138 +2,299 @@ 本指南将帮助您快速上手 ECS Framework,这是一个专业级的实体组件系统框架,采用现代化架构设计,专为高性能游戏开发打造。 -## 项目结构 - -``` -ecs-framework/ -├── src/ # 源代码 -│ ├── ECS/ # ECS核心系统 -│ │ ├── Core/ # 核心管理器 -│ │ ├── Systems/ # 系统类型 -│ │ ├── Utils/ # ECS工具类 -│ │ └── Components/# 组件类型 -│ ├── Types/ # TypeScript接口定义(精简版) -│ └── Utils/ # 通用工具类 -├── docs/ # 文档 -├── scripts/ # 构建脚本 -├── bin/ # 编译输出 -└── dist/ # 发布版本 -``` - -## 安装和使用 - -### NPM 安装 +## 安装 ```bash npm install @esengine/ecs-framework ``` -### 从源码构建 +## 平台集成 -```bash -# 克隆项目 -git clone https://github.com/esengine/ecs-framework.git - -# 进入项目目录 -cd ecs-framework - -# 安装依赖 -npm install - -# 编译TypeScript -npm run build - -# 或者使用监听模式开发 -npm run build:watch -``` - -## 基础设置 - -### 1. 导入框架 +### Laya引擎 ```typescript -// 导入核心类 -import { - Core, - Entity, - Component, - Scene, - EntitySystem, - EntityManager, - ComponentIndexManager, - ArchetypeSystem, - DirtyTrackingSystem -} from '@esengine/ecs-framework'; -``` +import { Scene as LayaScene } from "laya/display/Scene"; +import { Core, Scene as ECSScene, EntityManager, EntitySystem } from '@esengine/ecs-framework'; -### 2. 创建基础管理器 - -```typescript -class GameManager { - private core: Core; - private scene: Scene; +class LayaECSGame extends LayaScene { + private ecsScene: ECSScene; private entityManager: EntityManager; constructor() { - // 创建核心实例 - this.core = Core.create(true); + super(); - // 创建场景 - this.scene = new Scene(); - this.scene.name = "GameScene"; + // 初始化ECS框架 + Core.create(true); + this.ecsScene = new ECSScene(); + this.ecsScene.name = "LayaGameScene"; + Core.scene = this.ecsScene; - // 设置当前场景 - Core.scene = this.scene; - - // 初始化实体管理器 this.entityManager = new EntityManager(); + this.setupSystems(); } - public update(deltaTime: number): void { - // 更新场景 - this.scene.update(); + onAwake(): void { + super.onAwake(); + // 使用Laya的帧循环更新ECS + Laya.timer.frameLoop(1, this, this.updateECS); + } + + onDestroy(): void { + Laya.timer.clear(this, this.updateECS); + super.onDestroy(); + } + + private updateECS(): void { + this.ecsScene.update(); + } + + private setupSystems(): void { + this.ecsScene.addEntityProcessor(new LayaRenderSystem(this)); + this.ecsScene.addEntityProcessor(new MovementSystem()); + } +} + +// Laya渲染系统 +class LayaRenderSystem extends EntitySystem { + private layaScene: LayaScene; + + constructor(layaScene: LayaScene) { + super(Matcher.empty().all(PositionComponent, SpriteComponent)); + this.layaScene = layaScene; + } + + protected process(entities: Entity[]): void { + entities.forEach(entity => { + const pos = entity.getComponent(PositionComponent); + const sprite = entity.getComponent(SpriteComponent); + + if (pos && sprite && sprite.layaSprite) { + sprite.layaSprite.x = pos.x; + sprite.layaSprite.y = pos.y; + } + }); + } +} + +// 使用方法 +Laya.Scene.open("GameScene.scene", false, null, null, LayaECSGame); +``` + +### Cocos Creator + +```typescript +import { Component as CocosComponent, _decorator } from 'cc'; +import { Core, Scene as ECSScene, EntityManager, EntitySystem } from '@esengine/ecs-framework'; + +const { ccclass, property } = _decorator; + +@ccclass('ECSGameManager') +export class ECSGameManager extends CocosComponent { + private ecsScene: ECSScene; + private entityManager: EntityManager; + + start() { + // 初始化ECS框架 + Core.create(true); + this.ecsScene = new ECSScene(); + this.ecsScene.name = "CocosGameScene"; + Core.scene = this.ecsScene; - // 处理系统逻辑 - this.updateSystems(deltaTime); + this.entityManager = new EntityManager(); + this.setupSystems(); } - private updateSystems(deltaTime: number): void { - // 在这里添加您的系统更新逻辑 + update(deltaTime: number) { + // 在Cocos的update中更新ECS + this.ecsScene.update(); + } + + onDestroy() { + if (this.ecsScene) { + this.ecsScene.onDestroy(); + } + } + + private setupSystems(): void { + this.ecsScene.addEntityProcessor(new CocosRenderSystem(this.node)); + this.ecsScene.addEntityProcessor(new MovementSystem()); } - // 提供实体管理器访问 public getEntityManager(): EntityManager { return this.entityManager; } } -``` -### 3. 游戏循环 - -```typescript -const gameManager = new GameManager(); -let lastTime = performance.now(); - -function gameLoop() { - const currentTime = performance.now(); - const deltaTime = (currentTime - lastTime) / 1000; // 转换为秒 - lastTime = currentTime; +// Cocos渲染系统 +class CocosRenderSystem extends EntitySystem { + private rootNode: Node; - gameManager.update(deltaTime); + constructor(rootNode: Node) { + super(Matcher.empty().all(PositionComponent, SpriteComponent)); + this.rootNode = rootNode; + } - requestAnimationFrame(gameLoop); + protected process(entities: Entity[]): void { + entities.forEach(entity => { + const pos = entity.getComponent(PositionComponent); + const sprite = entity.getComponent(SpriteComponent); + + if (pos && sprite && sprite.cocosNode) { + sprite.cocosNode.setPosition(pos.x, pos.y); + } + }); + } } -// 启动游戏循环 -gameLoop(); +// 将ECSGameManager脚本挂载到场景根节点 ``` -## 创建实体和组件 - -### 1. 定义组件 +### Node.js后端 ```typescript -import { Component, ComponentPoolManager } from '@esengine/ecs-framework'; +import { Core, Scene, EntityManager, EntitySystem, Time } from '@esengine/ecs-framework'; + +class ServerGameManager { + private scene: Scene; + private entityManager: EntityManager; + private isRunning: boolean = false; + private tickRate: number = 60; // 60 TPS + private lastUpdate: number = Date.now(); + + constructor() { + Core.create(true); + this.scene = new Scene(); + this.scene.name = "ServerScene"; + Core.scene = this.scene; + + this.entityManager = new EntityManager(); + this.setupSystems(); + } + + public start(): void { + this.isRunning = true; + console.log(`游戏服务器启动,TPS: ${this.tickRate}`); + this.gameLoop(); + } + + public stop(): void { + this.isRunning = false; + } + + private gameLoop(): void { + if (!this.isRunning) return; + + const now = Date.now(); + const deltaTime = (now - this.lastUpdate) / 1000; + this.lastUpdate = now; + + Time.update(deltaTime); + this.scene.update(); + + const frameTime = 1000 / this.tickRate; + const processingTime = Date.now() - now; + const delay = Math.max(0, frameTime - processingTime); + + setTimeout(() => this.gameLoop(), delay); + } + + private setupSystems(): void { + this.scene.addEntityProcessor(new ServerMovementSystem()); + this.scene.addEntityProcessor(new NetworkSyncSystem()); + this.scene.addEntityProcessor(new AISystem()); + } + + public handlePlayerInput(playerId: string, input: any): void { + const playerEntity = this.findPlayerEntity(playerId); + if (playerEntity) { + const inputComp = playerEntity.getComponent(InputComponent); + if (inputComp) { + inputComp.updateInput(input); + } + } + } + + public getWorldState(): any { + const entities = this.entityManager + .query() + .withAll(PositionComponent, NetworkComponent) + .execute(); + + return entities.map(entity => ({ + id: entity.id, + position: entity.getComponent(PositionComponent), + })); + } + + private findPlayerEntity(playerId: string): Entity | null { + const players = this.entityManager + .query() + .withAll(PlayerComponent) + .execute(); + + return players.find(player => + player.getComponent(PlayerComponent).playerId === playerId + ) || null; + } +} + +// 启动服务器 +const server = new ServerGameManager(); +server.start(); +``` + +### 原生浏览器 + +```typescript +import { Core, Scene, EntityManager, EntitySystem } from '@esengine/ecs-framework'; + +class BrowserGame { + private scene: Scene; + private entityManager: EntityManager; + + constructor() { + Core.create(true); + this.scene = new Scene(); + this.scene.name = "BrowserScene"; + Core.scene = this.scene; + + this.entityManager = new EntityManager(); + this.setupSystems(); + } + + public start(): void { + this.createEntities(); + this.gameLoop(); + } + + private gameLoop(): void { + const update = () => { + this.scene.update(); + requestAnimationFrame(update); + }; + update(); + } + + private setupSystems(): void { + this.scene.addEntityProcessor(new MovementSystem()); + this.scene.addEntityProcessor(new RenderSystem()); + } + + private createEntities(): void { + const player = this.entityManager.createEntity("Player"); + player.addComponent(new PositionComponent(400, 300)); + player.addComponent(new VelocityComponent(0, 0)); + } +} + +const game = new BrowserGame(); +game.start(); +``` + +## 基础组件定义 + +```typescript +import { Component } from '@esengine/ecs-framework'; // 位置组件 class PositionComponent extends Component { @@ -146,7 +307,6 @@ class PositionComponent extends Component { this.y = y; } - // 对象池重置方法 public reset() { this.x = 0; this.y = 0; @@ -198,89 +358,12 @@ class HealthComponent extends Component { return this.currentHealth <= 0; } } - -// 简单的组件定义 -// 注:框架会自动优化组件的存储和查询 ``` -## 使用 EntityManager - -EntityManager 是框架的核心功能,提供统一的实体管理和高性能查询接口。 - -### 基础实体操作 +## 基础系统创建 ```typescript -// 获取EntityManager实例(在GameManager中已创建) -const entityManager = gameManager.getEntityManager(); - -// 创建单个实体 -const player = entityManager.createEntity("Player"); - -// 批量创建实体(使用Scene方法) -const enemies = scene.createEntities(50, "Enemy"); - -// 查询操作 -const movingEntities = entityManager - .query() - .withAll(PositionComponent, VelocityComponent) - .withNone(HealthComponent) - .execute(); - -// 组件查询 -const healthEntities = entityManager.getEntitiesWithComponent(HealthComponent); - -// 标签查询 -const allEnemies = entityManager.getEntitiesByTag(2); - -// 名称查询 -const specificEnemy = entityManager.getEntityByName("BossEnemy"); - -// 复合查询 -const livingEnemies = entityManager - .query() - .withAll(HealthComponent) - .withTag(2) - .execute(); -``` - -### 实体遍历 - -```typescript -// 遍历所有实体 -const allEntities = entityManager.getAllEntities(); -allEntities.forEach(entity => { - console.log(`实体: ${entity.name}, ID: ${entity.id}`); -}); - -// 遍历特定组件的实体 -const healthEntities = entityManager.getEntitiesWithComponent(HealthComponent); -healthEntities.forEach(entity => { - const health = entity.getComponent(HealthComponent); - console.log(`${entity.name} 生命值: ${health.currentHealth}/${health.maxHealth}`); -}); -``` - -### 性能监控 - -```typescript -// 获取场景统计 -const sceneStats = scene.getStats(); -console.log('实体数量:', sceneStats.entityCount); -console.log('系统数量:', sceneStats.processorCount); - -// 获取查询系统统计 -const queryStats = scene.querySystem.getStats(); -console.log('查询统计:', queryStats); - - -``` - -## 创建系统 - -系统处理具有特定组件的实体集合,实现游戏逻辑。 - -```typescript -import { EntitySystem, Entity, Matcher, EntityManager } from '@esengine/ecs-framework'; +import { EntitySystem, Entity, Matcher, Time } from '@esengine/ecs-framework'; class MovementSystem extends EntitySystem { constructor() { @@ -288,7 +371,6 @@ class MovementSystem extends EntitySystem { } protected process(entities: Entity[]): void { - // 使用Scene的查询系统进行高效查询 const movingEntities = this.scene.querySystem.queryAll(PositionComponent, VelocityComponent); movingEntities.entities.forEach(entity => { @@ -296,8 +378,8 @@ class MovementSystem extends EntitySystem { const velocity = entity.getComponent(VelocityComponent); if (position && velocity) { - position.x += velocity.dx * Time.deltaTime; - position.y += velocity.dy * Time.deltaTime; + position.x += velocity.x * Time.deltaTime; + position.y += velocity.y * Time.deltaTime; } }); } @@ -319,15 +401,35 @@ class HealthSystem extends EntitySystem { }); } } - -// 添加系统到场景 -gameManager.scene.addEntityProcessor(new MovementSystem()); -gameManager.scene.addEntityProcessor(new HealthSystem()); ``` -## 高级功能 +## 实体管理 -### 事件系统 +```typescript +// 创建实体 +const player = entityManager.createEntity("Player"); +player.addComponent(new PositionComponent(100, 100)); +player.addComponent(new VelocityComponent(5, 0)); +player.addComponent(new HealthComponent(100)); + +// 批量创建实体 +const enemies = scene.createEntities(50, "Enemy"); +enemies.forEach(enemy => { + enemy.addComponent(new PositionComponent(Math.random() * 800, Math.random() * 600)); + enemy.addComponent(new HealthComponent(50)); +}); + +// 查询实体 +const movingEntities = entityManager + .query() + .withAll(PositionComponent, VelocityComponent) + .execute(); + +const healthEntities = entityManager.getEntitiesWithComponent(HealthComponent); +const enemiesByTag = entityManager.getEntitiesByTag(2); +``` + +## 事件系统 ```typescript import { Core, CoreEvents } from '@esengine/ecs-framework'; @@ -342,160 +444,22 @@ Core.emitter.emit("playerDied", { player: entity, score: 1000 }); Core.emitter.removeObserver(CoreEvents.frameUpdated, this.onFrameUpdate); ``` -### 性能监控 +## 性能监控 ```typescript -// 获取场景性能统计 +// 获取场景统计 const sceneStats = scene.getStats(); -console.log(`总实体数: ${sceneStats.entityCount}`); -console.log(`系统数量: ${sceneStats.processorCount}`); +console.log('实体数量:', sceneStats.entityCount); +console.log('系统数量:', sceneStats.processorCount); -// 获取查询系统统计 +// 获取查询统计 const queryStats = scene.querySystem.getStats(); console.log('查询统计:', queryStats); ``` -## 简单示例 - -以下是一个完整的示例,展示了框架的主要功能: - -```typescript -import { - Core, - Entity, - Component, - Scene, - EntitySystem, - EntityManager -} from '@esengine/ecs-framework'; - -// 游戏管理器 -class SimpleGame { - private core: Core; - private scene: Scene; - private entityManager: EntityManager; - - constructor() { - this.core = Core.create(true); - this.scene = new Scene(); - this.scene.name = "GameScene"; - Core.scene = this.scene; - - this.entityManager = new EntityManager(); - this.setupSystems(); - } - - private setupSystems(): void { - this.scene.addEntityProcessor(new MovementSystem()); - this.scene.addEntityProcessor(new HealthSystem()); - } - - public start(): void { - // 创建游戏实体 - this.createPlayer(); - this.createEnemies(50); - - // 启动游戏循环 - this.gameLoop(); - } - - private createPlayer(): Entity { - const player = this.entityManager.createEntity("Player"); - player.addComponent(new PositionComponent(400, 300)); - player.addComponent(new VelocityComponent(0, 0)); - player.addComponent(new HealthComponent(100)); - player.tag = "player"; - return player; - } - - private createEnemies(count: number): Entity[] { - const enemies = this.scene.createEntities(count, "Enemy"); - - enemies.forEach((enemy, index) => { - enemy.addComponent(new PositionComponent( - Math.random() * 800, - Math.random() * 600 - )); - enemy.addComponent(new VelocityComponent( - (Math.random() - 0.5) * 100, - (Math.random() - 0.5) * 100 - )); - enemy.addComponent(new HealthComponent(50)); - enemy.tag = "enemy"; - }); - - return enemies; - } - - private gameLoop(): void { - const update = () => { - // 更新场景 - this.scene.update(); - requestAnimationFrame(update); - }; - update(); - } -} - -// 启动游戏 -const game = new SimpleGame(); -game.start(); -``` - -## 性能优化建议 - -### 1. 大规模实体处理 -- 使用 `scene.createEntities()` 批量创建实体 -- 利用组件索引系统进行高效查询 -- 启用Archetype系统减少查询遍历 - -### 2. 查询优化 -- 使用 `EntityManager.query()` 流式API构建复杂查询 -- 缓存频繁查询的结果 -- 利用脏标记系统避免不必要的更新 - -### 3. 性能监控 -- 定期检查 `scene.getStats()` 获取性能数据 -- 监控组件索引命中率 -- 使用框架提供的性能统计功能 - ## 下一步 -现在您已经掌握了 ECS Framework 的基础用法,可以继续学习: - - [EntityManager 使用指南](entity-manager-example.md) - 详细了解实体管理器的高级功能 - [性能优化指南](performance-optimization.md) - 深入了解三大性能优化系统 - [核心概念](core-concepts.md) - 深入了解 ECS 架构和设计原理 -- [查询系统使用指南](query-system-usage.md) - 学习高性能查询系统的详细用法 - -## 常见问题 - -### Q: 如何在不同游戏引擎中集成? - -A: ECS Framework 是引擎无关的,您只需要: -1. 通过npm安装框架 `npm install @esengine/ecs-framework` -2. 在游戏引擎的主循环中调用 `scene.update()` -3. 根据需要集成渲染、输入等引擎特定功能 - -### Q: 如何处理输入? - -A: 框架本身不提供输入处理,建议: -1. 创建一个输入组件来存储输入状态 -2. 在游戏引擎的输入回调中更新输入组件 -3. 创建输入处理系统来响应输入状态 - -### Q: 如何优化大规模实体性能? - -A: 关键优化策略: -1. 使用 `EntityManager` 的高级查询功能 -2. 启用组件索引系统进行快速查询 -3. 利用Archetype系统减少查询遍历 -4. 使用脏标记系统避免不必要的更新 - -### Q: EntityManager 有什么优势? - -A: EntityManager 提供了: -- O(1) 复杂度的组件查询(使用索引) -- 流式API的复杂查询构建 -- 自动的性能优化系统集成 -- 统一的实体管理接口 \ No newline at end of file +- [查询系统使用指南](query-system-usage.md) - 学习高性能查询系统的详细用法 \ No newline at end of file