新增world概念(多world管理多scene概念)现在支持多个world多个scene同时更新
This commit is contained in:
171
README.md
171
README.md
@@ -19,16 +19,154 @@ TypeScript ECS (Entity-Component-System) 框架,专为游戏开发设计。
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
## ECS 架构原理
|
## 架构原理
|
||||||
|
|
||||||
<div align="center">
|
ECS Framework 采用多World + 多Scene的现代化架构设计:
|
||||||
<img src="assets/svg/ecs-architecture.svg" alt="ECS 架构流程动画" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
ECS 是一种基于组合而非继承的软件架构模式:
|
```mermaid
|
||||||
- **Entity(实体)**: 游戏对象的唯一标识
|
graph TD
|
||||||
- **Component(组件)**: 纯数据结构,描述实体属性
|
subgraph Main["🎮 ECS Framework - 多World・多Scene架构"]
|
||||||
- **System(系统)**: 处理具有特定组件的实体
|
direction TB
|
||||||
|
|
||||||
|
subgraph CoreLayer["⚙️ 核心层 (Core Foundation)"]
|
||||||
|
direction LR
|
||||||
|
Core["🔧 <b>Core</b><br/>📋 生命周期管理<br/>⚙️ 配置系统<br/>🔗 平台兼容"]
|
||||||
|
Registry["📝 <b>ComponentRegistry</b><br/>🏷️ 类型注册<br/>✨ 装饰器支持<br/>🔒 类型安全"]
|
||||||
|
Pool["🔢 <b>IdentifierPool</b><br/>🆔 实体ID分配<br/>♻️ ID回收<br/>📊 BigInt兼容"]
|
||||||
|
PoolMgr["♻️ <b>PoolManager</b><br/>🎯 对象池<br/>⚡ 内存优化<br/>📈 性能提升"]
|
||||||
|
EventBus["📡 <b>EventBus</b><br/>🔄 事件系统<br/>⚡ 异步/同步<br/>🎭 类型安全"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph WorldLayer["🌍 世界管理层 (World Management)"]
|
||||||
|
direction TB
|
||||||
|
WorldMgr["🗺️ <b>WorldManager</b><br/>🚀 多World调度<br/>📊 资源管理<br/>🔍 统计监控<br/>🧹 自动清理"]
|
||||||
|
|
||||||
|
subgraph WorldsContainer["多World容器"]
|
||||||
|
direction LR
|
||||||
|
World1["🌐 <b>GameWorld</b><br/>🎮 游戏逻辑<br/>🌟 全局系统<br/>🔄 跨Scene业务"]
|
||||||
|
World2["🌐 <b>UIWorld</b><br/>🎨 界面管理<br/>⚡ 独立更新<br/>🔒 资源隔离"]
|
||||||
|
end
|
||||||
|
|
||||||
|
GlobalSys["🎭 <b>Global Systems</b><br/>🌐 NetworkSync<br/>👥 PlayerMgmt<br/>📡 跨Scene通信"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph SceneLayer["🎬 场景层 (Scene Management)"]
|
||||||
|
direction LR
|
||||||
|
Scene1["🎯 <b>BattleScene</b><br/>⚔️ 实体管理<br/>🎪 系统调度<br/>⚡ 高性能处理"]
|
||||||
|
Scene2["🎯 <b>MenuScene</b><br/>🎨 界面逻辑<br/>🔄 生命周期<br/>💾 状态管理"]
|
||||||
|
Scene3["🎯 <b>UIScene</b><br/>📦 组件存储<br/>🔍 查询引擎<br/>🎭 交互处理"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph ECLayer["🤖 实体组件层 (Entity-Component System)"]
|
||||||
|
direction TB
|
||||||
|
|
||||||
|
subgraph EntityMgmt["📦 实体管理 (Entity Management)"]
|
||||||
|
direction LR
|
||||||
|
EntityMgr["👥 <b>EntityManager</b><br/>📋 集合管理<br/>🌳 层次结构<br/>⚡ 高效操作"]
|
||||||
|
Entities["🎭 <b>Entities</b><br/>👤 Player<br/>👹 Enemy<br/>💥 Bullet<br/>🎯 轻量容器"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph ComponentStore["🧩 组件存储 (Component Storage)"]
|
||||||
|
direction LR
|
||||||
|
Storage["💾 <b>ComponentStorage</b><br/>📊 SoA模式<br/>📚 AoS模式<br/>⚡ 内存优化"]
|
||||||
|
StorageMgr["🗄️ <b>StorageManager</b><br/>🏷️ 类型管理<br/>🔄 脏标记<br/>📈 性能监控"]
|
||||||
|
Components["🎲 <b>Components</b><br/>📍 Position<br/>🏃 Velocity<br/>❤️ Health<br/>📊 纯数据"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph SystemLayer["⚡ 系统层 (System Processing)"]
|
||||||
|
direction TB
|
||||||
|
|
||||||
|
subgraph EntitySys["🔄 实体系统 (Entity Systems)"]
|
||||||
|
direction LR
|
||||||
|
EntitySystems["🎪 <b>EntitySystems</b><br/>🏃 MovementSystem<br/>🎨 RenderSystem<br/>🧠 AISystem<br/>⚡ 业务逻辑"]
|
||||||
|
Processors["📋 <b>EntityProcessors</b><br/>🎯 调度管理<br/>📊 优先级<br/>⚡ 批量处理"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph QueryLayer["🔍 查询优化层 (Query & Optimization)"]
|
||||||
|
direction LR
|
||||||
|
Matcher["🎯 <b>Matcher</b><br/>✅ withAll<br/>🔄 withAny<br/>❌ withNone<br/>🌊 流式API<br/>💾 智能缓存"]
|
||||||
|
QuerySys["🔎 <b>QuerySystem</b><br/>⚡ 实时查询<br/>📦 批量优化<br/>🔄 自动更新"]
|
||||||
|
Archetype["🏗️ <b>ArchetypeSystem</b><br/>📊 组件分组<br/>🎯 原型缓存<br/>💻 BitSet优化"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph DebugLayer["📊 监控调试层 (Debug & Monitoring)"]
|
||||||
|
direction LR
|
||||||
|
Debug["🐛 <b>DebugManager</b><br/>🌐 WebSocket调试<br/>🎮 Cocos Creator插件<br/>📸 内存快照"]
|
||||||
|
Perf["📈 <b>PerformanceMonitor</b><br/>📊 性能统计<br/>⚠️ 阈值告警<br/>📱 实时监控"]
|
||||||
|
Logger["📋 <b>Logger</b><br/>📊 分级日志<br/>🎨 彩色输出<br/>🔧 自定义处理器"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
%% 连接关系 - 使用更丰富的箭头样式
|
||||||
|
Core -.->|初始化| WorldMgr
|
||||||
|
Core -.->|注册| Registry
|
||||||
|
Core -.->|分配| Pool
|
||||||
|
Core -.->|管理| PoolMgr
|
||||||
|
Core -.->|事件| EventBus
|
||||||
|
|
||||||
|
WorldMgr ==>|调度| World1
|
||||||
|
WorldMgr ==>|调度| World2
|
||||||
|
World1 -.->|管理| GlobalSys
|
||||||
|
|
||||||
|
World1 ==>|包含| Scene1
|
||||||
|
World1 ==>|包含| Scene2
|
||||||
|
World2 ==>|包含| Scene3
|
||||||
|
|
||||||
|
Scene1 -->|使用| EntityMgr
|
||||||
|
Scene2 -->|使用| EntityMgr
|
||||||
|
Scene3 -->|使用| EntityMgr
|
||||||
|
|
||||||
|
EntityMgr -->|管理| Entities
|
||||||
|
Entities -->|附加| Components
|
||||||
|
|
||||||
|
Scene1 -->|存储| Storage
|
||||||
|
Scene2 -->|存储| Storage
|
||||||
|
Scene3 -->|存储| Storage
|
||||||
|
Storage -->|管理| StorageMgr
|
||||||
|
|
||||||
|
Scene1 -->|调度| EntitySystems
|
||||||
|
Scene2 -->|调度| EntitySystems
|
||||||
|
Scene3 -->|调度| EntitySystems
|
||||||
|
EntitySystems -->|处理| Processors
|
||||||
|
|
||||||
|
EntitySystems -->|查询| Matcher
|
||||||
|
Matcher -->|缓存| QuerySys
|
||||||
|
QuerySys -->|优化| Archetype
|
||||||
|
|
||||||
|
Core -.->|调试| Debug
|
||||||
|
Core -.->|监控| Perf
|
||||||
|
Core -.->|日志| Logger
|
||||||
|
|
||||||
|
%% 样式定义 - 使用Mermaid支持的语法
|
||||||
|
classDef coreStyle fill:#E3F2FD,stroke:#1976D2,stroke-width:3px,color:#0D47A1
|
||||||
|
classDef worldStyle fill:#F3E5F5,stroke:#7B1FA2,stroke-width:3px,color:#4A148C
|
||||||
|
classDef sceneStyle fill:#FFF3E0,stroke:#F57C00,stroke-width:3px,color:#E65100
|
||||||
|
classDef entityStyle fill:#E8F5E8,stroke:#388E3C,stroke-width:3px,color:#1B5E20
|
||||||
|
classDef systemStyle fill:#FCE4EC,stroke:#C2185B,stroke-width:3px,color:#880E4F
|
||||||
|
classDef queryStyle fill:#E0F2F1,stroke:#00695C,stroke-width:3px,color:#004D40
|
||||||
|
classDef debugStyle fill:#FFF8E1,stroke:#F9A825,stroke-width:3px,color:#FF8F00
|
||||||
|
|
||||||
|
class Core,Registry,Pool,PoolMgr,EventBus coreStyle
|
||||||
|
class WorldMgr,World1,World2,GlobalSys worldStyle
|
||||||
|
class Scene1,Scene2,Scene3 sceneStyle
|
||||||
|
class EntityMgr,Entities,Storage,StorageMgr,Components entityStyle
|
||||||
|
class EntitySystems,Processors systemStyle
|
||||||
|
class Matcher,QuerySys,Archetype queryStyle
|
||||||
|
class Debug,Perf,Logger debugStyle
|
||||||
|
```
|
||||||
|
|
||||||
|
### 核心概念
|
||||||
|
|
||||||
|
| 概念 | 职责 | 特点 |
|
||||||
|
|------|------|------|
|
||||||
|
| **Entity** | 游戏对象唯一标识 | 轻量级容器,无业务逻辑 |
|
||||||
|
| **Component** | 纯数据结构 | 描述实体属性,支持SoA优化 |
|
||||||
|
| **System** | 业务逻辑处理 | 操作组件数据,可热插拔 |
|
||||||
|
| **Scene** | 实体和系统容器 | 独立的游戏场景 |
|
||||||
|
| **World** | Scene和全局系统容器 | 支持跨Scene的全局逻辑 |
|
||||||
|
| **WorldManager** | 多World管理 | 统一调度和资源管理 |
|
||||||
|
|
||||||
## 特性
|
## 特性
|
||||||
|
|
||||||
@@ -201,11 +339,20 @@ class GameSystem {
|
|||||||
|
|
||||||
### SoA 存储优化
|
### SoA 存储优化
|
||||||
|
|
||||||
<div align="center">
|
针对大规模实体处理的内存布局优化:
|
||||||
<img src="assets/svg/soa-vs-aos.svg" alt="SoA vs AoS 数据结构对比" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
用于大规模实体处理:
|
| 存储方式 | 内存布局 | 适用场景 | 性能特点 |
|
||||||
|
|----------|----------|----------|----------|
|
||||||
|
| **AoS** (Array of Structures) | `[{x,y,z}, {x,y,z}, {x,y,z}]` | 通用场景 | 访问灵活,缓存效率一般 |
|
||||||
|
| **SoA** (Structure of Arrays) | `{x:[1,2,3], y:[4,5,6], z:[7,8,9]}` | 批量处理 | SIMD优化,缓存友好 |
|
||||||
|
|
||||||
|
**SoA 优势:**
|
||||||
|
- 🚀 提升 2-4x 批量处理性能
|
||||||
|
- 💾 更好的CPU缓存利用率
|
||||||
|
- 🔧 支持SIMD向量化操作
|
||||||
|
- ⚡ 减少内存访问跳跃
|
||||||
|
|
||||||
|
用法示例:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { EnableSoA, Float32, Int32 } from '@esengine/ecs-framework';
|
import { EnableSoA, Float32, Int32 } from '@esengine/ecs-framework';
|
||||||
|
|||||||
@@ -1,382 +0,0 @@
|
|||||||
<svg width="1200" height="850" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<defs>
|
|
||||||
<!-- 渐变定义 - 柔和色调 -->
|
|
||||||
<linearGradient id="coreGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
||||||
<stop offset="0%" style="stop-color:#8892b0"/>
|
|
||||||
<stop offset="100%" style="stop-color:#a5b4cb"/>
|
|
||||||
</linearGradient>
|
|
||||||
|
|
||||||
<linearGradient id="sceneGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
||||||
<stop offset="0%" style="stop-color:#7c9cbf"/>
|
|
||||||
<stop offset="100%" style="stop-color:#9bb5d1"/>
|
|
||||||
</linearGradient>
|
|
||||||
|
|
||||||
<linearGradient id="entityGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
||||||
<stop offset="0%" style="stop-color:#81b29a"/>
|
|
||||||
<stop offset="100%" style="stop-color:#a8caba"/>
|
|
||||||
</linearGradient>
|
|
||||||
|
|
||||||
<linearGradient id="componentGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
||||||
<stop offset="0%" style="stop-color:#d4a574"/>
|
|
||||||
<stop offset="100%" style="stop-color:#e5c7a0"/>
|
|
||||||
</linearGradient>
|
|
||||||
|
|
||||||
<linearGradient id="systemGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
||||||
<stop offset="0%" style="stop-color:#c49799"/>
|
|
||||||
<stop offset="100%" style="stop-color:#d9b5b7"/>
|
|
||||||
</linearGradient>
|
|
||||||
|
|
||||||
<linearGradient id="queryGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
||||||
<stop offset="0%" style="stop-color:#b0c4de"/>
|
|
||||||
<stop offset="100%" style="stop-color:#d0dff0"/>
|
|
||||||
</linearGradient>
|
|
||||||
|
|
||||||
<linearGradient id="eventGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
||||||
<stop offset="0%" style="stop-color:#c5a3c5"/>
|
|
||||||
<stop offset="100%" style="stop-color:#e0c4e0"/>
|
|
||||||
</linearGradient>
|
|
||||||
|
|
||||||
<!-- 动画定义 -->
|
|
||||||
<style>
|
|
||||||
.data-flow-line {
|
|
||||||
animation: dataFlow 6s linear infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes dataFlow {
|
|
||||||
0% { stroke-dashoffset: 40; }
|
|
||||||
100% { stroke-dashoffset: 0; }
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</defs>
|
|
||||||
|
|
||||||
<!-- 背景 -->
|
|
||||||
<rect width="1200" height="850" fill="white" rx="15" stroke="#e2e8f0" stroke-width="1"/>
|
|
||||||
|
|
||||||
<!-- 标题 -->
|
|
||||||
<text x="600" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="24" font-weight="bold" fill="#2c3e50">
|
|
||||||
ECS Framework 完整架构
|
|
||||||
</text>
|
|
||||||
|
|
||||||
<!-- Core 核心层 -->
|
|
||||||
<g id="core-layer">
|
|
||||||
<!-- 流程步骤标号 -->
|
|
||||||
<circle cx="480" cy="75" r="15" fill="#8892b0" stroke="white" stroke-width="2"/>
|
|
||||||
<text x="480" y="81" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="white">1</text>
|
|
||||||
|
|
||||||
<rect x="400" y="60" width="400" height="60" rx="10" fill="url(#coreGradient)" opacity="0.9"/>
|
|
||||||
<text x="600" y="85" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="white">
|
|
||||||
Core 框架核心
|
|
||||||
</text>
|
|
||||||
<text x="500" y="105" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="white">
|
|
||||||
ComponentRegistry • IdentifierPool
|
|
||||||
</text>
|
|
||||||
<text x="700" y="105" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="white">
|
|
||||||
EventBus • ConfigManager
|
|
||||||
</text>
|
|
||||||
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Scene 场景管理层 -->
|
|
||||||
<g id="scene-layer">
|
|
||||||
<!-- 流程步骤标号 -->
|
|
||||||
<circle cx="280" cy="175" r="15" fill="#7c9cbf" stroke="white" stroke-width="2"/>
|
|
||||||
<text x="280" y="181" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="white">2</text>
|
|
||||||
|
|
||||||
<rect x="300" y="150" width="600" height="80" rx="12" fill="url(#sceneGradient)" opacity="0.9"/>
|
|
||||||
<text x="600" y="175" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="bold" fill="white">
|
|
||||||
Scene 场景管理器
|
|
||||||
</text>
|
|
||||||
<text x="450" y="195" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="white">
|
|
||||||
EntityList
|
|
||||||
</text>
|
|
||||||
<text x="600" y="195" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="white">
|
|
||||||
ComponentStorageManager
|
|
||||||
</text>
|
|
||||||
<text x="750" y="195" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="white">
|
|
||||||
EntityProcessors
|
|
||||||
</text>
|
|
||||||
<text x="600" y="215" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="white">
|
|
||||||
实体管理 • 组件存储 • 系统调度 • 查询引擎
|
|
||||||
</text>
|
|
||||||
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Entity 实体层 -->
|
|
||||||
<g id="entity-layer">
|
|
||||||
<!-- 流程步骤标号 -->
|
|
||||||
<circle cx="30" cy="320" r="15" fill="#81b29a" stroke="white" stroke-width="2"/>
|
|
||||||
<text x="30" y="326" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="white">3</text>
|
|
||||||
|
|
||||||
<rect x="50" y="280" width="280" height="180" rx="10" fill="url(#entityGradient)" opacity="0.9"/>
|
|
||||||
<text x="190" y="305" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="white">
|
|
||||||
Entity 实体系统
|
|
||||||
</text>
|
|
||||||
|
|
||||||
<!-- Entity Manager -->
|
|
||||||
<rect x="70" y="320" width="240" height="30" rx="5" fill="rgba(255,255,255,0.3)" stroke="rgba(255,255,255,0.5)" stroke-width="1"/>
|
|
||||||
<text x="190" y="340" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="white">
|
|
||||||
EntityList • 高性能实体集合管理
|
|
||||||
</text>
|
|
||||||
|
|
||||||
<!-- Entity instances -->
|
|
||||||
<rect x="80" y="360" width="60" height="20" rx="3" fill="rgba(255,255,255,0.4)"/>
|
|
||||||
<text x="110" y="373" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="white">Player</text>
|
|
||||||
|
|
||||||
<rect x="150" y="360" width="60" height="20" rx="3" fill="rgba(255,255,255,0.4)"/>
|
|
||||||
<text x="180" y="373" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="white">Enemy</text>
|
|
||||||
|
|
||||||
<rect x="220" y="360" width="60" height="20" rx="3" fill="rgba(255,255,255,0.4)"/>
|
|
||||||
<text x="250" y="373" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="white">Bullet</text>
|
|
||||||
|
|
||||||
<!-- Entity features -->
|
|
||||||
<text x="80" y="400" font-family="Arial, sans-serif" font-size="9" fill="white">• 组件容器</text>
|
|
||||||
<text x="80" y="415" font-family="Arial, sans-serif" font-size="9" fill="white">• 层次结构</text>
|
|
||||||
<text x="190" y="400" font-family="Arial, sans-serif" font-size="9" fill="white">• 生命周期管理</text>
|
|
||||||
<text x="190" y="415" font-family="Arial, sans-serif" font-size="9" fill="white">• 状态管理</text>
|
|
||||||
|
|
||||||
<text x="190" y="440" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="white">
|
|
||||||
唯一标识 • 无数据逻辑载体
|
|
||||||
</text>
|
|
||||||
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Component 组件层 -->
|
|
||||||
<g id="component-layer">
|
|
||||||
<!-- 流程步骤标号 -->
|
|
||||||
<circle cx="650" cy="320" r="15" fill="#d4a574" stroke="white" stroke-width="2"/>
|
|
||||||
<text x="650" y="326" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="white">4</text>
|
|
||||||
|
|
||||||
<rect x="360" y="280" width="280" height="180" rx="10" fill="url(#componentGradient)" opacity="0.9"/>
|
|
||||||
<text x="500" y="305" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="white">
|
|
||||||
Component 组件系统
|
|
||||||
</text>
|
|
||||||
|
|
||||||
<!-- Component Storage -->
|
|
||||||
<rect x="380" y="315" width="240" height="40" rx="5" fill="rgba(255,255,255,0.3)" stroke="rgba(255,255,255,0.5)" stroke-width="1"/>
|
|
||||||
<text x="500" y="330" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="white">
|
|
||||||
ComponentStorageManager • SoA/AoS 双模式
|
|
||||||
</text>
|
|
||||||
<text x="500" y="345" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="white">
|
|
||||||
ComponentPool • DirtyTrackingSystem
|
|
||||||
</text>
|
|
||||||
|
|
||||||
<!-- Component types -->
|
|
||||||
<rect x="380" y="360" width="110" height="18" rx="2" fill="rgba(255,255,255,0.4)"/>
|
|
||||||
<text x="435" y="372" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="white">Position {x,y,z}</text>
|
|
||||||
|
|
||||||
<rect x="500" y="360" width="110" height="18" rx="2" fill="rgba(255,255,255,0.4)"/>
|
|
||||||
<text x="555" y="372" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="white">Velocity {dx,dy,dz}</text>
|
|
||||||
|
|
||||||
<rect x="380" y="385" width="110" height="18" rx="2" fill="rgba(255,255,255,0.4)"/>
|
|
||||||
<text x="435" y="397" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="white">Health {hp,max}</text>
|
|
||||||
|
|
||||||
<rect x="500" y="385" width="110" height="18" rx="2" fill="rgba(255,255,255,0.4)"/>
|
|
||||||
<text x="555" y="397" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="white">Render {sprite}</text>
|
|
||||||
|
|
||||||
<!-- Component features -->
|
|
||||||
<text x="380" y="420" font-family="Arial, sans-serif" font-size="9" fill="white">• @EnableSoA 优化</text>
|
|
||||||
<text x="520" y="420" font-family="Arial, sans-serif" font-size="9" fill="white">• 对象池管理</text>
|
|
||||||
<text x="380" y="435" font-family="Arial, sans-serif" font-size="9" fill="white">• 序列化支持</text>
|
|
||||||
<text x="520" y="435" font-family="Arial, sans-serif" font-size="9" fill="white">• 类型安全</text>
|
|
||||||
|
|
||||||
<text x="500" y="450" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="white">
|
|
||||||
纯数据结构 • 描述实体属性
|
|
||||||
</text>
|
|
||||||
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- System 系统层 -->
|
|
||||||
<g id="system-layer">
|
|
||||||
<!-- 流程步骤标号 -->
|
|
||||||
<circle cx="1160" cy="320" r="15" fill="#c49799" stroke="white" stroke-width="2"/>
|
|
||||||
<text x="1160" y="326" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="white">5</text>
|
|
||||||
|
|
||||||
<rect x="870" y="280" width="280" height="180" rx="10" fill="url(#systemGradient)" opacity="0.9"/>
|
|
||||||
<text x="1010" y="305" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="white">
|
|
||||||
System 系统层
|
|
||||||
</text>
|
|
||||||
|
|
||||||
<!-- System Processors -->
|
|
||||||
<rect x="890" y="320" width="240" height="30" rx="5" fill="rgba(255,255,255,0.3)" stroke="rgba(255,255,255,0.5)" stroke-width="1"/>
|
|
||||||
<text x="1010" y="340" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="white">
|
|
||||||
EntityProcessors • 系统调度管理
|
|
||||||
</text>
|
|
||||||
|
|
||||||
<!-- System instances -->
|
|
||||||
<rect x="890" y="360" width="110" height="18" rx="2" fill="rgba(255,255,255,0.4)"/>
|
|
||||||
<text x="945" y="372" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="white">MovementSystem</text>
|
|
||||||
|
|
||||||
<rect x="1010" y="360" width="110" height="18" rx="2" fill="rgba(255,255,255,0.4)"/>
|
|
||||||
<text x="1065" y="372" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="white">RenderSystem</text>
|
|
||||||
|
|
||||||
<rect x="890" y="385" width="110" height="18" rx="2" fill="rgba(255,255,255,0.4)"/>
|
|
||||||
<text x="945" y="397" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="white">PhysicsSystem</text>
|
|
||||||
|
|
||||||
<rect x="1010" y="385" width="110" height="18" rx="2" fill="rgba(255,255,255,0.4)"/>
|
|
||||||
<text x="1065" y="397" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="white">AISystem</text>
|
|
||||||
|
|
||||||
<!-- System features -->
|
|
||||||
<text x="890" y="420" font-family="Arial, sans-serif" font-size="9" fill="white">• Matcher 查询</text>
|
|
||||||
<text x="1020" y="420" font-family="Arial, sans-serif" font-size="9" fill="white">• 性能监控</text>
|
|
||||||
<text x="890" y="435" font-family="Arial, sans-serif" font-size="9" fill="white">• 优先级调度</text>
|
|
||||||
<text x="1020" y="435" font-family="Arial, sans-serif" font-size="9" fill="white">• 热插拔</text>
|
|
||||||
|
|
||||||
<text x="1010" y="450" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="white">
|
|
||||||
业务逻辑处理 • 操作组件数据
|
|
||||||
</text>
|
|
||||||
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Query 查询层 -->
|
|
||||||
<g id="query-layer">
|
|
||||||
<!-- 流程步骤标号 -->
|
|
||||||
<circle cx="30" cy="540" r="15" fill="#b0c4de" stroke="white" stroke-width="2"/>
|
|
||||||
<text x="30" y="546" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="white">6</text>
|
|
||||||
|
|
||||||
<rect x="50" y="500" width="500" height="120" rx="10" fill="url(#queryGradient)" opacity="0.9"/>
|
|
||||||
<text x="300" y="525" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="#2c3e50">
|
|
||||||
Query 查询系统
|
|
||||||
</text>
|
|
||||||
|
|
||||||
<!-- Query components -->
|
|
||||||
<rect x="70" y="540" width="140" height="60" rx="5" fill="rgba(255,255,255,0.6)" stroke="rgba(255,255,255,0.8)" stroke-width="1"/>
|
|
||||||
<text x="140" y="555" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#4a5568">Matcher</text>
|
|
||||||
<text x="140" y="570" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#4a5568">withAll()</text>
|
|
||||||
<text x="140" y="582" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#4a5568">withAny()</text>
|
|
||||||
<text x="140" y="594" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#4a5568">withNone()</text>
|
|
||||||
|
|
||||||
<rect x="230" y="540" width="140" height="60" rx="5" fill="rgba(255,255,255,0.6)" stroke="rgba(255,255,255,0.8)" stroke-width="1"/>
|
|
||||||
<text x="300" y="555" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#4a5568">QuerySystem</text>
|
|
||||||
<text x="300" y="570" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#4a5568">查询缓存</text>
|
|
||||||
<text x="300" y="582" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#4a5568">批量优化</text>
|
|
||||||
<text x="300" y="594" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#4a5568">实时更新</text>
|
|
||||||
|
|
||||||
<rect x="390" y="540" width="140" height="60" rx="5" fill="rgba(255,255,255,0.6)" stroke="rgba(255,255,255,0.8)" stroke-width="1"/>
|
|
||||||
<text x="460" y="553" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#4a5568">ArchetypeSystem</text>
|
|
||||||
<text x="460" y="568" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#4a5568">组件组合分组</text>
|
|
||||||
<text x="460" y="580" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#4a5568">原型级缓存</text>
|
|
||||||
<text x="460" y="592" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#4a5568">BitSet优化查询</text>
|
|
||||||
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Event 事件系统 -->
|
|
||||||
<g id="event-layer">
|
|
||||||
<!-- 流程步骤标号 -->
|
|
||||||
<circle cx="1160" cy="540" r="15" fill="#c5a3c5" stroke="white" stroke-width="2"/>
|
|
||||||
<text x="1160" y="546" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="white">7</text>
|
|
||||||
|
|
||||||
<rect x="580" y="500" width="570" height="120" rx="10" fill="url(#eventGradient)" opacity="0.9"/>
|
|
||||||
<text x="865" y="525" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="#2c3e50">
|
|
||||||
Event 事件系统
|
|
||||||
</text>
|
|
||||||
|
|
||||||
<!-- Event components -->
|
|
||||||
<rect x="600" y="540" width="150" height="60" rx="5" fill="rgba(255,255,255,0.6)" stroke="rgba(255,255,255,0.8)" stroke-width="1"/>
|
|
||||||
<text x="675" y="555" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#4a5568">TypeSafeEventSystem</text>
|
|
||||||
<text x="675" y="570" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#4a5568">同步/异步</text>
|
|
||||||
<text x="675" y="582" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#4a5568">优先级排序</text>
|
|
||||||
<text x="675" y="594" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#4a5568">批处理机制</text>
|
|
||||||
|
|
||||||
<rect x="770" y="540" width="150" height="60" rx="5" fill="rgba(255,255,255,0.6)" stroke="rgba(255,255,255,0.8)" stroke-width="1"/>
|
|
||||||
<text x="845" y="555" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#4a5568">Performance Monitor</text>
|
|
||||||
<text x="845" y="570" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#4a5568">性能统计</text>
|
|
||||||
<text x="845" y="582" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#4a5568">阈值告警</text>
|
|
||||||
<text x="845" y="594" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#4a5568">实时监控</text>
|
|
||||||
|
|
||||||
<rect x="940" y="540" width="150" height="60" rx="5" fill="rgba(255,255,255,0.6)" stroke="rgba(255,255,255,0.8)" stroke-width="1"/>
|
|
||||||
<text x="1015" y="555" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#4a5568">Debug Manager</text>
|
|
||||||
<text x="1015" y="570" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#4a5568">WebSocket通信</text>
|
|
||||||
<text x="1015" y="582" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#4a5568">实时调试数据</text>
|
|
||||||
<text x="1015" y="594" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#4a5568">内存快照</text>
|
|
||||||
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- 数据流连接线 -->
|
|
||||||
<!-- 1. Core to Scene - 初始化流程 -->
|
|
||||||
<path d="M 600 120 Q 600 135 600 150" stroke="#8892b0" stroke-width="3" fill="none" stroke-dasharray="10,5" class="data-flow-line"/>
|
|
||||||
<polygon points="595,145 600,150 605,145" fill="#8892b0"/>
|
|
||||||
<text x="620" y="135" font-family="Arial, sans-serif" font-size="10" fill="#8892b0" font-weight="bold">初始化</text>
|
|
||||||
|
|
||||||
<!-- 2. Scene to Entity/Component/System - 管理流程 -->
|
|
||||||
<path d="M 400 230 Q 300 250 190 280" stroke="#7c9cbf" stroke-width="3" fill="none" stroke-dasharray="8,4" class="data-flow-line"/>
|
|
||||||
<polygon points="195,275 190,280 200,282" fill="#7c9cbf"/>
|
|
||||||
<text x="295" y="250" font-family="Arial, sans-serif" font-size="9" fill="#7c9cbf">管理实体</text>
|
|
||||||
|
|
||||||
<path d="M 600 230 Q 600 250 500 280" stroke="#7c9cbf" stroke-width="3" fill="none" stroke-dasharray="8,4" class="data-flow-line"/>
|
|
||||||
<polygon points="495,275 500,280 505,275" fill="#7c9cbf"/>
|
|
||||||
<text x="550" y="250" font-family="Arial, sans-serif" font-size="9" fill="#7c9cbf">存储组件</text>
|
|
||||||
|
|
||||||
<path d="M 800 230 Q 900 250 1010 280" stroke="#7c9cbf" stroke-width="3" fill="none" stroke-dasharray="8,4" class="data-flow-line"/>
|
|
||||||
<polygon points="1005,275 1010,280 1000,282" fill="#7c9cbf"/>
|
|
||||||
<text x="905" y="250" font-family="Arial, sans-serif" font-size="9" fill="#7c9cbf">调度系统</text>
|
|
||||||
|
|
||||||
<!-- 3. Entity to Component - 组件附加 -->
|
|
||||||
<path d="M 330 370 Q 350 370 360 370" stroke="#81b29a" stroke-width="3" fill="none" stroke-dasharray="6,3" class="data-flow-line"/>
|
|
||||||
<polygon points="355,365 360,370 355,375" fill="#81b29a"/>
|
|
||||||
<text x="345" y="360" font-family="Arial, sans-serif" font-size="9" fill="#81b29a" font-weight="bold">附加组件</text>
|
|
||||||
|
|
||||||
<!-- 4. Component to System - 数据处理 -->
|
|
||||||
<path d="M 640 370 Q 750 370 870 370" stroke="#d4a574" stroke-width="3" fill="none" stroke-dasharray="6,3" class="data-flow-line"/>
|
|
||||||
<polygon points="865,365 870,370 865,375" fill="#d4a574"/>
|
|
||||||
<text x="755" y="360" font-family="Arial, sans-serif" font-size="9" fill="#d4a574" font-weight="bold">处理数据</text>
|
|
||||||
|
|
||||||
<!-- 5. Scene to Query/Event - 查询和事件 -->
|
|
||||||
<path d="M 450 230 Q 350 350 300 500" stroke="#b0c4de" stroke-width="2" fill="none" stroke-dasharray="5,2" class="data-flow-line"/>
|
|
||||||
<polygon points="305,495 300,500 295,495" fill="#b0c4de"/>
|
|
||||||
<text x="375" y="365" font-family="Arial, sans-serif" font-size="8" fill="#b0c4de">查询支持</text>
|
|
||||||
|
|
||||||
<path d="M 750 230 Q 850 350 865 500" stroke="#c5a3c5" stroke-width="2" fill="none" stroke-dasharray="5,2" class="data-flow-line"/>
|
|
||||||
<polygon points="860,495 865,500 870,495" fill="#c5a3c5"/>
|
|
||||||
<text x="808" y="365" font-family="Arial, sans-serif" font-size="8" fill="#c5a3c5">事件通知</text>
|
|
||||||
|
|
||||||
<!-- 6. Query to System - 查询结果 -->
|
|
||||||
<path d="M 460 540 Q 680 480 900 380" stroke="#b0c4de" stroke-width="3" fill="none" stroke-dasharray="4,2" class="data-flow-line"/>
|
|
||||||
<polygon points="895,375 900,380 895,385" fill="#b0c4de"/>
|
|
||||||
<text x="680" y="470" font-family="Arial, sans-serif" font-size="9" fill="#b0c4de" font-weight="bold">匹配结果</text>
|
|
||||||
|
|
||||||
<!-- 7. System回流 - 数据修改和事件 -->
|
|
||||||
<path d="M 900 400 Q 800 420 700 430 Q 600 440 500 430 Q 400 420 350 400" stroke="#c49799" stroke-width="2" fill="none" stroke-dasharray="3,2" class="data-flow-line"/>
|
|
||||||
<text x="625" y="415" font-family="Arial, sans-serif" font-size="8" fill="#c49799">修改组件数据</text>
|
|
||||||
|
|
||||||
<path d="M 1000 460 Q 1050 480 1100 500" stroke="#c5a3c5" stroke-width="2" fill="none" stroke-dasharray="3,2" class="data-flow-line"/>
|
|
||||||
<text x="1050" y="475" font-family="Arial, sans-serif" font-size="8" fill="#c5a3c5">触发事件</text>
|
|
||||||
|
|
||||||
<!-- 工作流程说明 -->
|
|
||||||
<g id="workflow">
|
|
||||||
<rect x="50" y="720" width="1100" height="120" rx="8" fill="rgba(108, 117, 125, 0.05)" stroke="#6c757d" stroke-width="1" stroke-dasharray="5,3"/>
|
|
||||||
<text x="600" y="745" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="#495057">
|
|
||||||
🔄 ECS 框架7步工作流程
|
|
||||||
</text>
|
|
||||||
|
|
||||||
<!-- 流程步骤详解 -->
|
|
||||||
<g id="step-details">
|
|
||||||
<text x="70" y="770" font-family="Arial, sans-serif" font-size="11" fill="#8892b0" font-weight="bold">①初始化</text>
|
|
||||||
<text x="70" y="785" font-family="Arial, sans-serif" font-size="10" fill="#6c757d">Core.create()</text>
|
|
||||||
|
|
||||||
<text x="170" y="770" font-family="Arial, sans-serif" font-size="11" fill="#7c9cbf" font-weight="bold">②场景管理</text>
|
|
||||||
<text x="170" y="785" font-family="Arial, sans-serif" font-size="10" fill="#6c757d">Scene.initialize()</text>
|
|
||||||
|
|
||||||
<text x="280" y="770" font-family="Arial, sans-serif" font-size="11" fill="#81b29a" font-weight="bold">③创建实体</text>
|
|
||||||
<text x="280" y="785" font-family="Arial, sans-serif" font-size="10" fill="#6c757d">Entity.create()</text>
|
|
||||||
|
|
||||||
<text x="380" y="770" font-family="Arial, sans-serif" font-size="11" fill="#d4a574" font-weight="bold">④附加组件</text>
|
|
||||||
<text x="380" y="785" font-family="Arial, sans-serif" font-size="10" fill="#6c757d">addComponent()</text>
|
|
||||||
|
|
||||||
<text x="480" y="770" font-family="Arial, sans-serif" font-size="11" fill="#c49799" font-weight="bold">⑤系统处理</text>
|
|
||||||
<text x="480" y="785" font-family="Arial, sans-serif" font-size="10" fill="#6c757d">System.process()</text>
|
|
||||||
|
|
||||||
<text x="580" y="770" font-family="Arial, sans-serif" font-size="11" fill="#b0c4de" font-weight="bold">⑥查询匹配</text>
|
|
||||||
<text x="580" y="785" font-family="Arial, sans-serif" font-size="10" fill="#6c757d">Matcher.query()</text>
|
|
||||||
|
|
||||||
<text x="680" y="770" font-family="Arial, sans-serif" font-size="11" fill="#c5a3c5" font-weight="bold">⑦事件通知</text>
|
|
||||||
<text x="680" y="785" font-family="Arial, sans-serif" font-size="10" fill="#6c757d">Event.emit()</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<text x="600" y="810" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="#6c757d">
|
|
||||||
每帧循环:查询实体 → 匹配组件 → 执行系统逻辑 → 修改数据 → 触发事件 → 性能监控
|
|
||||||
</text>
|
|
||||||
<text x="600" y="828" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="#6c757d">
|
|
||||||
💡 圆形数字显示执行顺序 • 不同颜色连线代表不同数据流
|
|
||||||
</text>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 23 KiB |
@@ -1,273 +0,0 @@
|
|||||||
<svg width="800" height="500" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<defs>
|
|
||||||
<!-- 渐变定义 -->
|
|
||||||
<linearGradient id="soaGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
||||||
<stop offset="0%" style="stop-color:#667eea"/>
|
|
||||||
<stop offset="100%" style="stop-color:#764ba2"/>
|
|
||||||
</linearGradient>
|
|
||||||
|
|
||||||
<linearGradient id="aosGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
||||||
<stop offset="0%" style="stop-color:#f093fb"/>
|
|
||||||
<stop offset="100%" style="stop-color:#f5576c"/>
|
|
||||||
</linearGradient>
|
|
||||||
|
|
||||||
<linearGradient id="performanceGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
||||||
<stop offset="0%" style="stop-color:#4facfe"/>
|
|
||||||
<stop offset="100%" style="stop-color:#00f2fe"/>
|
|
||||||
</linearGradient>
|
|
||||||
|
|
||||||
<!-- 动画定义 -->
|
|
||||||
<style>
|
|
||||||
.data-flow {
|
|
||||||
animation: dataMove 3s ease-in-out infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
.memory-access {
|
|
||||||
animation: memoryAccess 2s ease-in-out infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
.performance-bar {
|
|
||||||
animation: performanceGrow 2.5s ease-out forwards;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-reveal {
|
|
||||||
animation: textReveal 1s ease-in forwards;
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.structure-highlight {
|
|
||||||
animation: structureHighlight 4s ease-in-out infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes dataMove {
|
|
||||||
0%, 100% { opacity: 0.6; filter: brightness(1); }
|
|
||||||
50% { opacity: 1; filter: brightness(1.2); }
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes memoryAccess {
|
|
||||||
0%, 100% { fill: #e2e8f0; }
|
|
||||||
50% { fill: #3182ce; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes performanceGrow {
|
|
||||||
0% { width: 0; opacity: 0.5; }
|
|
||||||
100% { opacity: 1; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes textReveal {
|
|
||||||
0% { opacity: 0; }
|
|
||||||
100% { opacity: 1; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes structureHighlight {
|
|
||||||
0%, 100% { stroke: #cbd5e0; stroke-width: 1; opacity: 0.8; }
|
|
||||||
50% { stroke: #3182ce; stroke-width: 2; opacity: 1; }
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</defs>
|
|
||||||
|
|
||||||
<!-- 背景 -->
|
|
||||||
<rect width="800" height="500" fill="white" rx="15" stroke="#e2e8f0" stroke-width="1"/>
|
|
||||||
|
|
||||||
<!-- 标题 -->
|
|
||||||
<text x="400" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="22" font-weight="bold" fill="#2c3e50">
|
|
||||||
SoA vs AoS 数据结构对比
|
|
||||||
</text>
|
|
||||||
|
|
||||||
<text x="400" y="50" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" fill="#666">
|
|
||||||
Structure of Arrays vs Array of Structures
|
|
||||||
</text>
|
|
||||||
|
|
||||||
<!-- AoS 部分 (左侧) -->
|
|
||||||
<g id="aos-section">
|
|
||||||
<rect x="50" y="80" width="320" height="180" rx="10" fill="url(#aosGradient)" opacity="0.9"/>
|
|
||||||
|
|
||||||
<!-- AoS 标题 -->
|
|
||||||
<text x="210" y="105" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="bold" fill="white">
|
|
||||||
AoS - Array of Structures
|
|
||||||
</text>
|
|
||||||
<text x="210" y="125" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="white">
|
|
||||||
结构体数组(传统方式)
|
|
||||||
</text>
|
|
||||||
|
|
||||||
<!-- AoS 数据结构示例 -->
|
|
||||||
<rect x="70" y="140" width="280" height="100" rx="5" fill="rgba(255,255,255,0.2)" stroke="rgba(255,255,255,0.4)" stroke-width="1"/>
|
|
||||||
|
|
||||||
<!-- Entity 0 -->
|
|
||||||
<rect x="85" y="155" width="70" height="15" rx="2" fill="rgba(255,255,255,0.3)" class="structure-highlight"/>
|
|
||||||
<text x="120" y="166" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="white">Entity[0]</text>
|
|
||||||
|
|
||||||
<rect x="85" y="175" width="15" height="8" rx="1" fill="#ff6b6b" class="data-flow"/>
|
|
||||||
<text x="92" y="181" text-anchor="middle" font-family="Arial, sans-serif" font-size="6" fill="white">x</text>
|
|
||||||
|
|
||||||
<rect x="105" y="175" width="15" height="8" rx="1" fill="#4ecdc4" class="data-flow" style="animation-delay: 0.5s"/>
|
|
||||||
<text x="112" y="181" text-anchor="middle" font-family="Arial, sans-serif" font-size="6" fill="white">y</text>
|
|
||||||
|
|
||||||
<rect x="125" y="175" width="15" height="8" rx="1" fill="#45b7d1" class="data-flow" style="animation-delay: 1s"/>
|
|
||||||
<text x="132" y="181" text-anchor="middle" font-family="Arial, sans-serif" font-size="6" fill="white">hp</text>
|
|
||||||
|
|
||||||
<rect x="145" y="175" width="10" height="8" rx="1" fill="#96ceb4" class="data-flow" style="animation-delay: 1.5s"/>
|
|
||||||
<text x="150" y="181" text-anchor="middle" font-family="Arial, sans-serif" font-size="6" fill="white">id</text>
|
|
||||||
|
|
||||||
<!-- Entity 1 -->
|
|
||||||
<rect x="170" y="155" width="70" height="15" rx="2" fill="rgba(255,255,255,0.3)" class="structure-highlight" style="animation-delay: 1s"/>
|
|
||||||
<text x="205" y="166" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="white">Entity[1]</text>
|
|
||||||
|
|
||||||
<rect x="170" y="175" width="15" height="8" rx="1" fill="#ff6b6b" class="data-flow" style="animation-delay: 2s"/>
|
|
||||||
<rect x="190" y="175" width="15" height="8" rx="1" fill="#4ecdc4" class="data-flow" style="animation-delay: 2.5s"/>
|
|
||||||
<rect x="210" y="175" width="15" height="8" rx="1" fill="#45b7d1" class="data-flow" style="animation-delay: 3s"/>
|
|
||||||
<rect x="230" y="175" width="10" height="8" rx="1" fill="#96ceb4" class="data-flow" style="animation-delay: 3.5s"/>
|
|
||||||
|
|
||||||
<!-- Entity 2 -->
|
|
||||||
<rect x="255" y="155" width="70" height="15" rx="2" fill="rgba(255,255,255,0.3)" class="structure-highlight" style="animation-delay: 2s"/>
|
|
||||||
<text x="290" y="166" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="white">Entity[2]</text>
|
|
||||||
|
|
||||||
<rect x="255" y="175" width="15" height="8" rx="1" fill="#ff6b6b" class="data-flow" style="animation-delay: 4s"/>
|
|
||||||
<rect x="275" y="175" width="15" height="8" rx="1" fill="#4ecdc4" class="data-flow" style="animation-delay: 4.5s"/>
|
|
||||||
<rect x="295" y="175" width="15" height="8" rx="1" fill="#45b7d1" class="data-flow" style="animation-delay: 5s"/>
|
|
||||||
<rect x="315" y="175" width="10" height="8" rx="1" fill="#96ceb4" class="data-flow" style="animation-delay: 5.5s"/>
|
|
||||||
|
|
||||||
<!-- 内存访问模式 -->
|
|
||||||
<text x="210" y="205" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="white">
|
|
||||||
内存访问:跳跃式访问,缓存不友好
|
|
||||||
</text>
|
|
||||||
|
|
||||||
<path d="M 92 195 Q 112 210 132 195 Q 152 210 177 195" stroke="white" stroke-width="1" fill="none" stroke-dasharray="3,2"/>
|
|
||||||
<text x="135" y="225" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="white">
|
|
||||||
处理位置时需跳过其他数据
|
|
||||||
</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- SoA 部分 (右侧) -->
|
|
||||||
<g id="soa-section">
|
|
||||||
<rect x="430" y="80" width="320" height="180" rx="10" fill="url(#soaGradient)" opacity="0.9"/>
|
|
||||||
|
|
||||||
<!-- SoA 标题 -->
|
|
||||||
<text x="590" y="105" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="bold" fill="white">
|
|
||||||
SoA - Structure of Arrays
|
|
||||||
</text>
|
|
||||||
<text x="590" y="125" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="white">
|
|
||||||
数组结构(ECS优化方式)
|
|
||||||
</text>
|
|
||||||
|
|
||||||
<!-- SoA 数据结构示例 -->
|
|
||||||
<rect x="450" y="140" width="280" height="100" rx="5" fill="rgba(255,255,255,0.2)" stroke="rgba(255,255,255,0.4)" stroke-width="1"/>
|
|
||||||
|
|
||||||
<!-- Position Array -->
|
|
||||||
<text x="460" y="155" font-family="Arial, sans-serif" font-size="8" fill="white">Position[]:</text>
|
|
||||||
<rect x="515" y="145" width="20" height="10" rx="1" fill="#ff6b6b" class="data-flow"/>
|
|
||||||
<rect x="540" y="145" width="20" height="10" rx="1" fill="#ff6b6b" class="data-flow" style="animation-delay: 0.3s"/>
|
|
||||||
<rect x="565" y="145" width="20" height="10" rx="1" fill="#ff6b6b" class="data-flow" style="animation-delay: 0.6s"/>
|
|
||||||
<rect x="590" y="145" width="20" height="10" rx="1" fill="#ff6b6b" class="data-flow" style="animation-delay: 0.9s"/>
|
|
||||||
<text x="625" y="153" font-family="Arial, sans-serif" font-size="8" fill="white">连续存储</text>
|
|
||||||
|
|
||||||
<!-- Velocity Array -->
|
|
||||||
<text x="460" y="170" font-family="Arial, sans-serif" font-size="8" fill="white">Velocity[]:</text>
|
|
||||||
<rect x="515" y="160" width="20" height="10" rx="1" fill="#4ecdc4" class="data-flow" style="animation-delay: 1s"/>
|
|
||||||
<rect x="540" y="160" width="20" height="10" rx="1" fill="#4ecdc4" class="data-flow" style="animation-delay: 1.3s"/>
|
|
||||||
<rect x="565" y="160" width="20" height="10" rx="1" fill="#4ecdc4" class="data-flow" style="animation-delay: 1.6s"/>
|
|
||||||
<rect x="590" y="160" width="20" height="10" rx="1" fill="#4ecdc4" class="data-flow" style="animation-delay: 1.9s"/>
|
|
||||||
|
|
||||||
<!-- Health Array -->
|
|
||||||
<text x="460" y="185" font-family="Arial, sans-serif" font-size="8" fill="white">Health[]:</text>
|
|
||||||
<rect x="515" y="175" width="20" height="10" rx="1" fill="#45b7d1" class="data-flow" style="animation-delay: 2s"/>
|
|
||||||
<rect x="540" y="175" width="20" height="10" rx="1" fill="#45b7d1" class="data-flow" style="animation-delay: 2.3s"/>
|
|
||||||
<rect x="565" y="175" width="20" height="10" rx="1" fill="#45b7d1" class="data-flow" style="animation-delay: 2.6s"/>
|
|
||||||
<rect x="590" y="175" width="20" height="10" rx="1" fill="#45b7d1" class="data-flow" style="animation-delay: 2.9s"/>
|
|
||||||
|
|
||||||
<!-- ID Array -->
|
|
||||||
<text x="460" y="200" font-family="Arial, sans-serif" font-size="8" fill="white">EntityID[]:</text>
|
|
||||||
<rect x="515" y="190" width="15" height="10" rx="1" fill="#96ceb4" class="data-flow" style="animation-delay: 3s"/>
|
|
||||||
<rect x="535" y="190" width="15" height="10" rx="1" fill="#96ceb4" class="data-flow" style="animation-delay: 3.3s"/>
|
|
||||||
<rect x="555" y="190" width="15" height="10" rx="1" fill="#96ceb4" class="data-flow" style="animation-delay: 3.6s"/>
|
|
||||||
<rect x="575" y="190" width="15" height="10" rx="1" fill="#96ceb4" class="data-flow" style="animation-delay: 3.9s"/>
|
|
||||||
|
|
||||||
<!-- 内存访问模式 -->
|
|
||||||
<text x="590" y="220" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="white">
|
|
||||||
内存访问:连续访问,缓存友好
|
|
||||||
</text>
|
|
||||||
|
|
||||||
<path d="M 525 205 L 570 205" stroke="white" stroke-width="2" fill="none" class="data-flow"/>
|
|
||||||
<text x="590" y="235" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="white">
|
|
||||||
处理位置时连续访问相同类型数据
|
|
||||||
</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- 性能对比区域 -->
|
|
||||||
<g id="performance-comparison">
|
|
||||||
<rect x="50" y="280" width="700" height="150" rx="10" fill="rgba(248, 249, 250, 0.9)" stroke="#e2e8f0" stroke-width="1"/>
|
|
||||||
|
|
||||||
<text x="400" y="305" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="#2c3e50">
|
|
||||||
性能对比分析
|
|
||||||
</text>
|
|
||||||
|
|
||||||
<!-- 缓存性能对比 -->
|
|
||||||
<g id="cache-performance">
|
|
||||||
<text x="80" y="330" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#4a5568">
|
|
||||||
缓存命中率:
|
|
||||||
</text>
|
|
||||||
|
|
||||||
<!-- AoS 缓存性能 -->
|
|
||||||
<text x="80" y="350" font-family="Arial, sans-serif" font-size="11" fill="#666">AoS:</text>
|
|
||||||
<rect x="120" y="342" width="100" height="12" rx="6" fill="#f7fafc" stroke="#e2e8f0" stroke-width="1"/>
|
|
||||||
<rect x="120" y="342" width="35" height="12" rx="6" fill="url(#aosGradient)" class="performance-bar"/>
|
|
||||||
<text x="230" y="351" font-family="Arial, sans-serif" font-size="10" fill="#666">35%</text>
|
|
||||||
|
|
||||||
<!-- SoA 缓存性能 -->
|
|
||||||
<text x="80" y="370" font-family="Arial, sans-serif" font-size="11" fill="#666">SoA:</text>
|
|
||||||
<rect x="120" y="362" width="100" height="12" rx="6" fill="#f7fafc" stroke="#e2e8f0" stroke-width="1"/>
|
|
||||||
<rect x="120" y="362" width="85" height="12" rx="6" fill="url(#soaGradient)" class="performance-bar" style="animation-delay: 0.5s"/>
|
|
||||||
<text x="230" y="371" font-family="Arial, sans-serif" font-size="10" fill="#666">85%</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- 处理速度对比 -->
|
|
||||||
<g id="processing-speed">
|
|
||||||
<text x="320" y="330" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#4a5568">
|
|
||||||
批量处理速度:
|
|
||||||
</text>
|
|
||||||
|
|
||||||
<!-- AoS 处理速度 -->
|
|
||||||
<text x="320" y="350" font-family="Arial, sans-serif" font-size="11" fill="#666">AoS:</text>
|
|
||||||
<rect x="360" y="342" width="120" height="12" rx="6" fill="#f7fafc" stroke="#e2e8f0" stroke-width="1"/>
|
|
||||||
<rect x="360" y="342" width="48" height="12" rx="6" fill="url(#aosGradient)" class="performance-bar" style="animation-delay: 1s"/>
|
|
||||||
<text x="490" y="351" font-family="Arial, sans-serif" font-size="10" fill="#666">2.3x slower</text>
|
|
||||||
|
|
||||||
<!-- SoA 处理速度 -->
|
|
||||||
<text x="320" y="370" font-family="Arial, sans-serif" font-size="11" fill="#666">SoA:</text>
|
|
||||||
<rect x="360" y="362" width="120" height="12" rx="6" fill="#f7fafc" stroke="#e2e8f0" stroke-width="1"/>
|
|
||||||
<rect x="360" y="362" width="120" height="12" rx="6" fill="url(#soaGradient)" class="performance-bar" style="animation-delay: 1.5s"/>
|
|
||||||
<text x="490" y="371" font-family="Arial, sans-serif" font-size="10" fill="#666">baseline</text>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- 使用场景 -->
|
|
||||||
<g id="use-cases">
|
|
||||||
<text x="560" y="330" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#4a5568">
|
|
||||||
适用场景:
|
|
||||||
</text>
|
|
||||||
|
|
||||||
<text x="560" y="350" font-family="Arial, sans-serif" font-size="10" fill="#666">
|
|
||||||
✅ SoA: 大量实体的同类型操作
|
|
||||||
</text>
|
|
||||||
<text x="560" y="365" font-family="Arial, sans-serif" font-size="10" fill="#666">
|
|
||||||
✅ SoA: 游戏循环中的系统处理
|
|
||||||
</text>
|
|
||||||
<text x="560" y="380" font-family="Arial, sans-serif" font-size="10" fill="#666">
|
|
||||||
❌ AoS: 混合操作、少量实体
|
|
||||||
</text>
|
|
||||||
<text x="560" y="395" font-family="Arial, sans-serif" font-size="10" fill="#666">
|
|
||||||
❌ AoS: 随机访问模式
|
|
||||||
</text>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- ECS 框架优势说明 -->
|
|
||||||
<g id="ecs-advantage">
|
|
||||||
<rect x="50" y="450" width="700" height="40" rx="8" fill="rgba(67, 233, 123, 0.1)" stroke="#43e97b" stroke-width="1"/>
|
|
||||||
<text x="400" y="468" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" fill="#2d3748">
|
|
||||||
🚀 本框架采用 SoA 优化存储,@EnableSoA 装饰器自动转换,性能提升 2-3 倍
|
|
||||||
</text>
|
|
||||||
<text x="400" y="485" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="#4a5568">
|
|
||||||
支持热切换存储方式,开发时使用 AoS 调试,生产环境自动启用 SoA 优化
|
|
||||||
</text>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 14 KiB |
@@ -14,7 +14,7 @@ ECS 架构将传统的面向对象设计分解为三个核心部分:
|
|||||||
|
|
||||||
## Core(核心)
|
## Core(核心)
|
||||||
|
|
||||||
Core 是框架的核心管理类,负责游戏的生命周期管理。
|
Core 是框架的核心管理类,负责游戏的生命周期管理。框架采用融合设计,既支持传统的单Scene模式(向后兼容),也支持高级的多World/多Scene架构。
|
||||||
|
|
||||||
### 创建和配置
|
### 创建和配置
|
||||||
|
|
||||||
@@ -47,6 +47,40 @@ const core2 = Core.create(false); // 发布模式
|
|||||||
const core3 = Core.create(); // 默认调试模式
|
const core3 = Core.create(); // 默认调试模式
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 场景管理API
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 单Scene模式(默认,向后兼容)
|
||||||
|
const scene = new Scene();
|
||||||
|
Core.setScene(scene); // 设置场景
|
||||||
|
const currentScene = Core.getScene(); // 获取当前场景
|
||||||
|
|
||||||
|
// 多World模式(高级功能)
|
||||||
|
Core.enableWorldManager(); // 启用World管理器
|
||||||
|
const worldManager = Core.getWorldManager();
|
||||||
|
|
||||||
|
// 创建World
|
||||||
|
const gameWorld = worldManager.createWorld('GameWorld', {
|
||||||
|
name: 'GameWorld',
|
||||||
|
maxScenes: 10,
|
||||||
|
autoCleanup: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// 在World中管理Scene
|
||||||
|
const battleScene = gameWorld.createScene('battle', new Scene());
|
||||||
|
const uiScene = gameWorld.createScene('ui', new Scene());
|
||||||
|
|
||||||
|
gameWorld.setSceneActive('battle', true);
|
||||||
|
gameWorld.setSceneActive('ui', true);
|
||||||
|
|
||||||
|
// 启动World
|
||||||
|
gameWorld.start();
|
||||||
|
|
||||||
|
// 获取World统计
|
||||||
|
const worldStats = gameWorld.getStats();
|
||||||
|
console.log('World状态:', worldStats);
|
||||||
|
```
|
||||||
|
|
||||||
### 事件系统
|
### 事件系统
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
@@ -404,6 +438,235 @@ console.log("实体数量:", stats.entityCount);
|
|||||||
console.log("系统数量:", stats.processorCount);
|
console.log("系统数量:", stats.processorCount);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## World(世界)
|
||||||
|
|
||||||
|
World是Scene的容器,提供了更高级的场景管理功能。每个World可以包含多个Scene,适用于复杂的游戏架构。
|
||||||
|
|
||||||
|
### World基本使用
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { World, Scene, IWorldConfig } from '@esengine/ecs-framework';
|
||||||
|
|
||||||
|
// 创建World配置
|
||||||
|
const worldConfig: IWorldConfig = {
|
||||||
|
name: 'GameWorld',
|
||||||
|
debug: true,
|
||||||
|
maxScenes: 10,
|
||||||
|
autoCleanup: true
|
||||||
|
};
|
||||||
|
|
||||||
|
// 创建World
|
||||||
|
const gameWorld = new World(worldConfig);
|
||||||
|
|
||||||
|
// 在World中创建Scene
|
||||||
|
const battleScene = gameWorld.createScene('battle', new Scene());
|
||||||
|
const uiScene = gameWorld.createScene('ui', new Scene());
|
||||||
|
const menuScene = gameWorld.createScene('menu');
|
||||||
|
|
||||||
|
// 激活Scene
|
||||||
|
gameWorld.setSceneActive('battle', true);
|
||||||
|
gameWorld.setSceneActive('ui', true);
|
||||||
|
|
||||||
|
// 启动World
|
||||||
|
gameWorld.start();
|
||||||
|
```
|
||||||
|
|
||||||
|
### World生命周期管理
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 启动World(启动所有全局System)
|
||||||
|
gameWorld.start();
|
||||||
|
|
||||||
|
// 检查World状态
|
||||||
|
if (gameWorld.isActive) {
|
||||||
|
console.log('World正在运行');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 停止World(停止所有Scene和全局System)
|
||||||
|
gameWorld.stop();
|
||||||
|
|
||||||
|
// 销毁World(清理所有资源)
|
||||||
|
gameWorld.destroy();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Scene管理
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 获取Scene
|
||||||
|
const battleScene = gameWorld.getScene<Scene>('battle');
|
||||||
|
|
||||||
|
// 检查Scene是否激活
|
||||||
|
if (gameWorld.isSceneActive('battle')) {
|
||||||
|
console.log('战斗场景正在运行');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移除Scene
|
||||||
|
gameWorld.removeScene('menu');
|
||||||
|
|
||||||
|
// 获取所有Scene ID
|
||||||
|
const sceneIds = gameWorld.getSceneIds();
|
||||||
|
console.log('所有Scene:', sceneIds);
|
||||||
|
|
||||||
|
// 获取活跃Scene数量
|
||||||
|
const activeCount = gameWorld.getActiveSceneCount();
|
||||||
|
console.log('活跃Scene数量:', activeCount);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 全局System管理
|
||||||
|
|
||||||
|
World支持全局System,这些System会在所有Scene之前执行,适用于跨Scene的业务逻辑:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { IGlobalSystem } from '@esengine/ecs-framework';
|
||||||
|
|
||||||
|
// 全局网络同步系统
|
||||||
|
class GlobalNetworkSystem implements IGlobalSystem {
|
||||||
|
public readonly name = 'GlobalNetworkSystem';
|
||||||
|
|
||||||
|
public initialize(): void {
|
||||||
|
// 初始化网络连接
|
||||||
|
console.log('网络系统初始化');
|
||||||
|
}
|
||||||
|
|
||||||
|
public update(): void {
|
||||||
|
// 处理全局网络同步逻辑
|
||||||
|
// 注意:全局系统处理的是World级别的逻辑,不直接处理实体
|
||||||
|
// 如需处理特定实体,请在Scene中使用EntitySystem
|
||||||
|
this.syncGlobalNetworkState();
|
||||||
|
}
|
||||||
|
|
||||||
|
public reset(): void {
|
||||||
|
// 重置系统状态
|
||||||
|
}
|
||||||
|
|
||||||
|
public destroy(): void {
|
||||||
|
// 清理网络连接
|
||||||
|
console.log('网络系统销毁');
|
||||||
|
}
|
||||||
|
|
||||||
|
private syncGlobalNetworkState(): void {
|
||||||
|
// 全局网络状态同步
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加全局System
|
||||||
|
const networkSystem = gameWorld.addGlobalSystem(new GlobalNetworkSystem());
|
||||||
|
|
||||||
|
// 获取全局System
|
||||||
|
const existingSystem = gameWorld.getGlobalSystem(GlobalNetworkSystem);
|
||||||
|
|
||||||
|
// 移除全局System
|
||||||
|
gameWorld.removeGlobalSystem(networkSystem);
|
||||||
|
```
|
||||||
|
|
||||||
|
> **注意**:全局System适用于World级别的业务逻辑(如网络管理、资源管理、全局状态管理等)。如果需要处理具体的实体和组件,请在Scene中使用EntitySystem。
|
||||||
|
|
||||||
|
### World状态监控
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 获取World状态
|
||||||
|
const status = gameWorld.getStatus();
|
||||||
|
console.log('World状态:', {
|
||||||
|
name: status.name,
|
||||||
|
isActive: status.isActive,
|
||||||
|
sceneCount: status.sceneCount,
|
||||||
|
activeSceneCount: status.activeSceneCount,
|
||||||
|
globalSystemCount: status.globalSystemCount,
|
||||||
|
scenes: status.scenes
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取World统计信息
|
||||||
|
const stats = gameWorld.getStats();
|
||||||
|
console.log('World统计:', {
|
||||||
|
totalEntities: stats.totalEntities,
|
||||||
|
totalSystems: stats.totalSystems,
|
||||||
|
memoryUsage: stats.memoryUsage
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## WorldManager(世界管理器)
|
||||||
|
|
||||||
|
WorldManager是单例模式的World管理器,负责管理多个World实例。
|
||||||
|
|
||||||
|
### WorldManager基本使用
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { WorldManager, IWorldManagerConfig } from '@esengine/ecs-framework';
|
||||||
|
|
||||||
|
// 获取WorldManager实例
|
||||||
|
const worldManager = WorldManager.getInstance({
|
||||||
|
maxWorlds: 50,
|
||||||
|
autoCleanup: true,
|
||||||
|
debug: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// 或者通过Core获取
|
||||||
|
Core.enableWorldManager();
|
||||||
|
const worldManager2 = Core.getWorldManager();
|
||||||
|
```
|
||||||
|
|
||||||
|
### World管理
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 创建World
|
||||||
|
const gameWorld = worldManager.createWorld('GameRoom_001', {
|
||||||
|
name: 'GameRoom_001',
|
||||||
|
maxScenes: 5,
|
||||||
|
autoCleanup: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取World
|
||||||
|
const existingWorld = worldManager.getWorld('GameRoom_001');
|
||||||
|
|
||||||
|
// 检查World是否存在
|
||||||
|
if (worldManager.getWorld('GameRoom_001')) {
|
||||||
|
console.log('World存在');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 销毁World
|
||||||
|
worldManager.removeWorld('GameRoom_001');
|
||||||
|
|
||||||
|
// 获取所有World ID
|
||||||
|
const worldIds = worldManager.getWorldIds();
|
||||||
|
console.log('所有World ID:', worldIds);
|
||||||
|
|
||||||
|
// 获取活跃World
|
||||||
|
const activeWorlds = worldManager.getActiveWorlds();
|
||||||
|
console.log('活跃World数量:', activeWorlds.length);
|
||||||
|
```
|
||||||
|
|
||||||
|
### WorldManager统计和监控
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 获取WorldManager状态
|
||||||
|
const managerStatus = worldManager.getStatus();
|
||||||
|
console.log('WorldManager状态:', {
|
||||||
|
totalWorlds: managerStatus.totalWorlds,
|
||||||
|
activeWorlds: managerStatus.activeWorlds,
|
||||||
|
maxWorlds: managerStatus.maxWorlds,
|
||||||
|
memoryUsage: managerStatus.memoryUsage
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取所有World的统计
|
||||||
|
const allStats = worldManager.getAllWorldStats();
|
||||||
|
allStats.forEach(stat => {
|
||||||
|
console.log(`World ${stat.worldName}:`, stat);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 清理空闲World
|
||||||
|
const cleanedCount = worldManager.cleanup();
|
||||||
|
console.log(`清理了 ${cleanedCount} 个空闲World`);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 使用场景
|
||||||
|
|
||||||
|
World和WorldManager适用于:
|
||||||
|
- **游戏服务器**:每个房间一个独立World
|
||||||
|
- **复杂客户端**:按功能分层管理Scene(游戏层、UI层、特效层)
|
||||||
|
- **并发世界**:需要同时运行多个独立游戏世界的场景
|
||||||
|
|
||||||
|
> **完整示例和最佳实践**:查看 [场景管理完整指南](scene-management-guide.md#world多场景管理) 了解详细的实现方案和架构设计
|
||||||
|
|
||||||
## System(系统)
|
## System(系统)
|
||||||
|
|
||||||
系统处理实体集合,实现游戏的核心逻辑。
|
系统处理实体集合,实现游戏的核心逻辑。
|
||||||
@@ -646,11 +909,18 @@ const movingEntities = scene.querySystem.queryAll(PositionComponent, VelocityCom
|
|||||||
|
|
||||||
ECS Framework 提供了完整的实体组件系统架构:
|
ECS Framework 提供了完整的实体组件系统架构:
|
||||||
|
|
||||||
- **Core** 管理游戏生命周期和全局功能
|
- **Core** 管理游戏生命周期和全局功能,支持单Scene和多World模式
|
||||||
- **Entity** 作为游戏对象的基础容器
|
- **Entity** 作为游戏对象的基础容器
|
||||||
- **Component** 实现具体的功能模块,支持对象池优化
|
- **Component** 实现具体的功能模块,支持对象池优化
|
||||||
- **System** 处理游戏逻辑
|
- **System** 处理游戏逻辑
|
||||||
- **Scene** 管理游戏世界状态,支持批量操作
|
- **Scene** 管理游戏世界状态,支持批量操作
|
||||||
|
- **World** 高级场景容器,支持多Scene管理和全局System
|
||||||
|
- **WorldManager** 管理多个World实例,适用于复杂架构
|
||||||
- **高级优化** 位掩码优化器、组件对象池、批量操作等
|
- **高级优化** 位掩码优化器、组件对象池、批量操作等
|
||||||
|
|
||||||
通过合理使用这些核心概念和优化功能,可以构建出高性能、结构清晰、易于维护的游戏代码。
|
### 架构选择指南
|
||||||
|
|
||||||
|
- **单Scene模式**:适合简单游戏、单机游戏、原型开发
|
||||||
|
- **多World模式**:适合多人游戏服务器、复杂应用、需要场景隔离的项目
|
||||||
|
|
||||||
|
框架采用融合设计,确保向后兼容性的同时提供强大的扩展能力。通过合理使用这些核心概念和优化功能,可以构建出高性能、结构清晰、易于维护的游戏代码。
|
||||||
@@ -33,7 +33,7 @@ Core.update(deltaTime);
|
|||||||
|
|
||||||
### 基础配置
|
### 基础配置
|
||||||
|
|
||||||
ECS框架提供了灵活的配置选项来满足不同项目需求:
|
ECS框架提供了灵活的配置选项来满足不同项目需求。框架采用融合设计,既支持传统的单Scene模式(向后兼容),也支持高级的多World/多Scene架构:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { Core, ICoreConfig } from '@esengine/ecs-framework';
|
import { Core, ICoreConfig } from '@esengine/ecs-framework';
|
||||||
@@ -65,6 +65,31 @@ const config: ICoreConfig = {
|
|||||||
const core = Core.create(config);
|
const core = Core.create(config);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 架构说明
|
||||||
|
|
||||||
|
ECS框架支持两种使用模式:
|
||||||
|
|
||||||
|
1. **单Scene模式(默认,向后兼容)**:
|
||||||
|
```typescript
|
||||||
|
// 传统用法,无需任何修改
|
||||||
|
const scene = new Scene();
|
||||||
|
Core.setScene(scene);
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **多World模式(高级功能)**:
|
||||||
|
```typescript
|
||||||
|
// 启用World管理器,支持多World/多Scene架构
|
||||||
|
Core.enableWorldManager();
|
||||||
|
const roomWorld = Core.getWorldManager().createWorld('Room_001');
|
||||||
|
const battleScene = roomWorld.createScene('battle');
|
||||||
|
```
|
||||||
|
|
||||||
|
**使用场景:**
|
||||||
|
- **单Scene模式**:适合简单游戏、单机游戏、原型开发
|
||||||
|
- **多World模式**:适合多人游戏服务器、复杂应用、需要场景隔离的项目
|
||||||
|
|
||||||
|
> **详细了解World系统**:查看 [场景管理完整指南](scene-management-guide.md) 获取完整的多World架构示例和最佳实践
|
||||||
|
|
||||||
### 调试功能
|
### 调试功能
|
||||||
|
|
||||||
ECS框架内置了强大的调试功能,支持运行时监控和远程调试:
|
ECS框架内置了强大的调试功能,支持运行时监控和远程调试:
|
||||||
@@ -324,7 +349,7 @@ class CocosRenderSystem extends EntitySystem {
|
|||||||
### Node.js后端
|
### Node.js后端
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { Core, Scene, EntityManager, EntitySystem, Time } from '@esengine/ecs-framework';
|
import { Core, Scene, EntityManager, EntitySystem, Time, World } from '@esengine/ecs-framework';
|
||||||
|
|
||||||
class ServerGameManager {
|
class ServerGameManager {
|
||||||
private scene: Scene;
|
private scene: Scene;
|
||||||
@@ -338,6 +363,7 @@ class ServerGameManager {
|
|||||||
Core.create(true); // 启用调试模式
|
Core.create(true); // 启用调试模式
|
||||||
// 完整配置示例: Core.create({ debug: true, enableEntitySystems: true, debugConfig: {...} })
|
// 完整配置示例: Core.create({ debug: true, enableEntitySystems: true, debugConfig: {...} })
|
||||||
|
|
||||||
|
// 单Scene模式(简单场景)
|
||||||
this.scene = new Scene();
|
this.scene = new Scene();
|
||||||
this.scene.name = "ServerScene";
|
this.scene.name = "ServerScene";
|
||||||
Core.setScene(this.scene);
|
Core.setScene(this.scene);
|
||||||
@@ -418,6 +444,8 @@ const server = new ServerGameManager();
|
|||||||
server.start();
|
server.start();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> **多房间游戏服务器示例**:查看 [场景管理完整指南](scene-management-guide.md#world多场景管理) 了解如何使用多World架构实现复杂的多房间游戏服务器
|
||||||
|
|
||||||
### 原生浏览器
|
### 原生浏览器
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# 场景管理完整指南
|
# 场景管理完整指南
|
||||||
|
|
||||||
场景(Scene)是ECS框架中管理游戏对象和系统的核心容器。本指南将详细介绍如何有效地使用场景来构建和管理你的游戏。
|
场景(Scene)是ECS框架中管理游戏对象和系统的核心容器。框架采用融合设计,既支持传统的单Scene模式(向后兼容),也支持高级的多World/多Scene架构。本指南将详细介绍如何有效地使用场景来构建和管理你的游戏。
|
||||||
|
|
||||||
## 场景基础概念
|
## 场景基础概念
|
||||||
|
|
||||||
@@ -25,6 +25,47 @@ Core.setScene(gameScene);
|
|||||||
|
|
||||||
> **注意**: `Core.scene = ` 设置方式已被标记为废弃,推荐使用 `Core.setScene()` 方法。新方法提供更好的类型安全性和可预测的激活时序。
|
> **注意**: `Core.scene = ` 设置方式已被标记为废弃,推荐使用 `Core.setScene()` 方法。新方法提供更好的类型安全性和可预测的激活时序。
|
||||||
|
|
||||||
|
### 架构选择指南
|
||||||
|
|
||||||
|
ECS框架提供两种使用模式:
|
||||||
|
|
||||||
|
#### 1. 单Scene模式(默认,向后兼容)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 传统用法,无需任何修改
|
||||||
|
const scene = new Scene();
|
||||||
|
Core.setScene(scene);
|
||||||
|
```
|
||||||
|
|
||||||
|
**适用场景:**
|
||||||
|
- 简单游戏、单机游戏
|
||||||
|
- 原型开发、快速验证
|
||||||
|
- 学习ECS架构
|
||||||
|
- 不需要复杂场景管理的项目
|
||||||
|
|
||||||
|
#### 2. 多World模式(高级功能)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 启用World管理器
|
||||||
|
Core.enableWorldManager();
|
||||||
|
const worldManager = Core.getWorldManager();
|
||||||
|
|
||||||
|
// 创建多个World,每个World可包含多个Scene
|
||||||
|
const roomWorld = worldManager.createWorld('Room_001');
|
||||||
|
const battleScene = roomWorld.createScene('battle');
|
||||||
|
const uiScene = roomWorld.createScene('ui');
|
||||||
|
|
||||||
|
roomWorld.start();
|
||||||
|
roomWorld.setSceneActive('battle', true);
|
||||||
|
roomWorld.setSceneActive('ui', true);
|
||||||
|
```
|
||||||
|
|
||||||
|
**适用场景:**
|
||||||
|
- 多人游戏服务器(每个房间一个World)
|
||||||
|
- 复杂应用架构(需要场景隔离)
|
||||||
|
- 需要并发处理多个游戏世界
|
||||||
|
- 高级场景管理需求
|
||||||
|
|
||||||
### 场景的生命周期
|
### 场景的生命周期
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
@@ -214,9 +255,374 @@ class GameScene extends Scene {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## 场景切换和管理
|
## World多场景管理
|
||||||
|
|
||||||
### 1. 场景管理器
|
### 1. World基础使用
|
||||||
|
|
||||||
|
对于需要复杂场景管理的项目,可以使用World系统:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { Core, World, Scene, WorldManager, IGlobalSystem } from '@esengine/ecs-framework';
|
||||||
|
|
||||||
|
// 定义全局系统(跨Scene的业务逻辑)
|
||||||
|
class NetworkSyncSystem implements IGlobalSystem {
|
||||||
|
public readonly name = 'NetworkSyncSystem';
|
||||||
|
|
||||||
|
public initialize(): void {
|
||||||
|
console.log('网络同步系统初始化');
|
||||||
|
}
|
||||||
|
|
||||||
|
public update(): void {
|
||||||
|
// 同步所有Scene的网络状态
|
||||||
|
this.syncNetworkData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public reset(): void {
|
||||||
|
// 重置网络连接
|
||||||
|
}
|
||||||
|
|
||||||
|
public destroy(): void {
|
||||||
|
console.log('网络同步系统销毁');
|
||||||
|
}
|
||||||
|
|
||||||
|
private syncNetworkData(): void {
|
||||||
|
// 网络数据同步逻辑
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PlayerManagementSystem implements IGlobalSystem {
|
||||||
|
public readonly name = 'PlayerManagementSystem';
|
||||||
|
|
||||||
|
public initialize(): void {
|
||||||
|
console.log('玩家管理系统初始化');
|
||||||
|
}
|
||||||
|
|
||||||
|
public update(): void {
|
||||||
|
// 管理跨Scene的玩家数据
|
||||||
|
this.managePlayerStates();
|
||||||
|
}
|
||||||
|
|
||||||
|
public reset(): void {
|
||||||
|
// 重置玩家状态
|
||||||
|
}
|
||||||
|
|
||||||
|
public destroy(): void {
|
||||||
|
console.log('玩家管理系统销毁');
|
||||||
|
}
|
||||||
|
|
||||||
|
private managePlayerStates(): void {
|
||||||
|
// 玩家状态管理逻辑
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启用World管理功能
|
||||||
|
Core.enableWorldManager();
|
||||||
|
const worldManager = Core.getWorldManager();
|
||||||
|
|
||||||
|
// 创建游戏房间World
|
||||||
|
const roomWorld = worldManager.createWorld('GameRoom_001', {
|
||||||
|
name: 'GameRoom_001',
|
||||||
|
maxScenes: 5,
|
||||||
|
autoCleanup: true,
|
||||||
|
debug: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// 在World中创建多个Scene
|
||||||
|
const gameScene = roomWorld.createScene('game', new GameScene());
|
||||||
|
const uiScene = roomWorld.createScene('ui', new UIScene());
|
||||||
|
const backgroundScene = roomWorld.createScene('background', new BackgroundScene());
|
||||||
|
|
||||||
|
// 添加全局系统(跨Scene的系统)
|
||||||
|
roomWorld.addGlobalSystem(new NetworkSyncSystem());
|
||||||
|
roomWorld.addGlobalSystem(new PlayerManagementSystem());
|
||||||
|
|
||||||
|
// 启动World并激活Scene
|
||||||
|
roomWorld.start();
|
||||||
|
roomWorld.setSceneActive('game', true);
|
||||||
|
roomWorld.setSceneActive('ui', true);
|
||||||
|
roomWorld.setSceneActive('background', true);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 多房间游戏服务器示例
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 房间管理系统
|
||||||
|
class RoomManagementSystem implements IGlobalSystem {
|
||||||
|
public readonly name = 'RoomManagementSystem';
|
||||||
|
private roomId: string;
|
||||||
|
|
||||||
|
constructor(roomId: string) {
|
||||||
|
this.roomId = roomId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public initialize(): void {
|
||||||
|
console.log(`房间管理系统初始化: ${this.roomId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public update(): void {
|
||||||
|
// 管理房间状态、玩家进出等
|
||||||
|
this.manageRoomState();
|
||||||
|
}
|
||||||
|
|
||||||
|
public reset(): void {
|
||||||
|
// 重置房间状态
|
||||||
|
}
|
||||||
|
|
||||||
|
public destroy(): void {
|
||||||
|
console.log(`房间管理系统销毁: ${this.roomId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
private manageRoomState(): void {
|
||||||
|
// 房间状态管理逻辑
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 玩家同步系统
|
||||||
|
class PlayerSyncSystem implements IGlobalSystem {
|
||||||
|
public readonly name = 'PlayerSyncSystem';
|
||||||
|
|
||||||
|
public initialize(): void {
|
||||||
|
console.log('玩家同步系统初始化');
|
||||||
|
}
|
||||||
|
|
||||||
|
public update(): void {
|
||||||
|
// 同步房间内所有玩家的状态
|
||||||
|
this.syncPlayerData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public reset(): void {
|
||||||
|
// 重置同步状态
|
||||||
|
}
|
||||||
|
|
||||||
|
public destroy(): void {
|
||||||
|
console.log('玩家同步系统销毁');
|
||||||
|
}
|
||||||
|
|
||||||
|
private syncPlayerData(): void {
|
||||||
|
// 玩家数据同步逻辑
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MultiRoomGameServer {
|
||||||
|
private worldManager: WorldManager;
|
||||||
|
private rooms: Map<string, World> = new Map();
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
Core.create({ debug: false });
|
||||||
|
Core.enableWorldManager();
|
||||||
|
this.worldManager = Core.getWorldManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建游戏房间
|
||||||
|
createRoom(roomId: string): World {
|
||||||
|
const roomWorld = this.worldManager.createWorld(`Room_${roomId}`, {
|
||||||
|
name: `GameRoom_${roomId}`,
|
||||||
|
maxScenes: 3,
|
||||||
|
autoCleanup: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// 房间内的Scene设置
|
||||||
|
const gameScene = roomWorld.createScene('game', new ServerGameScene());
|
||||||
|
const lobbyScene = roomWorld.createScene('lobby', new LobbyScene());
|
||||||
|
|
||||||
|
// 设置房间级的全局系统
|
||||||
|
roomWorld.addGlobalSystem(new RoomManagementSystem(roomId));
|
||||||
|
roomWorld.addGlobalSystem(new PlayerSyncSystem());
|
||||||
|
|
||||||
|
// 启动房间
|
||||||
|
roomWorld.start();
|
||||||
|
roomWorld.setSceneActive('lobby', true); // 默认激活大厅
|
||||||
|
|
||||||
|
this.rooms.set(roomId, roomWorld);
|
||||||
|
console.log(`创建房间: ${roomId}`);
|
||||||
|
|
||||||
|
return roomWorld;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始游戏
|
||||||
|
startGame(roomId: string): boolean {
|
||||||
|
const roomWorld = this.rooms.get(roomId);
|
||||||
|
if (!roomWorld) return false;
|
||||||
|
|
||||||
|
// 停用大厅,激活游戏Scene
|
||||||
|
roomWorld.setSceneActive('lobby', false);
|
||||||
|
roomWorld.setSceneActive('game', true);
|
||||||
|
|
||||||
|
console.log(`房间 ${roomId} 开始游戏`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 销毁房间
|
||||||
|
destroyRoom(roomId: string): boolean {
|
||||||
|
const roomWorld = this.rooms.get(roomId);
|
||||||
|
if (!roomWorld) return false;
|
||||||
|
|
||||||
|
roomWorld.destroy();
|
||||||
|
this.rooms.delete(roomId);
|
||||||
|
console.log(`销毁房间: ${roomId}`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取服务器状态
|
||||||
|
getServerStats() {
|
||||||
|
return {
|
||||||
|
totalRooms: this.rooms.size,
|
||||||
|
activeWorlds: this.worldManager.getActiveWorlds().length,
|
||||||
|
rooms: Array.from(this.rooms.keys()).map(roomId => ({
|
||||||
|
roomId,
|
||||||
|
world: this.rooms.get(roomId)?.getStatus()
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 游戏循环
|
||||||
|
start(): void {
|
||||||
|
const gameLoop = () => {
|
||||||
|
const deltaTime = 1000 / 60; // 60 TPS
|
||||||
|
Core.update(deltaTime / 1000);
|
||||||
|
setTimeout(gameLoop, deltaTime);
|
||||||
|
};
|
||||||
|
gameLoop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用示例
|
||||||
|
const gameServer = new MultiRoomGameServer();
|
||||||
|
gameServer.start();
|
||||||
|
|
||||||
|
// 创建房间
|
||||||
|
const room1 = gameServer.createRoom('room_001');
|
||||||
|
const room2 = gameServer.createRoom('room_002');
|
||||||
|
|
||||||
|
// 开始游戏
|
||||||
|
setTimeout(() => {
|
||||||
|
gameServer.startGame('room_001');
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
console.log('服务器状态:', gameServer.getServerStats());
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 客户端多Scene管理示例
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
class GameClient {
|
||||||
|
private worldManager: WorldManager;
|
||||||
|
private mainWorld: World;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
Core.create({ debug: true });
|
||||||
|
Core.enableWorldManager();
|
||||||
|
this.worldManager = Core.getWorldManager();
|
||||||
|
|
||||||
|
this.setupGameWorld();
|
||||||
|
}
|
||||||
|
|
||||||
|
private setupGameWorld(): void {
|
||||||
|
// 创建主游戏世界
|
||||||
|
this.mainWorld = this.worldManager.createWorld('MainWorld', {
|
||||||
|
name: 'ClientWorld',
|
||||||
|
maxScenes: 10,
|
||||||
|
autoCleanup: false // 客户端通常不需要自动清理
|
||||||
|
});
|
||||||
|
|
||||||
|
// 创建不同层级的Scene
|
||||||
|
this.createGameplayScenes();
|
||||||
|
this.createUIScenes();
|
||||||
|
this.createEffectScenes();
|
||||||
|
|
||||||
|
// 启动世界
|
||||||
|
this.mainWorld.start();
|
||||||
|
this.activateDefaultScenes();
|
||||||
|
}
|
||||||
|
|
||||||
|
private createGameplayScenes(): void {
|
||||||
|
// 游戏主场景
|
||||||
|
const gameScene = this.mainWorld.createScene('gameplay', new GameplayScene());
|
||||||
|
|
||||||
|
// 背景场景
|
||||||
|
const backgroundScene = this.mainWorld.createScene('background', new BackgroundScene());
|
||||||
|
|
||||||
|
// 特效场景
|
||||||
|
const effectsScene = this.mainWorld.createScene('effects', new EffectsScene());
|
||||||
|
}
|
||||||
|
|
||||||
|
private createUIScenes(): void {
|
||||||
|
// 主UI场景
|
||||||
|
const mainUIScene = this.mainWorld.createScene('mainUI', new MainUIScene());
|
||||||
|
|
||||||
|
// 菜单场景
|
||||||
|
const menuScene = this.mainWorld.createScene('menu', new MenuScene());
|
||||||
|
|
||||||
|
// 设置场景
|
||||||
|
const settingsScene = this.mainWorld.createScene('settings', new SettingsScene());
|
||||||
|
}
|
||||||
|
|
||||||
|
private createEffectScenes(): void {
|
||||||
|
// 粒子效果场景
|
||||||
|
const particleScene = this.mainWorld.createScene('particles', new ParticleScene());
|
||||||
|
|
||||||
|
// 音效场景
|
||||||
|
const audioScene = this.mainWorld.createScene('audio', new AudioScene());
|
||||||
|
}
|
||||||
|
|
||||||
|
private activateDefaultScenes(): void {
|
||||||
|
// 激活基础Scene
|
||||||
|
this.mainWorld.setSceneActive('background', true);
|
||||||
|
this.mainWorld.setSceneActive('gameplay', true);
|
||||||
|
this.mainWorld.setSceneActive('mainUI', true);
|
||||||
|
this.mainWorld.setSceneActive('particles', true);
|
||||||
|
this.mainWorld.setSceneActive('audio', true);
|
||||||
|
|
||||||
|
// 菜单和设置默认不激活
|
||||||
|
this.mainWorld.setSceneActive('menu', false);
|
||||||
|
this.mainWorld.setSceneActive('settings', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换到菜单
|
||||||
|
showMenu(): void {
|
||||||
|
this.mainWorld.setSceneActive('gameplay', false);
|
||||||
|
this.mainWorld.setSceneActive('menu', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换回游戏
|
||||||
|
hideMenu(): void {
|
||||||
|
this.mainWorld.setSceneActive('menu', false);
|
||||||
|
this.mainWorld.setSceneActive('gameplay', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示设置
|
||||||
|
showSettings(): void {
|
||||||
|
this.mainWorld.setSceneActive('settings', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 隐藏设置
|
||||||
|
hideSettings(): void {
|
||||||
|
this.mainWorld.setSceneActive('settings', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取World状态
|
||||||
|
getWorldStatus() {
|
||||||
|
return this.mainWorld.getStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用示例
|
||||||
|
const gameClient = new GameClient();
|
||||||
|
|
||||||
|
// 显示菜单
|
||||||
|
gameClient.showMenu();
|
||||||
|
|
||||||
|
// 5秒后返回游戏
|
||||||
|
setTimeout(() => {
|
||||||
|
gameClient.hideMenu();
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
console.log('客户端World状态:', gameClient.getWorldStatus());
|
||||||
|
```
|
||||||
|
|
||||||
|
## 传统场景切换和管理
|
||||||
|
|
||||||
|
### 1. 单Scene模式场景管理器
|
||||||
|
|
||||||
> **注意:** 以下的 SceneManager、TransitionManager 等是自定义的场景管理类示例,不是ECS框架提供的内置API。你可以基于这些示例实现自己的场景管理系统。
|
> **注意:** 以下的 SceneManager、TransitionManager 等是自定义的场景管理类示例,不是ECS框架提供的内置API。你可以基于这些示例实现自己的场景管理系统。
|
||||||
|
|
||||||
@@ -731,6 +1137,47 @@ A:
|
|||||||
|
|
||||||
### Q: 多个场景可以同时存在吗?
|
### Q: 多个场景可以同时存在吗?
|
||||||
|
|
||||||
A: 框架同时只支持一个活跃场景,但可以通过场景栈实现多场景管理(如暂停菜单)。
|
A:
|
||||||
|
- **单Scene模式**:框架同时只支持一个活跃场景,但可以通过场景栈实现多场景管理(如暂停菜单)
|
||||||
|
- **多World模式**:每个World可以包含多个同时激活的Scene,支持复杂的多场景架构
|
||||||
|
|
||||||
通过合理使用场景系统,你可以构建出结构清晰、性能优良的游戏架构!
|
### Q: 什么时候使用World系统?
|
||||||
|
|
||||||
|
A:
|
||||||
|
- 多人游戏服务器(每个房间独立管理)
|
||||||
|
- 需要并发运行多个独立游戏世界
|
||||||
|
- 复杂的客户端架构(游戏层、UI层、特效层分离)
|
||||||
|
- 需要跨Scene的全局系统支持
|
||||||
|
|
||||||
|
### Q: World和Scene的性能影响?
|
||||||
|
|
||||||
|
A:
|
||||||
|
- **单Scene模式**:最佳性能,适合简单项目
|
||||||
|
- **多World模式**:每个World独立更新,合理使用不会显著影响性能
|
||||||
|
- **建议**:根据项目复杂度选择合适的架构
|
||||||
|
|
||||||
|
### Q: 如何从单Scene迁移到多World?
|
||||||
|
|
||||||
|
A:
|
||||||
|
```typescript
|
||||||
|
// 原始单Scene代码
|
||||||
|
const scene = new Scene();
|
||||||
|
Core.setScene(scene);
|
||||||
|
|
||||||
|
// 迁移到World模式(可选)
|
||||||
|
Core.enableWorldManager();
|
||||||
|
const world = Core.getWorldManager().createWorld('MainWorld');
|
||||||
|
const scene = world.createScene('main', new Scene());
|
||||||
|
world.start();
|
||||||
|
world.setSceneActive('main', true);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q: World系统的最佳实践?
|
||||||
|
|
||||||
|
A:
|
||||||
|
1. **服务器端**:每个游戏房间使用独立World
|
||||||
|
2. **客户端**:按功能层级划分Scene(游戏、UI、特效)
|
||||||
|
3. **全局系统**:将跨Scene的逻辑放在World的全局System中
|
||||||
|
4. **资源管理**:使用World的autoCleanup功能自动清理空闲资源
|
||||||
|
|
||||||
|
通过合理选择单Scene或多World架构,你可以构建出结构清晰、性能优良的游戏架构!
|
||||||
2
package-lock.json
generated
2
package-lock.json
generated
@@ -11537,7 +11537,7 @@
|
|||||||
},
|
},
|
||||||
"packages/core": {
|
"packages/core": {
|
||||||
"name": "@esengine/ecs-framework",
|
"name": "@esengine/ecs-framework",
|
||||||
"version": "2.1.44",
|
"version": "2.1.45",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-commonjs": "^28.0.3",
|
"@rollup/plugin-commonjs": "^28.0.3",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@esengine/ecs-framework",
|
"name": "@esengine/ecs-framework",
|
||||||
"version": "2.1.44",
|
"version": "2.1.45",
|
||||||
"description": "用于Laya、Cocos Creator等JavaScript游戏引擎的高性能ECS框架",
|
"description": "用于Laya、Cocos Creator等JavaScript游戏引擎的高性能ECS框架",
|
||||||
"main": "bin/index.js",
|
"main": "bin/index.js",
|
||||||
"types": "bin/index.d.ts",
|
"types": "bin/index.d.ts",
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import { Time } from './Utils/Time';
|
|||||||
import { PerformanceMonitor } from './Utils/PerformanceMonitor';
|
import { PerformanceMonitor } from './Utils/PerformanceMonitor';
|
||||||
import { PoolManager } from './Utils/Pool/PoolManager';
|
import { PoolManager } from './Utils/Pool/PoolManager';
|
||||||
import { ECSFluentAPI, createECSAPI } from './ECS/Core/FluentAPI';
|
import { ECSFluentAPI, createECSAPI } from './ECS/Core/FluentAPI';
|
||||||
import { Scene } from './ECS/Scene';
|
|
||||||
import { IScene } from './ECS/IScene';
|
import { IScene } from './ECS/IScene';
|
||||||
|
import { WorldManager } from './ECS/WorldManager';
|
||||||
import { DebugManager } from './Utils/Debug';
|
import { DebugManager } from './Utils/Debug';
|
||||||
import { ICoreConfig, IECSDebugConfig } from './Types';
|
import { ICoreConfig, IECSDebugConfig } from './Types';
|
||||||
import { BigIntFactory, EnvironmentInfo } from './ECS/Utils/BigIntCompatibility';
|
import { BigIntFactory, EnvironmentInfo } from './ECS/Utils/BigIntCompatibility';
|
||||||
@@ -47,6 +47,20 @@ export class Core {
|
|||||||
*/
|
*/
|
||||||
public static paused = false;
|
public static paused = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认World ID
|
||||||
|
*
|
||||||
|
* 用于单Scene模式的默认World标识
|
||||||
|
*/
|
||||||
|
private static readonly DEFAULT_WORLD_ID = '__default__';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认Scene ID
|
||||||
|
*
|
||||||
|
* 用于单Scene模式的默认Scene标识
|
||||||
|
*/
|
||||||
|
private static readonly DEFAULT_SCENE_ID = '__main__';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 全局核心实例
|
* 全局核心实例
|
||||||
*/
|
*/
|
||||||
@@ -71,12 +85,6 @@ export class Core {
|
|||||||
*/
|
*/
|
||||||
public readonly debug: boolean;
|
public readonly debug: boolean;
|
||||||
|
|
||||||
/**
|
|
||||||
* 待切换的场景
|
|
||||||
*
|
|
||||||
* 存储下一帧要切换到的场景实例。
|
|
||||||
*/
|
|
||||||
public _nextScene: IScene | null = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 全局管理器集合
|
* 全局管理器集合
|
||||||
@@ -113,10 +121,6 @@ export class Core {
|
|||||||
*/
|
*/
|
||||||
public _ecsAPI?: ECSFluentAPI;
|
public _ecsAPI?: ECSFluentAPI;
|
||||||
|
|
||||||
/**
|
|
||||||
* 当前活动场景
|
|
||||||
*/
|
|
||||||
public _scene?: IScene;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 调试管理器
|
* 调试管理器
|
||||||
@@ -125,6 +129,13 @@ export class Core {
|
|||||||
*/
|
*/
|
||||||
public _debugManager?: DebugManager;
|
public _debugManager?: DebugManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* World管理器
|
||||||
|
*
|
||||||
|
* 管理多个World实例,支持多房间/多世界架构。
|
||||||
|
*/
|
||||||
|
public _worldManager?: WorldManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Core配置
|
* Core配置
|
||||||
*/
|
*/
|
||||||
@@ -194,82 +205,63 @@ export class Core {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取当前活动的场景
|
* 获取当前活动的场景(属性访问器)
|
||||||
*
|
*
|
||||||
* @returns 当前场景实例,如果没有则返回null
|
* @returns 当前场景实例,如果没有则返回null
|
||||||
*/
|
*/
|
||||||
public static get scene(): IScene | null {
|
public static get scene(): IScene | null {
|
||||||
if (!this._instance)
|
return this.getScene();
|
||||||
return null;
|
|
||||||
return this._instance._scene || null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置当前场景(已废弃)
|
* 获取当前活动的场景(方法调用)
|
||||||
*
|
*
|
||||||
* @deprecated 请使用 Core.setScene() 方法代替。scene setter 可能导致场景延迟激活的时序问题,
|
* @returns 当前场景实例,如果没有则返回null
|
||||||
* 而 setScene() 提供更好的类型安全性和可预测的激活时序。
|
|
||||||
*
|
|
||||||
* 迁移示例:
|
|
||||||
* ```typescript
|
|
||||||
* // 旧方式(已废弃)
|
|
||||||
* Core.scene = myScene;
|
|
||||||
*
|
|
||||||
* // 新方式(推荐)
|
|
||||||
* Core.setScene(myScene);
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* 如果当前没有场景,会立即切换;否则会在下一帧切换。
|
|
||||||
*
|
|
||||||
* @param value - 场景实例
|
|
||||||
*/
|
*/
|
||||||
public static set scene(value: IScene | null) {
|
public static getScene<T extends IScene>(): T | null {
|
||||||
if (!value) return;
|
if (!this._instance) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (this._instance._scene == null) {
|
// 确保默认World存在
|
||||||
this._instance.setSceneInternal(value);
|
this._instance.ensureDefaultWorld();
|
||||||
} else {
|
|
||||||
this._instance._nextScene = value;
|
const defaultWorld = this._instance._worldManager!.getWorld(this.DEFAULT_WORLD_ID);
|
||||||
}
|
return defaultWorld?.getScene(this.DEFAULT_SCENE_ID) as T || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 类型安全的场景设置方法(推荐)
|
* 设置当前场景
|
||||||
*
|
|
||||||
* 这是设置场景的推荐方法,提供更好的类型安全性和可预测的激活时序。
|
|
||||||
* 相比于 scene setter,此方法能确保场景正确初始化和激活。
|
|
||||||
*
|
|
||||||
* 如果当前没有场景,会立即切换;否则会在下一帧切换。
|
|
||||||
*
|
*
|
||||||
* @param scene - 要设置的场景实例
|
* @param scene - 要设置的场景实例
|
||||||
* @returns 设置的场景实例,便于链式调用
|
* @returns 设置的场景实例,便于链式调用
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```typescript
|
|
||||||
* const myScene = new MyScene();
|
|
||||||
* Core.setScene(myScene);
|
|
||||||
*
|
|
||||||
* // 链式调用
|
|
||||||
* const scene = Core.setScene(new MyScene()).addSystem(new MySystem());
|
|
||||||
* ```
|
|
||||||
*/
|
*/
|
||||||
public static setScene<T extends IScene>(scene: T): T {
|
public static setScene<T extends IScene>(scene: T): T {
|
||||||
if (this._instance._scene == null) {
|
if (!this._instance) {
|
||||||
this._instance.setSceneInternal(scene);
|
throw new Error("Core实例未创建,请先调用Core.create()");
|
||||||
} else {
|
|
||||||
this._instance._nextScene = scene;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 确保默认World存在
|
||||||
|
this._instance.ensureDefaultWorld();
|
||||||
|
|
||||||
|
const defaultWorld = this._instance._worldManager!.getWorld(this.DEFAULT_WORLD_ID)!;
|
||||||
|
|
||||||
|
// 移除旧的主Scene(如果存在)
|
||||||
|
if (defaultWorld.getScene(this.DEFAULT_SCENE_ID)) {
|
||||||
|
defaultWorld.removeScene(this.DEFAULT_SCENE_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加新Scene到默认World
|
||||||
|
defaultWorld.createScene(this.DEFAULT_SCENE_ID, scene);
|
||||||
|
defaultWorld.setSceneActive(this.DEFAULT_SCENE_ID, true);
|
||||||
|
|
||||||
|
// 触发场景切换回调
|
||||||
|
this._instance.onSceneChanged();
|
||||||
|
|
||||||
return scene;
|
return scene;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 类型安全的场景获取方法
|
|
||||||
*
|
|
||||||
* @returns 当前场景实例
|
|
||||||
*/
|
|
||||||
public static getScene<T extends IScene>(): T | null {
|
|
||||||
return this._instance?._scene as T || null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建Core实例
|
* 创建Core实例
|
||||||
@@ -466,15 +458,61 @@ export class Core {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 内部场景设置方法
|
* 获取WorldManager实例
|
||||||
*
|
*
|
||||||
* @param scene - 要设置的场景实例
|
* @returns WorldManager实例,如果未初始化则自动创建
|
||||||
*/
|
*/
|
||||||
private setSceneInternal(scene: IScene): void {
|
public static getWorldManager(): WorldManager {
|
||||||
this._scene = scene;
|
if (!this._instance) {
|
||||||
this.onSceneChanged();
|
throw new Error("Core实例未创建,请先调用Core.create()");
|
||||||
this._scene.initialize();
|
}
|
||||||
this._scene.begin();
|
|
||||||
|
if (!this._instance._worldManager) {
|
||||||
|
// 多World模式的配置(用户主动获取WorldManager)
|
||||||
|
this._instance._worldManager = WorldManager.getInstance({
|
||||||
|
maxWorlds: 50,
|
||||||
|
autoCleanup: true,
|
||||||
|
cleanupInterval: 60000,
|
||||||
|
debug: this._instance._config.debug
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._instance._worldManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启用World管理
|
||||||
|
*
|
||||||
|
* 显式启用World功能,用于多房间/多世界架构
|
||||||
|
*/
|
||||||
|
public static enableWorldManager(): WorldManager {
|
||||||
|
return this.getWorldManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 确保默认World存在
|
||||||
|
*
|
||||||
|
* 内部方法,用于懒初始化默认World
|
||||||
|
*/
|
||||||
|
private ensureDefaultWorld(): void {
|
||||||
|
if (!this._worldManager) {
|
||||||
|
this._worldManager = WorldManager.getInstance({
|
||||||
|
maxWorlds: 1, // 单场景用户只需要1个World
|
||||||
|
autoCleanup: false, // 单场景不需要自动清理
|
||||||
|
cleanupInterval: 0, // 禁用清理定时器
|
||||||
|
debug: this._config.debug
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查默认World是否存在
|
||||||
|
if (!this._worldManager.getWorld(Core.DEFAULT_WORLD_ID)) {
|
||||||
|
this._worldManager.createWorld(Core.DEFAULT_WORLD_ID, {
|
||||||
|
name: 'DefaultWorld',
|
||||||
|
maxScenes: 1,
|
||||||
|
autoCleanup: false
|
||||||
|
});
|
||||||
|
this._worldManager.setWorldActive(Core.DEFAULT_WORLD_ID, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -485,28 +523,19 @@ export class Core {
|
|||||||
public onSceneChanged() {
|
public onSceneChanged() {
|
||||||
Time.sceneChanged();
|
Time.sceneChanged();
|
||||||
|
|
||||||
|
// 获取当前Scene(从默认World)
|
||||||
|
const currentScene = Core.getScene();
|
||||||
|
|
||||||
// 初始化ECS API(如果场景支持)
|
// 初始化ECS API(如果场景支持)
|
||||||
if (this._scene && this._scene.querySystem && this._scene.eventSystem) {
|
if (currentScene && currentScene.querySystem && currentScene.eventSystem) {
|
||||||
this._ecsAPI = createECSAPI(this._scene, this._scene.querySystem, this._scene.eventSystem);
|
this._ecsAPI = createECSAPI(currentScene, currentScene.querySystem, currentScene.eventSystem);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 延迟调试管理器通知,避免在场景初始化过程中干扰属性
|
// 延迟调试管理器通知,避免在场景初始化过程中干扰属性
|
||||||
if (this._debugManager) {
|
if (this._debugManager) {
|
||||||
// 使用 requestAnimationFrame 确保在场景完全初始化后再收集数据
|
queueMicrotask(() => {
|
||||||
if (typeof requestAnimationFrame !== 'undefined') {
|
this._debugManager?.onSceneChanged();
|
||||||
requestAnimationFrame(() => {
|
|
||||||
if (this._debugManager) {
|
|
||||||
this._debugManager.onSceneChanged();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
// 兜底:使用 setTimeout
|
|
||||||
setTimeout(() => {
|
|
||||||
if (this._debugManager) {
|
|
||||||
this._debugManager.onSceneChanged();
|
|
||||||
}
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -567,23 +596,25 @@ export class Core {
|
|||||||
// 更新对象池管理器
|
// 更新对象池管理器
|
||||||
this._poolManager.update();
|
this._poolManager.update();
|
||||||
|
|
||||||
// 处理场景切换
|
// 更新所有World
|
||||||
if (this._nextScene != null) {
|
if (this._worldManager) {
|
||||||
if (this._scene != null)
|
const worldsStartTime = this._performanceMonitor.startMonitoring('Worlds.update');
|
||||||
this._scene.end();
|
const activeWorlds = this._worldManager.getActiveWorlds();
|
||||||
|
let totalWorldEntities = 0;
|
||||||
|
|
||||||
this._scene = this._nextScene;
|
for (const world of activeWorlds) {
|
||||||
this._nextScene = null;
|
// 更新World的全局System
|
||||||
this.onSceneChanged();
|
world.updateGlobalSystems();
|
||||||
this._scene.begin();
|
|
||||||
|
// 更新World中的所有Scene
|
||||||
|
world.updateScenes();
|
||||||
|
|
||||||
|
// 统计实体数量(用于性能监控)
|
||||||
|
const worldStats = world.getStats();
|
||||||
|
totalWorldEntities += worldStats.totalEntities;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新当前场景
|
this._performanceMonitor.endMonitoring('Worlds.update', worldsStartTime, totalWorldEntities);
|
||||||
if (this._scene != null && this._scene.update) {
|
|
||||||
const sceneStartTime = this._performanceMonitor.startMonitoring('Scene.update');
|
|
||||||
this._scene.update();
|
|
||||||
const entityCount = this._scene.entities?.count || 0;
|
|
||||||
this._performanceMonitor.endMonitoring('Scene.update', sceneStartTime, entityCount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新调试管理器(基于FPS的数据发送)
|
// 更新调试管理器(基于FPS的数据发送)
|
||||||
|
|||||||
504
packages/core/src/ECS/World.ts
Normal file
504
packages/core/src/ECS/World.ts
Normal file
@@ -0,0 +1,504 @@
|
|||||||
|
import { IScene } from './IScene';
|
||||||
|
import { Scene } from './Scene';
|
||||||
|
import { createLogger } from '../Utils/Logger';
|
||||||
|
|
||||||
|
const logger = createLogger('World');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局系统接口
|
||||||
|
* 全局系统是在World级别运行的系统,不依赖特定Scene
|
||||||
|
*/
|
||||||
|
export interface IGlobalSystem {
|
||||||
|
/**
|
||||||
|
* 系统名称
|
||||||
|
*/
|
||||||
|
readonly name: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化系统
|
||||||
|
*/
|
||||||
|
initialize?(): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新系统
|
||||||
|
*/
|
||||||
|
update(deltaTime?: number): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置系统
|
||||||
|
*/
|
||||||
|
reset?(): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销毁系统
|
||||||
|
*/
|
||||||
|
destroy?(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* World配置接口
|
||||||
|
*/
|
||||||
|
export interface IWorldConfig {
|
||||||
|
/**
|
||||||
|
* World名称
|
||||||
|
*/
|
||||||
|
name?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否启用调试模式
|
||||||
|
*/
|
||||||
|
debug?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最大Scene数量限制
|
||||||
|
*/
|
||||||
|
maxScenes?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否自动清理空Scene
|
||||||
|
*/
|
||||||
|
autoCleanup?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* World类 - ECS世界管理器
|
||||||
|
*
|
||||||
|
* World是Scene的容器,每个World可以管理多个Scene。
|
||||||
|
* 这种设计允许创建独立的游戏世界,如:
|
||||||
|
* - 游戏房间(每个房间一个World)
|
||||||
|
* - 不同的游戏模式
|
||||||
|
* - 独立的模拟环境
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* // 创建游戏房间的World
|
||||||
|
* const roomWorld = new World({ name: 'Room_001' });
|
||||||
|
*
|
||||||
|
* // 在World中创建Scene
|
||||||
|
* const gameScene = roomWorld.createScene('game', new Scene());
|
||||||
|
* const uiScene = roomWorld.createScene('ui', new Scene());
|
||||||
|
*
|
||||||
|
* // 更新整个World
|
||||||
|
* roomWorld.update(deltaTime);
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export class World {
|
||||||
|
public readonly name: string;
|
||||||
|
private readonly _config: IWorldConfig;
|
||||||
|
private readonly _scenes: Map<string, IScene> = new Map();
|
||||||
|
private readonly _activeScenes: Set<string> = new Set();
|
||||||
|
private readonly _globalSystems: IGlobalSystem[] = [];
|
||||||
|
private _isActive: boolean = false;
|
||||||
|
private _createdAt: number;
|
||||||
|
|
||||||
|
constructor(config: IWorldConfig = {}) {
|
||||||
|
this._config = {
|
||||||
|
name: 'World',
|
||||||
|
debug: false,
|
||||||
|
maxScenes: 10,
|
||||||
|
autoCleanup: true,
|
||||||
|
...config
|
||||||
|
};
|
||||||
|
|
||||||
|
this.name = this._config.name!;
|
||||||
|
this._createdAt = Date.now();
|
||||||
|
|
||||||
|
logger.info(`创建World: ${this.name}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== Scene管理 =====
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建并添加Scene到World
|
||||||
|
*/
|
||||||
|
public createScene<T extends IScene>(sceneId: string, sceneInstance?: T): T {
|
||||||
|
if (this._scenes.has(sceneId)) {
|
||||||
|
throw new Error(`Scene ID '${sceneId}' 已存在于World '${this.name}' 中`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._scenes.size >= this._config.maxScenes!) {
|
||||||
|
throw new Error(`World '${this.name}' 已达到最大Scene数量限制: ${this._config.maxScenes}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有提供Scene实例,创建默认Scene
|
||||||
|
const scene = sceneInstance || (new Scene() as unknown as T);
|
||||||
|
|
||||||
|
// 设置Scene的标识
|
||||||
|
if ('id' in scene) {
|
||||||
|
(scene as any).id = sceneId;
|
||||||
|
}
|
||||||
|
if ('name' in scene && !scene.name) {
|
||||||
|
scene.name = sceneId;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._scenes.set(sceneId, scene);
|
||||||
|
|
||||||
|
// 初始化Scene
|
||||||
|
scene.initialize();
|
||||||
|
|
||||||
|
logger.info(`在World '${this.name}' 中创建Scene: ${sceneId}`);
|
||||||
|
return scene;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除Scene
|
||||||
|
*/
|
||||||
|
public removeScene(sceneId: string): boolean {
|
||||||
|
const scene = this._scenes.get(sceneId);
|
||||||
|
if (!scene) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果Scene正在运行,先停止它
|
||||||
|
if (this._activeScenes.has(sceneId)) {
|
||||||
|
this.setSceneActive(sceneId, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理Scene资源
|
||||||
|
scene.end();
|
||||||
|
this._scenes.delete(sceneId);
|
||||||
|
|
||||||
|
logger.info(`从World '${this.name}' 中移除Scene: ${sceneId}`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Scene
|
||||||
|
*/
|
||||||
|
public getScene<T extends IScene>(sceneId: string): T | null {
|
||||||
|
return this._scenes.get(sceneId) as T || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有Scene ID
|
||||||
|
*/
|
||||||
|
public getSceneIds(): string[] {
|
||||||
|
return Array.from(this._scenes.keys());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有Scene
|
||||||
|
*/
|
||||||
|
public getAllScenes(): IScene[] {
|
||||||
|
return Array.from(this._scenes.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置Scene激活状态
|
||||||
|
*/
|
||||||
|
public setSceneActive(sceneId: string, active: boolean): void {
|
||||||
|
const scene = this._scenes.get(sceneId);
|
||||||
|
if (!scene) {
|
||||||
|
logger.warn(`Scene '${sceneId}' 不存在于World '${this.name}' 中`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (active) {
|
||||||
|
this._activeScenes.add(sceneId);
|
||||||
|
// 启动Scene
|
||||||
|
if (scene.begin) {
|
||||||
|
scene.begin();
|
||||||
|
}
|
||||||
|
logger.debug(`在World '${this.name}' 中激活Scene: ${sceneId}`);
|
||||||
|
} else {
|
||||||
|
this._activeScenes.delete(sceneId);
|
||||||
|
// 可选择性地停止Scene,或者让它继续运行但不更新
|
||||||
|
logger.debug(`在World '${this.name}' 中停用Scene: ${sceneId}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查Scene是否激活
|
||||||
|
*/
|
||||||
|
public isSceneActive(sceneId: string): boolean {
|
||||||
|
return this._activeScenes.has(sceneId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取活跃Scene数量
|
||||||
|
*/
|
||||||
|
public getActiveSceneCount(): number {
|
||||||
|
return this._activeScenes.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 全局System管理 =====
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加全局System
|
||||||
|
* 全局System会在所有激活Scene之前更新
|
||||||
|
*/
|
||||||
|
public addGlobalSystem<T extends IGlobalSystem>(system: T): T {
|
||||||
|
if (this._globalSystems.includes(system)) {
|
||||||
|
return system;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._globalSystems.push(system);
|
||||||
|
if (system.initialize) {
|
||||||
|
system.initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug(`在World '${this.name}' 中添加全局System: ${system.name}`);
|
||||||
|
return system;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除全局System
|
||||||
|
*/
|
||||||
|
public removeGlobalSystem(system: IGlobalSystem): boolean {
|
||||||
|
const index = this._globalSystems.indexOf(system);
|
||||||
|
if (index === -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._globalSystems.splice(index, 1);
|
||||||
|
if (system.reset) {
|
||||||
|
system.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug(`从World '${this.name}' 中移除全局System: ${system.name}`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取全局System
|
||||||
|
*/
|
||||||
|
public getGlobalSystem<T extends IGlobalSystem>(type: new (...args: any[]) => T): T | null {
|
||||||
|
for (const system of this._globalSystems) {
|
||||||
|
if (system instanceof type) {
|
||||||
|
return system as T;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== World生命周期 =====
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启动World
|
||||||
|
*/
|
||||||
|
public start(): void {
|
||||||
|
if (this._isActive) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._isActive = true;
|
||||||
|
|
||||||
|
// 启动所有全局System
|
||||||
|
for (const system of this._globalSystems) {
|
||||||
|
if (system.initialize) {
|
||||||
|
system.initialize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(`启动World: ${this.name}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 停止World
|
||||||
|
*/
|
||||||
|
public stop(): void {
|
||||||
|
if (!this._isActive) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 停止所有Scene
|
||||||
|
for (const sceneId of this._activeScenes) {
|
||||||
|
this.setSceneActive(sceneId, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置所有全局System
|
||||||
|
for (const system of this._globalSystems) {
|
||||||
|
if (system.reset) {
|
||||||
|
system.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._isActive = false;
|
||||||
|
logger.info(`停止World: ${this.name}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新World中的全局System
|
||||||
|
* 注意:此方法由Core.update()调用,不应直接调用
|
||||||
|
*/
|
||||||
|
public updateGlobalSystems(): void {
|
||||||
|
if (!this._isActive) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新全局System
|
||||||
|
for (const system of this._globalSystems) {
|
||||||
|
if (system.update) {
|
||||||
|
system.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新World中的所有激活Scene
|
||||||
|
* 注意:此方法由Core.update()调用,不应直接调用
|
||||||
|
*/
|
||||||
|
public updateScenes(): void {
|
||||||
|
if (!this._isActive) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新所有激活的Scene
|
||||||
|
for (const sceneId of this._activeScenes) {
|
||||||
|
const scene = this._scenes.get(sceneId);
|
||||||
|
if (scene && scene.update) {
|
||||||
|
scene.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自动清理(如果启用)
|
||||||
|
if (this._config.autoCleanup && this.shouldAutoCleanup()) {
|
||||||
|
this.cleanup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销毁World
|
||||||
|
*/
|
||||||
|
public destroy(): void {
|
||||||
|
logger.info(`销毁World: ${this.name}`);
|
||||||
|
|
||||||
|
// 停止World
|
||||||
|
this.stop();
|
||||||
|
|
||||||
|
// 销毁所有Scene
|
||||||
|
const sceneIds = Array.from(this._scenes.keys());
|
||||||
|
for (const sceneId of sceneIds) {
|
||||||
|
this.removeScene(sceneId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理全局System
|
||||||
|
for (const system of this._globalSystems) {
|
||||||
|
if (system.destroy) {
|
||||||
|
system.destroy();
|
||||||
|
} else if (system.reset) {
|
||||||
|
system.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._globalSystems.length = 0;
|
||||||
|
|
||||||
|
this._scenes.clear();
|
||||||
|
this._activeScenes.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 状态信息 =====
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取World状态
|
||||||
|
*/
|
||||||
|
public getStatus() {
|
||||||
|
return {
|
||||||
|
name: this.name,
|
||||||
|
isActive: this._isActive,
|
||||||
|
sceneCount: this._scenes.size,
|
||||||
|
activeSceneCount: this._activeScenes.size,
|
||||||
|
globalSystemCount: this._globalSystems.length,
|
||||||
|
createdAt: this._createdAt,
|
||||||
|
config: { ...this._config },
|
||||||
|
scenes: Array.from(this._scenes.keys()).map(sceneId => ({
|
||||||
|
id: sceneId,
|
||||||
|
isActive: this._activeScenes.has(sceneId),
|
||||||
|
name: this._scenes.get(sceneId)?.name || sceneId
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取World统计信息
|
||||||
|
*/
|
||||||
|
public getStats() {
|
||||||
|
const stats = {
|
||||||
|
totalEntities: 0,
|
||||||
|
totalSystems: this._globalSystems.length,
|
||||||
|
memoryUsage: 0,
|
||||||
|
performance: {
|
||||||
|
averageUpdateTime: 0,
|
||||||
|
maxUpdateTime: 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 统计所有Scene的实体数量
|
||||||
|
for (const scene of this._scenes.values()) {
|
||||||
|
if (scene.entities) {
|
||||||
|
stats.totalEntities += scene.entities.count;
|
||||||
|
}
|
||||||
|
if (scene.systems) {
|
||||||
|
stats.totalSystems += scene.systems.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 私有方法 =====
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否应该执行自动清理
|
||||||
|
*/
|
||||||
|
private shouldAutoCleanup(): boolean {
|
||||||
|
// 简单的清理策略:如果有空Scene且超过5分钟没有实体
|
||||||
|
const currentTime = Date.now();
|
||||||
|
const cleanupThreshold = 5 * 60 * 1000; // 5分钟
|
||||||
|
|
||||||
|
for (const [sceneId, scene] of this._scenes) {
|
||||||
|
if (!this._activeScenes.has(sceneId) &&
|
||||||
|
scene.entities &&
|
||||||
|
scene.entities.count === 0 &&
|
||||||
|
(currentTime - this._createdAt) > cleanupThreshold) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行清理操作
|
||||||
|
*/
|
||||||
|
private cleanup(): void {
|
||||||
|
const sceneIds = Array.from(this._scenes.keys());
|
||||||
|
const currentTime = Date.now();
|
||||||
|
const cleanupThreshold = 5 * 60 * 1000; // 5分钟
|
||||||
|
|
||||||
|
for (const sceneId of sceneIds) {
|
||||||
|
const scene = this._scenes.get(sceneId);
|
||||||
|
if (scene &&
|
||||||
|
!this._activeScenes.has(sceneId) &&
|
||||||
|
scene.entities &&
|
||||||
|
scene.entities.count === 0 &&
|
||||||
|
(currentTime - this._createdAt) > cleanupThreshold) {
|
||||||
|
|
||||||
|
this.removeScene(sceneId);
|
||||||
|
logger.debug(`自动清理空Scene: ${sceneId} from World ${this.name}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 访问器 =====
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查World是否激活
|
||||||
|
*/
|
||||||
|
public get isActive(): boolean {
|
||||||
|
return this._isActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Scene数量
|
||||||
|
*/
|
||||||
|
public get sceneCount(): number {
|
||||||
|
return this._scenes.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取创建时间
|
||||||
|
*/
|
||||||
|
public get createdAt(): number {
|
||||||
|
return this._createdAt;
|
||||||
|
}
|
||||||
|
}
|
||||||
463
packages/core/src/ECS/WorldManager.ts
Normal file
463
packages/core/src/ECS/WorldManager.ts
Normal file
@@ -0,0 +1,463 @@
|
|||||||
|
import { World, IWorldConfig } from './World';
|
||||||
|
import { createLogger } from '../Utils/Logger';
|
||||||
|
|
||||||
|
const logger = createLogger('WorldManager');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WorldManager配置接口
|
||||||
|
*/
|
||||||
|
export interface IWorldManagerConfig {
|
||||||
|
/**
|
||||||
|
* 最大World数量
|
||||||
|
*/
|
||||||
|
maxWorlds?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否自动清理空World
|
||||||
|
*/
|
||||||
|
autoCleanup?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理间隔(毫秒)
|
||||||
|
*/
|
||||||
|
cleanupInterval?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否启用调试模式
|
||||||
|
*/
|
||||||
|
debug?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* World管理器 - 管理所有World实例
|
||||||
|
*
|
||||||
|
* WorldManager是全局单例,负责管理所有World的生命周期。
|
||||||
|
* 每个World都是独立的ECS环境,可以包含多个Scene。
|
||||||
|
*
|
||||||
|
* 设计理念:
|
||||||
|
* - Core负责单Scene的传统ECS管理
|
||||||
|
* - World负责多Scene的管理和协调
|
||||||
|
* - WorldManager负责多World的全局管理
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* // 获取全局WorldManager
|
||||||
|
* const worldManager = WorldManager.getInstance();
|
||||||
|
*
|
||||||
|
* // 创建游戏房间World
|
||||||
|
* const roomWorld = worldManager.createWorld('room_001', {
|
||||||
|
* name: 'GameRoom_001',
|
||||||
|
* maxScenes: 5
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* // 在游戏循环中更新所有World
|
||||||
|
* worldManager.updateAll(deltaTime);
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export class WorldManager {
|
||||||
|
private static _instance: WorldManager | null = null;
|
||||||
|
|
||||||
|
private readonly _config: IWorldManagerConfig;
|
||||||
|
private readonly _worlds: Map<string, World> = new Map();
|
||||||
|
private readonly _activeWorlds: Set<string> = new Set();
|
||||||
|
private _cleanupTimer: NodeJS.Timeout | null = null;
|
||||||
|
private _isRunning: boolean = false;
|
||||||
|
|
||||||
|
private constructor(config: IWorldManagerConfig = {}) {
|
||||||
|
this._config = {
|
||||||
|
maxWorlds: 50,
|
||||||
|
autoCleanup: true,
|
||||||
|
cleanupInterval: 30000, // 30秒
|
||||||
|
debug: false,
|
||||||
|
...config
|
||||||
|
};
|
||||||
|
|
||||||
|
logger.info('WorldManager已初始化', {
|
||||||
|
maxWorlds: this._config.maxWorlds,
|
||||||
|
autoCleanup: this._config.autoCleanup,
|
||||||
|
cleanupInterval: this._config.cleanupInterval
|
||||||
|
});
|
||||||
|
|
||||||
|
this.startCleanupTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取WorldManager单例实例
|
||||||
|
*/
|
||||||
|
public static getInstance(config?: IWorldManagerConfig): WorldManager {
|
||||||
|
if (!this._instance) {
|
||||||
|
this._instance = new WorldManager(config);
|
||||||
|
}
|
||||||
|
return this._instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置WorldManager实例(主要用于测试)
|
||||||
|
*/
|
||||||
|
public static reset(): void {
|
||||||
|
if (this._instance) {
|
||||||
|
this._instance.destroy();
|
||||||
|
this._instance = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== World管理 =====
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建新World
|
||||||
|
*/
|
||||||
|
public createWorld(worldId: string, config?: IWorldConfig): World {
|
||||||
|
if (!worldId || typeof worldId !== 'string' || worldId.trim() === '') {
|
||||||
|
throw new Error('World ID不能为空');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._worlds.has(worldId)) {
|
||||||
|
throw new Error(`World ID '${worldId}' 已存在`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._worlds.size >= this._config.maxWorlds!) {
|
||||||
|
throw new Error(`已达到最大World数量限制: ${this._config.maxWorlds}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const worldConfig: IWorldConfig = {
|
||||||
|
name: worldId,
|
||||||
|
debug: this._config.debug,
|
||||||
|
...config
|
||||||
|
};
|
||||||
|
|
||||||
|
const world = new World(worldConfig);
|
||||||
|
this._worlds.set(worldId, world);
|
||||||
|
|
||||||
|
logger.info(`创建World: ${worldId}`, { config: worldConfig });
|
||||||
|
return world;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除World
|
||||||
|
*/
|
||||||
|
public removeWorld(worldId: string): boolean {
|
||||||
|
const world = this._worlds.get(worldId);
|
||||||
|
if (!world) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果World正在运行,先停止它
|
||||||
|
if (this._activeWorlds.has(worldId)) {
|
||||||
|
this.setWorldActive(worldId, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 销毁World
|
||||||
|
world.destroy();
|
||||||
|
this._worlds.delete(worldId);
|
||||||
|
|
||||||
|
logger.info(`移除World: ${worldId}`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取World
|
||||||
|
*/
|
||||||
|
public getWorld(worldId: string): World | null {
|
||||||
|
return this._worlds.get(worldId) || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有World ID
|
||||||
|
*/
|
||||||
|
public getWorldIds(): string[] {
|
||||||
|
return Array.from(this._worlds.keys());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有World
|
||||||
|
*/
|
||||||
|
public getAllWorlds(): World[] {
|
||||||
|
return Array.from(this._worlds.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置World激活状态
|
||||||
|
*/
|
||||||
|
public setWorldActive(worldId: string, active: boolean): void {
|
||||||
|
const world = this._worlds.get(worldId);
|
||||||
|
if (!world) {
|
||||||
|
logger.warn(`World '${worldId}' 不存在`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (active) {
|
||||||
|
this._activeWorlds.add(worldId);
|
||||||
|
world.start();
|
||||||
|
logger.debug(`激活World: ${worldId}`);
|
||||||
|
} else {
|
||||||
|
this._activeWorlds.delete(worldId);
|
||||||
|
world.stop();
|
||||||
|
logger.debug(`停用World: ${worldId}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查World是否激活
|
||||||
|
*/
|
||||||
|
public isWorldActive(worldId: string): boolean {
|
||||||
|
return this._activeWorlds.has(worldId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 批量操作 =====
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有激活的World
|
||||||
|
* 注意:此方法供Core.update()使用
|
||||||
|
*/
|
||||||
|
public getActiveWorlds(): World[] {
|
||||||
|
const activeWorlds: World[] = [];
|
||||||
|
for (const worldId of this._activeWorlds) {
|
||||||
|
const world = this._worlds.get(worldId);
|
||||||
|
if (world) {
|
||||||
|
activeWorlds.push(world);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return activeWorlds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启动所有World
|
||||||
|
*/
|
||||||
|
public startAll(): void {
|
||||||
|
this._isRunning = true;
|
||||||
|
|
||||||
|
for (const worldId of this._worlds.keys()) {
|
||||||
|
this.setWorldActive(worldId, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info('启动所有World');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 停止所有World
|
||||||
|
*/
|
||||||
|
public stopAll(): void {
|
||||||
|
this._isRunning = false;
|
||||||
|
|
||||||
|
for (const worldId of this._activeWorlds) {
|
||||||
|
this.setWorldActive(worldId, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info('停止所有World');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找满足条件的World
|
||||||
|
*/
|
||||||
|
public findWorlds(predicate: (world: World) => boolean): World[] {
|
||||||
|
const results: World[] = [];
|
||||||
|
for (const world of this._worlds.values()) {
|
||||||
|
if (predicate(world)) {
|
||||||
|
results.push(world);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据名称查找World
|
||||||
|
*/
|
||||||
|
public findWorldByName(name: string): World | null {
|
||||||
|
for (const world of this._worlds.values()) {
|
||||||
|
if (world.name === name) {
|
||||||
|
return world;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 统计和监控 =====
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取WorldManager统计信息
|
||||||
|
*/
|
||||||
|
public getStats() {
|
||||||
|
const stats = {
|
||||||
|
totalWorlds: this._worlds.size,
|
||||||
|
activeWorlds: this._activeWorlds.size,
|
||||||
|
totalScenes: 0,
|
||||||
|
totalEntities: 0,
|
||||||
|
totalSystems: 0,
|
||||||
|
memoryUsage: 0,
|
||||||
|
isRunning: this._isRunning,
|
||||||
|
config: { ...this._config },
|
||||||
|
worlds: [] as any[]
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const [worldId, world] of this._worlds) {
|
||||||
|
const worldStats = world.getStats();
|
||||||
|
stats.totalScenes += worldStats.totalSystems; // World的getStats可能需要调整
|
||||||
|
stats.totalEntities += worldStats.totalEntities;
|
||||||
|
stats.totalSystems += worldStats.totalSystems;
|
||||||
|
|
||||||
|
stats.worlds.push({
|
||||||
|
id: worldId,
|
||||||
|
name: world.name,
|
||||||
|
isActive: this._activeWorlds.has(worldId),
|
||||||
|
sceneCount: world.sceneCount,
|
||||||
|
...worldStats
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取详细状态信息
|
||||||
|
*/
|
||||||
|
public getDetailedStatus() {
|
||||||
|
return {
|
||||||
|
...this.getStats(),
|
||||||
|
worlds: Array.from(this._worlds.entries()).map(([worldId, world]) => ({
|
||||||
|
id: worldId,
|
||||||
|
isActive: this._activeWorlds.has(worldId),
|
||||||
|
status: world.getStatus()
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 生命周期管理 =====
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理空World
|
||||||
|
*/
|
||||||
|
public cleanup(): number {
|
||||||
|
const worldsToRemove: string[] = [];
|
||||||
|
|
||||||
|
for (const [worldId, world] of this._worlds) {
|
||||||
|
if (this.shouldCleanupWorld(world)) {
|
||||||
|
worldsToRemove.push(worldId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const worldId of worldsToRemove) {
|
||||||
|
this.removeWorld(worldId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (worldsToRemove.length > 0) {
|
||||||
|
logger.debug(`清理了 ${worldsToRemove.length} 个World`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return worldsToRemove.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销毁WorldManager
|
||||||
|
*/
|
||||||
|
public destroy(): void {
|
||||||
|
logger.info('正在销毁WorldManager...');
|
||||||
|
|
||||||
|
// 停止清理定时器
|
||||||
|
this.stopCleanupTimer();
|
||||||
|
|
||||||
|
// 停止所有World
|
||||||
|
this.stopAll();
|
||||||
|
|
||||||
|
// 销毁所有World
|
||||||
|
const worldIds = Array.from(this._worlds.keys());
|
||||||
|
for (const worldId of worldIds) {
|
||||||
|
this.removeWorld(worldId);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._worlds.clear();
|
||||||
|
this._activeWorlds.clear();
|
||||||
|
this._isRunning = false;
|
||||||
|
|
||||||
|
logger.info('WorldManager已销毁');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 私有方法 =====
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启动清理定时器
|
||||||
|
*/
|
||||||
|
private startCleanupTimer(): void {
|
||||||
|
if (!this._config.autoCleanup || this._cleanupTimer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._cleanupTimer = setInterval(() => {
|
||||||
|
this.cleanup();
|
||||||
|
}, this._config.cleanupInterval);
|
||||||
|
|
||||||
|
logger.debug(`启动World清理定时器,间隔: ${this._config.cleanupInterval}ms`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 停止清理定时器
|
||||||
|
*/
|
||||||
|
private stopCleanupTimer(): void {
|
||||||
|
if (this._cleanupTimer) {
|
||||||
|
clearInterval(this._cleanupTimer);
|
||||||
|
this._cleanupTimer = null;
|
||||||
|
logger.debug('停止World清理定时器');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断World是否应该被清理
|
||||||
|
*/
|
||||||
|
private shouldCleanupWorld(world: World): boolean {
|
||||||
|
// 清理策略:
|
||||||
|
// 1. World未激活
|
||||||
|
// 2. 没有Scene或所有Scene都是空的
|
||||||
|
// 3. 创建时间超过10分钟
|
||||||
|
|
||||||
|
if (world.isActive) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (world.sceneCount === 0) {
|
||||||
|
const age = Date.now() - world.createdAt;
|
||||||
|
return age > 10 * 60 * 1000; // 10分钟
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否所有Scene都是空的
|
||||||
|
const allScenes = world.getAllScenes();
|
||||||
|
const hasEntities = allScenes.some(scene =>
|
||||||
|
scene.entities && scene.entities.count > 0
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!hasEntities) {
|
||||||
|
const age = Date.now() - world.createdAt;
|
||||||
|
return age > 10 * 60 * 1000; // 10分钟
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 访问器 =====
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取World总数
|
||||||
|
*/
|
||||||
|
public get worldCount(): number {
|
||||||
|
return this._worlds.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取激活World数量
|
||||||
|
*/
|
||||||
|
public get activeWorldCount(): number {
|
||||||
|
return this._activeWorlds.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否正在运行
|
||||||
|
*/
|
||||||
|
public get isRunning(): boolean {
|
||||||
|
return this._isRunning;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取配置
|
||||||
|
*/
|
||||||
|
public get config(): IWorldManagerConfig {
|
||||||
|
return { ...this._config };
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,8 @@ export * from './Utils';
|
|||||||
export * from './Decorators';
|
export * from './Decorators';
|
||||||
export { Scene } from './Scene';
|
export { Scene } from './Scene';
|
||||||
export { IScene, ISceneFactory, ISceneConfig } from './IScene';
|
export { IScene, ISceneFactory, ISceneConfig } from './IScene';
|
||||||
|
export { World, IWorldConfig } from './World';
|
||||||
|
export { WorldManager, IWorldManagerConfig } from './WorldManager';
|
||||||
export { EntityManager, EntityQueryBuilder } from './Core/EntityManager';
|
export { EntityManager, EntityQueryBuilder } from './Core/EntityManager';
|
||||||
export * from './Core/Events';
|
export * from './Core/Events';
|
||||||
export * from './Core/Query';
|
export * from './Core/Query';
|
||||||
|
|||||||
589
packages/core/tests/ECS/Core/WorldCoreIntegration.test.ts
Normal file
589
packages/core/tests/ECS/Core/WorldCoreIntegration.test.ts
Normal file
@@ -0,0 +1,589 @@
|
|||||||
|
import { Core } from '../../../src/Core';
|
||||||
|
import { Scene } from '../../../src/ECS/Scene';
|
||||||
|
import { World, IGlobalSystem } from '../../../src/ECS/World';
|
||||||
|
import { WorldManager } from '../../../src/ECS/WorldManager';
|
||||||
|
import { EntitySystem } from '../../../src/ECS/Systems/EntitySystem';
|
||||||
|
import { Component } from '../../../src/ECS/Component';
|
||||||
|
import { Matcher } from '../../../src/ECS/Utils/Matcher';
|
||||||
|
import { Entity } from '../../../src/ECS/Entity';
|
||||||
|
|
||||||
|
// 测试用组件
|
||||||
|
class TestComponent extends Component {
|
||||||
|
public value: number = 0;
|
||||||
|
|
||||||
|
constructor(value: number = 0) {
|
||||||
|
super();
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public reset(): void {
|
||||||
|
this.value = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NetworkComponent extends Component {
|
||||||
|
public playerId: string;
|
||||||
|
|
||||||
|
constructor(playerId: string) {
|
||||||
|
super();
|
||||||
|
this.playerId = playerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public reset(): void {
|
||||||
|
this.playerId = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试用系统
|
||||||
|
class TestGlobalSystem extends EntitySystem {
|
||||||
|
public processedEntities: Entity[] = [];
|
||||||
|
public updateCount: number = 0;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(Matcher.empty().all(TestComponent));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override process(entities: Entity[]): void {
|
||||||
|
this.processedEntities = [...entities];
|
||||||
|
this.updateCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 正确的全局系统实现
|
||||||
|
class NetworkSyncGlobalSystem implements IGlobalSystem {
|
||||||
|
public readonly name = 'NetworkSyncSystem';
|
||||||
|
public updateCount: number = 0;
|
||||||
|
|
||||||
|
public initialize(): void {
|
||||||
|
// 初始化网络连接等
|
||||||
|
}
|
||||||
|
|
||||||
|
public update(): void {
|
||||||
|
this.updateCount++;
|
||||||
|
// 同步网络数据等全局逻辑
|
||||||
|
}
|
||||||
|
|
||||||
|
public reset(): void {
|
||||||
|
this.updateCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public destroy(): void {
|
||||||
|
// 清理网络连接等
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scene级别的EntitySystem(正确的用法)
|
||||||
|
class NetworkSyncSystem extends EntitySystem {
|
||||||
|
public syncCount: number = 0;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(Matcher.empty().all(NetworkComponent));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override process(entities: Entity[]): void {
|
||||||
|
this.syncCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// World级别的网络同步全局系统
|
||||||
|
class NetworkGlobalSystem implements IGlobalSystem {
|
||||||
|
public readonly name = 'NetworkGlobalSystem';
|
||||||
|
public syncCount: number = 0;
|
||||||
|
|
||||||
|
public initialize(): void {
|
||||||
|
// 初始化网络连接
|
||||||
|
}
|
||||||
|
|
||||||
|
public update(): void {
|
||||||
|
this.syncCount++;
|
||||||
|
// 全局网络同步逻辑
|
||||||
|
}
|
||||||
|
|
||||||
|
public reset(): void {
|
||||||
|
this.syncCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public destroy(): void {
|
||||||
|
// 清理网络连接
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试用Scene
|
||||||
|
class TestScene extends Scene {
|
||||||
|
public updateCallCount: number = 0;
|
||||||
|
|
||||||
|
public override update(): void {
|
||||||
|
super.update();
|
||||||
|
this.updateCallCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('World与Core集成测试', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
// 重置Core和WorldManager
|
||||||
|
if ((Core as any)._instance) {
|
||||||
|
(Core as any)._instance = null;
|
||||||
|
}
|
||||||
|
WorldManager['_instance'] = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
// 清理资源
|
||||||
|
if ((Core as any)._instance) {
|
||||||
|
const worldManager = Core.getWorldManager?.();
|
||||||
|
if (worldManager) {
|
||||||
|
const worldIds = worldManager.getWorldIds();
|
||||||
|
worldIds.forEach(id => {
|
||||||
|
worldManager.removeWorld(id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
(Core as any)._instance = null;
|
||||||
|
}
|
||||||
|
WorldManager['_instance'] = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('融合设计基础功能', () => {
|
||||||
|
test('单Scene模式应该保持向后兼容', () => {
|
||||||
|
Core.create({ debug: false });
|
||||||
|
|
||||||
|
// 传统单Scene用法
|
||||||
|
const scene = new Scene();
|
||||||
|
scene.name = 'TestScene';
|
||||||
|
|
||||||
|
Core.setScene(scene);
|
||||||
|
|
||||||
|
const retrievedScene = Core.getScene();
|
||||||
|
expect(retrievedScene).toBe(scene);
|
||||||
|
expect(retrievedScene?.name).toBe('TestScene');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('启用WorldManager后应该支持多World功能', () => {
|
||||||
|
Core.create({ debug: false });
|
||||||
|
Core.enableWorldManager();
|
||||||
|
|
||||||
|
const worldManager = Core.getWorldManager();
|
||||||
|
expect(worldManager).toBeDefined();
|
||||||
|
|
||||||
|
const world = worldManager.createWorld('TestWorld');
|
||||||
|
expect(world).toBeDefined();
|
||||||
|
expect(world.name).toBe('TestWorld');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getWorldManager应该自动创建WorldManager', () => {
|
||||||
|
Core.create({ debug: false });
|
||||||
|
|
||||||
|
// 获取WorldManager会自动创建实例
|
||||||
|
const worldManager = Core.getWorldManager();
|
||||||
|
expect(worldManager).toBeDefined();
|
||||||
|
|
||||||
|
// 多次获取应该返回同一个实例
|
||||||
|
const worldManager2 = Core.getWorldManager();
|
||||||
|
expect(worldManager2).toBe(worldManager);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('单Scene模式下Core.update应该正常工作', () => {
|
||||||
|
Core.create({ debug: false });
|
||||||
|
|
||||||
|
const scene = new TestScene();
|
||||||
|
Core.setScene(scene);
|
||||||
|
|
||||||
|
// 模拟更新
|
||||||
|
Core.update(0.016);
|
||||||
|
|
||||||
|
expect(scene.updateCallCount).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('默认World机制', () => {
|
||||||
|
test('设置Scene应该自动创建默认World', () => {
|
||||||
|
Core.create({ debug: false });
|
||||||
|
|
||||||
|
const scene = new Scene();
|
||||||
|
Core.setScene(scene);
|
||||||
|
|
||||||
|
// 启用WorldManager后应该能看到默认World
|
||||||
|
Core.enableWorldManager();
|
||||||
|
const worldManager = Core.getWorldManager();
|
||||||
|
|
||||||
|
expect(worldManager.getWorld('__default__')).toBeDefined();
|
||||||
|
|
||||||
|
const defaultWorld = worldManager.getWorld('__default__');
|
||||||
|
expect(defaultWorld).toBeDefined();
|
||||||
|
expect(defaultWorld?.getScene('__main__')).toBe(scene);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('默认World的Scene应该正确激活', () => {
|
||||||
|
Core.create({ debug: false });
|
||||||
|
|
||||||
|
const scene = new Scene();
|
||||||
|
Core.setScene(scene);
|
||||||
|
|
||||||
|
Core.enableWorldManager();
|
||||||
|
const worldManager = Core.getWorldManager();
|
||||||
|
const defaultWorld = worldManager.getWorld('__default__');
|
||||||
|
|
||||||
|
expect(defaultWorld?.isSceneActive('__main__')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('替换默认Scene应该正确处理', () => {
|
||||||
|
Core.create({ debug: false });
|
||||||
|
|
||||||
|
const scene1 = new Scene();
|
||||||
|
scene1.name = 'Scene1';
|
||||||
|
Core.setScene(scene1);
|
||||||
|
|
||||||
|
const scene2 = new Scene();
|
||||||
|
scene2.name = 'Scene2';
|
||||||
|
Core.setScene(scene2);
|
||||||
|
|
||||||
|
const currentScene = Core.getScene();
|
||||||
|
expect(currentScene).toBe(scene2);
|
||||||
|
expect(currentScene?.name).toBe('Scene2');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('多World更新机制', () => {
|
||||||
|
test('Core.update应该更新所有活跃World', () => {
|
||||||
|
Core.create({ debug: false });
|
||||||
|
Core.enableWorldManager();
|
||||||
|
|
||||||
|
const worldManager = Core.getWorldManager();
|
||||||
|
|
||||||
|
// 创建多个World
|
||||||
|
const world1 = worldManager.createWorld('World1');
|
||||||
|
const world2 = worldManager.createWorld('World2');
|
||||||
|
const world3 = worldManager.createWorld('World3');
|
||||||
|
|
||||||
|
// 为每个World创建Scene和System
|
||||||
|
const scene1 = world1.createScene('scene1', new TestScene());
|
||||||
|
const scene2 = world2.createScene('scene2', new TestScene());
|
||||||
|
const scene3 = world3.createScene('scene3', new TestScene());
|
||||||
|
|
||||||
|
// 启动部分World
|
||||||
|
worldManager.setWorldActive('World1', true);
|
||||||
|
worldManager.setWorldActive('World2', true);
|
||||||
|
// world3保持未启动
|
||||||
|
|
||||||
|
world1.setSceneActive('scene1', true);
|
||||||
|
world2.setSceneActive('scene2', true);
|
||||||
|
|
||||||
|
// 执行更新
|
||||||
|
Core.update(0.016);
|
||||||
|
|
||||||
|
// 检查只有激活的World被更新
|
||||||
|
expect(scene1.updateCallCount).toBeGreaterThan(0);
|
||||||
|
expect(scene2.updateCallCount).toBeGreaterThan(0);
|
||||||
|
expect(scene3.updateCallCount).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('全局系统应该在Scene更新前执行', () => {
|
||||||
|
Core.create({ debug: false });
|
||||||
|
Core.enableWorldManager();
|
||||||
|
|
||||||
|
const worldManager = Core.getWorldManager();
|
||||||
|
const world = worldManager.createWorld('TestWorld');
|
||||||
|
|
||||||
|
// 添加正确设计的全局系统(业务逻辑系统,不是EntitySystem)
|
||||||
|
const globalSystem = new NetworkSyncGlobalSystem();
|
||||||
|
world.addGlobalSystem(globalSystem);
|
||||||
|
|
||||||
|
// 创建Scene
|
||||||
|
const scene = world.createScene('testScene');
|
||||||
|
|
||||||
|
worldManager.setWorldActive('TestWorld', true);
|
||||||
|
world.setSceneActive('testScene', true);
|
||||||
|
|
||||||
|
// 执行更新
|
||||||
|
Core.update(0.016);
|
||||||
|
|
||||||
|
// 验证全局System被正确更新
|
||||||
|
expect(globalSystem.updateCount).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('多房间游戏服务器场景', () => {
|
||||||
|
test('多个游戏房间应该独立运行', () => {
|
||||||
|
Core.create({ debug: false });
|
||||||
|
Core.enableWorldManager();
|
||||||
|
|
||||||
|
const worldManager = Core.getWorldManager();
|
||||||
|
|
||||||
|
// 创建两个游戏房间
|
||||||
|
const room1 = worldManager.createWorld('Room_001');
|
||||||
|
const room2 = worldManager.createWorld('Room_002');
|
||||||
|
|
||||||
|
// 为每个房间设置Scene
|
||||||
|
const gameScene1 = room1.createScene('game');
|
||||||
|
const gameScene2 = room2.createScene('game');
|
||||||
|
|
||||||
|
// 为每个房间添加全局网络系统
|
||||||
|
const netSystem1 = new NetworkGlobalSystem();
|
||||||
|
const netSystem2 = new NetworkGlobalSystem();
|
||||||
|
|
||||||
|
room1.addGlobalSystem(netSystem1);
|
||||||
|
room2.addGlobalSystem(netSystem2);
|
||||||
|
|
||||||
|
// 在每个房间创建玩家
|
||||||
|
const player1 = gameScene1.createEntity('Player1');
|
||||||
|
player1.addComponent(new NetworkComponent('player_123'));
|
||||||
|
|
||||||
|
const player2 = gameScene2.createEntity('Player2');
|
||||||
|
player2.addComponent(new NetworkComponent('player_456'));
|
||||||
|
|
||||||
|
// 启动房间
|
||||||
|
worldManager.setWorldActive('Room_001', true);
|
||||||
|
worldManager.setWorldActive('Room_002', true);
|
||||||
|
room1.setSceneActive('game', true);
|
||||||
|
room2.setSceneActive('game', true);
|
||||||
|
|
||||||
|
// 模拟游戏循环
|
||||||
|
for (let i = 0; i < 5; i++) {
|
||||||
|
Core.update(0.016);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证每个房间独立运行
|
||||||
|
expect(netSystem1.syncCount).toBeGreaterThan(0);
|
||||||
|
expect(netSystem2.syncCount).toBeGreaterThan(0);
|
||||||
|
expect(room1.getActiveSceneCount()).toBe(1);
|
||||||
|
expect(room2.getActiveSceneCount()).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('房间销毁应该完全清理资源', () => {
|
||||||
|
Core.create({ debug: false });
|
||||||
|
Core.enableWorldManager();
|
||||||
|
|
||||||
|
const worldManager = Core.getWorldManager();
|
||||||
|
|
||||||
|
// 创建房间
|
||||||
|
const room = worldManager.createWorld('TempRoom');
|
||||||
|
const scene = room.createScene('game');
|
||||||
|
|
||||||
|
// 添加内容
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
const entity = scene.createEntity(`Entity${i}`);
|
||||||
|
entity.addComponent(new TestComponent(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
room.addGlobalSystem(new NetworkSyncGlobalSystem());
|
||||||
|
worldManager.setWorldActive('TempRoom', true);
|
||||||
|
room.setSceneActive('game', true);
|
||||||
|
|
||||||
|
// 验证房间正常运行
|
||||||
|
Core.update(0.016);
|
||||||
|
|
||||||
|
const beforeDestroy = worldManager.getStats();
|
||||||
|
expect(beforeDestroy.totalWorlds).toBe(1);
|
||||||
|
expect(beforeDestroy.activeWorlds).toBe(1);
|
||||||
|
|
||||||
|
// 销毁房间
|
||||||
|
worldManager.removeWorld('TempRoom');
|
||||||
|
|
||||||
|
const afterDestroy = worldManager.getStats();
|
||||||
|
expect(afterDestroy.totalWorlds).toBe(0);
|
||||||
|
expect(afterDestroy.activeWorlds).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('客户端多层Scene架构', () => {
|
||||||
|
test('分层Scene应该同时运行', () => {
|
||||||
|
Core.create({ debug: false });
|
||||||
|
Core.enableWorldManager();
|
||||||
|
|
||||||
|
const worldManager = Core.getWorldManager();
|
||||||
|
const clientWorld = worldManager.createWorld('ClientWorld');
|
||||||
|
|
||||||
|
// 创建不同层的Scene
|
||||||
|
const gameplayScene = clientWorld.createScene('gameplay', new TestScene());
|
||||||
|
const uiScene = clientWorld.createScene('ui', new TestScene());
|
||||||
|
const effectsScene = clientWorld.createScene('effects', new TestScene());
|
||||||
|
|
||||||
|
// 启动世界并激活所有Scene
|
||||||
|
worldManager.setWorldActive('ClientWorld', true);
|
||||||
|
clientWorld.setSceneActive('gameplay', true);
|
||||||
|
clientWorld.setSceneActive('ui', true);
|
||||||
|
clientWorld.setSceneActive('effects', true);
|
||||||
|
|
||||||
|
// 执行更新
|
||||||
|
Core.update(0.016);
|
||||||
|
|
||||||
|
// 验证所有Scene都被更新
|
||||||
|
expect(gameplayScene.updateCallCount).toBeGreaterThan(0);
|
||||||
|
expect(uiScene.updateCallCount).toBeGreaterThan(0);
|
||||||
|
expect(effectsScene.updateCallCount).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Scene的动态激活和停用', () => {
|
||||||
|
Core.create({ debug: false });
|
||||||
|
Core.enableWorldManager();
|
||||||
|
|
||||||
|
const worldManager = Core.getWorldManager();
|
||||||
|
const world = worldManager.createWorld('DynamicWorld');
|
||||||
|
|
||||||
|
const gameScene = world.createScene('game', new TestScene());
|
||||||
|
const menuScene = world.createScene('menu', new TestScene());
|
||||||
|
|
||||||
|
worldManager.setWorldActive('DynamicWorld', true);
|
||||||
|
|
||||||
|
// 初始状态:只有游戏Scene激活
|
||||||
|
world.setSceneActive('game', true);
|
||||||
|
world.setSceneActive('menu', false);
|
||||||
|
|
||||||
|
Core.update(0.016);
|
||||||
|
|
||||||
|
const gameCount1 = gameScene.updateCallCount;
|
||||||
|
const menuCount1 = menuScene.updateCallCount;
|
||||||
|
|
||||||
|
// 切换到菜单
|
||||||
|
world.setSceneActive('game', false);
|
||||||
|
world.setSceneActive('menu', true);
|
||||||
|
|
||||||
|
Core.update(0.016);
|
||||||
|
|
||||||
|
const gameCount2 = gameScene.updateCallCount;
|
||||||
|
const menuCount2 = menuScene.updateCallCount;
|
||||||
|
|
||||||
|
// 验证Scene状态切换
|
||||||
|
expect(gameCount2).toBe(gameCount1); // 游戏Scene停止更新
|
||||||
|
expect(menuCount2).toBeGreaterThan(menuCount1); // 菜单Scene开始更新
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('性能和稳定性', () => {
|
||||||
|
test('大量World和Scene应该稳定运行', () => {
|
||||||
|
Core.create({ debug: false });
|
||||||
|
Core.enableWorldManager();
|
||||||
|
|
||||||
|
const worldManager = Core.getWorldManager();
|
||||||
|
const worldCount = 20;
|
||||||
|
const scenePerWorld = 3;
|
||||||
|
|
||||||
|
// 创建大量World和Scene
|
||||||
|
for (let i = 0; i < worldCount; i++) {
|
||||||
|
const world = worldManager.createWorld(`World${i}`);
|
||||||
|
|
||||||
|
for (let j = 0; j < scenePerWorld; j++) {
|
||||||
|
const scene = world.createScene(`Scene${j}`, new TestScene());
|
||||||
|
|
||||||
|
// 添加一些实体
|
||||||
|
for (let k = 0; k < 5; k++) {
|
||||||
|
const entity = scene.createEntity(`Entity${k}`);
|
||||||
|
entity.addComponent(new TestComponent(k));
|
||||||
|
}
|
||||||
|
|
||||||
|
world.setSceneActive(`Scene${j}`, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
worldManager.setWorldActive(`World${i}`, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证所有资源创建成功
|
||||||
|
expect(worldManager.getWorldIds()).toHaveLength(worldCount);
|
||||||
|
expect(worldManager.getActiveWorlds()).toHaveLength(worldCount);
|
||||||
|
|
||||||
|
// 执行多次更新测试稳定性
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
expect(() => {
|
||||||
|
Core.update(0.016);
|
||||||
|
}).not.toThrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证更新正常工作
|
||||||
|
const activeWorlds = worldManager.getActiveWorlds();
|
||||||
|
activeWorlds.forEach(world => {
|
||||||
|
const scenes = world.getAllScenes();
|
||||||
|
scenes.forEach(scene => {
|
||||||
|
if (scene instanceof TestScene && world.isSceneActive(scene.name)) {
|
||||||
|
expect(scene.updateCallCount).toBeGreaterThan(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('频繁的World创建和销毁应该不影响性能', () => {
|
||||||
|
Core.create({ debug: false });
|
||||||
|
Core.enableWorldManager();
|
||||||
|
|
||||||
|
const worldManager = Core.getWorldManager();
|
||||||
|
|
||||||
|
// 频繁创建和销毁World
|
||||||
|
for (let cycle = 0; cycle < 10; cycle++) {
|
||||||
|
// 创建批次World
|
||||||
|
const worldIds: string[] = [];
|
||||||
|
for (let i = 0; i < 5; i++) {
|
||||||
|
const worldId = `Cycle${cycle}_World${i}`;
|
||||||
|
worldIds.push(worldId);
|
||||||
|
|
||||||
|
const world = worldManager.createWorld(worldId);
|
||||||
|
const scene = world.createScene('test');
|
||||||
|
scene.createEntity('entity');
|
||||||
|
|
||||||
|
worldManager.setWorldActive(worldId, true);
|
||||||
|
world.setSceneActive('test', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新一次
|
||||||
|
Core.update(0.016);
|
||||||
|
|
||||||
|
// 销毁批次World
|
||||||
|
worldIds.forEach(id => {
|
||||||
|
worldManager.removeWorld(id);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 验证清理完成
|
||||||
|
expect(worldManager.getWorldIds()).toHaveLength(0);
|
||||||
|
expect(worldManager.getActiveWorlds()).toHaveLength(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('错误处理和边界情况', () => {
|
||||||
|
test('Core未初始化时操作应该抛出合适错误', () => {
|
||||||
|
// getScene 会返回 null 而不是抛出错误
|
||||||
|
expect(Core.getScene()).toBeNull();
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
Core.setScene(new Scene());
|
||||||
|
}).toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('在World销毁后继续操作应该安全', () => {
|
||||||
|
Core.create({ debug: false });
|
||||||
|
Core.enableWorldManager();
|
||||||
|
|
||||||
|
const worldManager = Core.getWorldManager();
|
||||||
|
const world = worldManager.createWorld('DestroyTest');
|
||||||
|
|
||||||
|
worldManager.setWorldActive('DestroyTest', true);
|
||||||
|
worldManager.removeWorld('DestroyTest');
|
||||||
|
|
||||||
|
// 对已销毁的World进行操作应该不会崩溃
|
||||||
|
expect(() => {
|
||||||
|
world.updateGlobalSystems();
|
||||||
|
world.updateScenes();
|
||||||
|
}).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('混合使用单Scene和多World模式', () => {
|
||||||
|
Core.create({ debug: false });
|
||||||
|
|
||||||
|
// 直接启用WorldManager(避免先使用单Scene创建限制性配置)
|
||||||
|
const worldManager = Core.getWorldManager();
|
||||||
|
|
||||||
|
// 然后使用单Scene模式
|
||||||
|
const singleScene = new Scene();
|
||||||
|
Core.setScene(singleScene);
|
||||||
|
|
||||||
|
// 验证默认World被创建
|
||||||
|
expect(worldManager.getWorld('__default__')).toBeDefined();
|
||||||
|
|
||||||
|
// 创建额外的World
|
||||||
|
const extraWorld = worldManager.createWorld('ExtraWorld');
|
||||||
|
worldManager.setWorldActive('ExtraWorld', true);
|
||||||
|
|
||||||
|
// 两种模式应该能共存
|
||||||
|
expect(() => {
|
||||||
|
Core.update(0.016);
|
||||||
|
}).not.toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
466
packages/core/tests/ECS/World.test.ts
Normal file
466
packages/core/tests/ECS/World.test.ts
Normal file
@@ -0,0 +1,466 @@
|
|||||||
|
import { World, IWorldConfig, IGlobalSystem } from '../../src/ECS/World';
|
||||||
|
import { Scene } from '../../src/ECS/Scene';
|
||||||
|
import { EntitySystem } from '../../src/ECS/Systems/EntitySystem';
|
||||||
|
import { Entity } from '../../src/ECS/Entity';
|
||||||
|
import { Component } from '../../src/ECS/Component';
|
||||||
|
import { Matcher } from '../../src/ECS/Utils/Matcher';
|
||||||
|
|
||||||
|
// 测试用组件
|
||||||
|
class TestComponent extends Component {
|
||||||
|
public value: number = 0;
|
||||||
|
|
||||||
|
constructor(value: number = 0) {
|
||||||
|
super();
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PlayerComponent extends Component {
|
||||||
|
public playerId: string;
|
||||||
|
|
||||||
|
constructor(playerId: string) {
|
||||||
|
super();
|
||||||
|
this.playerId = playerId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试用全局系统
|
||||||
|
class TestGlobalSystem implements IGlobalSystem {
|
||||||
|
public readonly name = 'TestGlobalSystem';
|
||||||
|
public updateCount: number = 0;
|
||||||
|
|
||||||
|
public initialize(): void {
|
||||||
|
// 初始化逻辑
|
||||||
|
}
|
||||||
|
|
||||||
|
public update(): void {
|
||||||
|
this.updateCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public reset(): void {
|
||||||
|
this.updateCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public destroy(): void {
|
||||||
|
// 销毁逻辑
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestSceneSystem extends EntitySystem {
|
||||||
|
public updateCount = 0;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(Matcher.empty().all(PlayerComponent));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override process(): void {
|
||||||
|
this.updateCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试用Scene
|
||||||
|
class TestScene extends Scene {
|
||||||
|
public initializeCalled = false;
|
||||||
|
public beginCalled = false;
|
||||||
|
public endCalled = false;
|
||||||
|
|
||||||
|
public override initialize(): void {
|
||||||
|
this.initializeCalled = true;
|
||||||
|
super.initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override begin(): void {
|
||||||
|
this.beginCalled = true;
|
||||||
|
super.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override end(): void {
|
||||||
|
this.endCalled = true;
|
||||||
|
super.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('World', () => {
|
||||||
|
let world: World;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
world = new World({ name: 'TestWorld' });
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
if (world) {
|
||||||
|
world.destroy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('基础功能', () => {
|
||||||
|
test('创建World时应该设置正确的配置', () => {
|
||||||
|
const config: IWorldConfig = {
|
||||||
|
name: 'GameWorld',
|
||||||
|
debug: true,
|
||||||
|
maxScenes: 5,
|
||||||
|
autoCleanup: false
|
||||||
|
};
|
||||||
|
|
||||||
|
const testWorld = new World(config);
|
||||||
|
|
||||||
|
expect(testWorld.name).toBe('GameWorld');
|
||||||
|
expect(testWorld.sceneCount).toBe(0);
|
||||||
|
expect(testWorld.isActive).toBe(false);
|
||||||
|
expect(testWorld.createdAt).toBeGreaterThan(0);
|
||||||
|
|
||||||
|
testWorld.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('默认配置应该正确', () => {
|
||||||
|
const defaultWorld = new World();
|
||||||
|
|
||||||
|
expect(defaultWorld.name).toBe('World');
|
||||||
|
expect(defaultWorld.sceneCount).toBe(0);
|
||||||
|
expect(defaultWorld.isActive).toBe(false);
|
||||||
|
|
||||||
|
defaultWorld.destroy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Scene管理', () => {
|
||||||
|
test('创建Scene应该成功', () => {
|
||||||
|
const scene = world.createScene('test-scene');
|
||||||
|
|
||||||
|
expect(scene).toBeDefined();
|
||||||
|
expect(world.sceneCount).toBe(1);
|
||||||
|
expect(world.getSceneIds()).toContain('test-scene');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('创建Scene时传入自定义Scene实例', () => {
|
||||||
|
const customScene = new TestScene();
|
||||||
|
const scene = world.createScene('custom-scene', customScene);
|
||||||
|
|
||||||
|
expect(scene).toBe(customScene);
|
||||||
|
expect(scene.initializeCalled).toBe(true);
|
||||||
|
expect(world.sceneCount).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('重复的Scene ID应该抛出错误', () => {
|
||||||
|
world.createScene('duplicate');
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
world.createScene('duplicate');
|
||||||
|
}).toThrow("Scene ID 'duplicate' 已存在于World 'TestWorld' 中");
|
||||||
|
});
|
||||||
|
|
||||||
|
test('超出最大Scene数量限制应该抛出错误', () => {
|
||||||
|
const limitedWorld = new World({ maxScenes: 2 });
|
||||||
|
|
||||||
|
limitedWorld.createScene('scene1');
|
||||||
|
limitedWorld.createScene('scene2');
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
limitedWorld.createScene('scene3');
|
||||||
|
}).toThrow("World 'World' 已达到最大Scene数量限制: 2");
|
||||||
|
|
||||||
|
limitedWorld.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('获取Scene应该正确', () => {
|
||||||
|
const scene = world.createScene('get-test');
|
||||||
|
const retrievedScene = world.getScene('get-test');
|
||||||
|
|
||||||
|
expect(retrievedScene).toBe(scene);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('获取不存在的Scene应该返回null', () => {
|
||||||
|
const scene = world.getScene('non-existent');
|
||||||
|
expect(scene).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('移除Scene应该正确清理', () => {
|
||||||
|
const testScene = new TestScene();
|
||||||
|
world.createScene('remove-test', testScene);
|
||||||
|
world.setSceneActive('remove-test', true);
|
||||||
|
|
||||||
|
const removed = world.removeScene('remove-test');
|
||||||
|
|
||||||
|
expect(removed).toBe(true);
|
||||||
|
expect(world.sceneCount).toBe(0);
|
||||||
|
expect(world.getScene('remove-test')).toBeNull();
|
||||||
|
expect(testScene.endCalled).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('移除不存在的Scene应该返回false', () => {
|
||||||
|
const removed = world.removeScene('non-existent');
|
||||||
|
expect(removed).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('获取所有Scene应该正确', () => {
|
||||||
|
const scene1 = world.createScene('scene1');
|
||||||
|
const scene2 = world.createScene('scene2');
|
||||||
|
|
||||||
|
const allScenes = world.getAllScenes();
|
||||||
|
|
||||||
|
expect(allScenes).toHaveLength(2);
|
||||||
|
expect(allScenes).toContain(scene1);
|
||||||
|
expect(allScenes).toContain(scene2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Scene激活管理', () => {
|
||||||
|
test('激活Scene应该正确', () => {
|
||||||
|
const testScene = new TestScene();
|
||||||
|
world.createScene('active-test', testScene);
|
||||||
|
|
||||||
|
world.setSceneActive('active-test', true);
|
||||||
|
|
||||||
|
expect(world.isSceneActive('active-test')).toBe(true);
|
||||||
|
expect(world.getActiveSceneCount()).toBe(1);
|
||||||
|
expect(testScene.beginCalled).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('停用Scene应该正确', () => {
|
||||||
|
world.createScene('deactive-test');
|
||||||
|
world.setSceneActive('deactive-test', true);
|
||||||
|
|
||||||
|
world.setSceneActive('deactive-test', false);
|
||||||
|
|
||||||
|
expect(world.isSceneActive('deactive-test')).toBe(false);
|
||||||
|
expect(world.getActiveSceneCount()).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('激活不存在的Scene应该记录警告', () => {
|
||||||
|
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
|
||||||
|
|
||||||
|
world.setSceneActive('non-existent', true);
|
||||||
|
|
||||||
|
// 注意:这里需要检查具体的日志实现,可能需要调整
|
||||||
|
consoleSpy.mockRestore();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('全局System管理', () => {
|
||||||
|
test('添加全局System应该成功', () => {
|
||||||
|
const globalSystem = new TestGlobalSystem();
|
||||||
|
|
||||||
|
const addedSystem = world.addGlobalSystem(globalSystem);
|
||||||
|
|
||||||
|
expect(addedSystem).toBe(globalSystem);
|
||||||
|
expect(world.getGlobalSystem(TestGlobalSystem)).toBe(globalSystem);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('重复添加相同System应该返回原System', () => {
|
||||||
|
const globalSystem = new TestGlobalSystem();
|
||||||
|
|
||||||
|
const firstAdd = world.addGlobalSystem(globalSystem);
|
||||||
|
const secondAdd = world.addGlobalSystem(globalSystem);
|
||||||
|
|
||||||
|
expect(firstAdd).toBe(secondAdd);
|
||||||
|
expect(firstAdd).toBe(globalSystem);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('移除全局System应该成功', () => {
|
||||||
|
const globalSystem = new TestGlobalSystem();
|
||||||
|
world.addGlobalSystem(globalSystem);
|
||||||
|
|
||||||
|
const removed = world.removeGlobalSystem(globalSystem);
|
||||||
|
|
||||||
|
expect(removed).toBe(true);
|
||||||
|
expect(world.getGlobalSystem(TestGlobalSystem)).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('移除不存在的System应该返回false', () => {
|
||||||
|
const globalSystem = new TestGlobalSystem();
|
||||||
|
|
||||||
|
const removed = world.removeGlobalSystem(globalSystem);
|
||||||
|
|
||||||
|
expect(removed).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('获取不存在的System类型应该返回null', () => {
|
||||||
|
const system = world.getGlobalSystem(TestGlobalSystem);
|
||||||
|
expect(system).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('World生命周期', () => {
|
||||||
|
test('启动World应该正确', () => {
|
||||||
|
const globalSystem = new TestGlobalSystem();
|
||||||
|
world.addGlobalSystem(globalSystem);
|
||||||
|
|
||||||
|
world.start();
|
||||||
|
|
||||||
|
expect(world.isActive).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('重复启动World应该无效果', () => {
|
||||||
|
world.start();
|
||||||
|
const firstActive = world.isActive;
|
||||||
|
|
||||||
|
world.start();
|
||||||
|
|
||||||
|
expect(world.isActive).toBe(firstActive);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('停止World应该停用所有Scene', () => {
|
||||||
|
const testScene = new TestScene();
|
||||||
|
world.createScene('stop-test', testScene);
|
||||||
|
world.setSceneActive('stop-test', true);
|
||||||
|
world.start();
|
||||||
|
|
||||||
|
world.stop();
|
||||||
|
|
||||||
|
expect(world.isActive).toBe(false);
|
||||||
|
expect(world.isSceneActive('stop-test')).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('销毁World应该清理所有资源', () => {
|
||||||
|
const testScene = new TestScene();
|
||||||
|
const globalSystem = new TestGlobalSystem();
|
||||||
|
|
||||||
|
world.createScene('destroy-test', testScene);
|
||||||
|
world.addGlobalSystem(globalSystem);
|
||||||
|
world.start();
|
||||||
|
|
||||||
|
world.destroy();
|
||||||
|
|
||||||
|
expect(world.sceneCount).toBe(0);
|
||||||
|
expect(world.isActive).toBe(false);
|
||||||
|
expect(testScene.endCalled).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('更新逻辑', () => {
|
||||||
|
test('updateGlobalSystems应该更新全局系统', () => {
|
||||||
|
const globalSystem = new TestGlobalSystem();
|
||||||
|
world.addGlobalSystem(globalSystem);
|
||||||
|
world.start();
|
||||||
|
|
||||||
|
// 创建测试Scene
|
||||||
|
const scene = world.createScene('update-test');
|
||||||
|
world.setSceneActive('update-test', true);
|
||||||
|
|
||||||
|
// 直接测试全局系统更新
|
||||||
|
world.updateGlobalSystems();
|
||||||
|
|
||||||
|
// 验证全局System被正确调用
|
||||||
|
expect(globalSystem.updateCount).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('未激活的World不应该更新', () => {
|
||||||
|
const globalSystem = new TestGlobalSystem();
|
||||||
|
world.addGlobalSystem(globalSystem);
|
||||||
|
// 不启动World
|
||||||
|
|
||||||
|
world.updateGlobalSystems();
|
||||||
|
|
||||||
|
expect(globalSystem.updateCount).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('updateScenes应该更新激活的Scene', () => {
|
||||||
|
const scene1 = world.createScene('scene1');
|
||||||
|
const scene2 = world.createScene('scene2');
|
||||||
|
|
||||||
|
scene1.addEntityProcessor(new TestSceneSystem());
|
||||||
|
scene2.addEntityProcessor(new TestSceneSystem());
|
||||||
|
|
||||||
|
world.start();
|
||||||
|
world.setSceneActive('scene1', true);
|
||||||
|
// scene2保持未激活
|
||||||
|
|
||||||
|
world.updateScenes();
|
||||||
|
|
||||||
|
// 这里需要根据具体的Scene更新实现来验证
|
||||||
|
// 由于Scene.update()的具体实现可能不同,这里主要测试调用不出错
|
||||||
|
expect(() => world.updateScenes()).not.toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('状态和统计', () => {
|
||||||
|
test('获取World状态应该正确', () => {
|
||||||
|
world.createScene('status-scene1');
|
||||||
|
world.createScene('status-scene2');
|
||||||
|
world.setSceneActive('status-scene1', true);
|
||||||
|
world.addGlobalSystem(new TestGlobalSystem());
|
||||||
|
world.start();
|
||||||
|
|
||||||
|
const status = world.getStatus();
|
||||||
|
|
||||||
|
expect(status.name).toBe('TestWorld');
|
||||||
|
expect(status.isActive).toBe(true);
|
||||||
|
expect(status.sceneCount).toBe(2);
|
||||||
|
expect(status.activeSceneCount).toBe(1);
|
||||||
|
expect(status.globalSystemCount).toBe(1);
|
||||||
|
expect(status.createdAt).toBeGreaterThan(0);
|
||||||
|
expect(status.scenes).toHaveLength(2);
|
||||||
|
|
||||||
|
const activeScene = status.scenes.find(s => s.id === 'status-scene1');
|
||||||
|
expect(activeScene?.isActive).toBe(true);
|
||||||
|
|
||||||
|
const inactiveScene = status.scenes.find(s => s.id === 'status-scene2');
|
||||||
|
expect(inactiveScene?.isActive).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('获取World统计应该包含基本信息', () => {
|
||||||
|
world.addGlobalSystem(new TestGlobalSystem());
|
||||||
|
|
||||||
|
const scene = world.createScene('stats-scene');
|
||||||
|
const entity = scene.createEntity('stats-entity');
|
||||||
|
entity.addComponent(new TestComponent());
|
||||||
|
|
||||||
|
const stats = world.getStats();
|
||||||
|
|
||||||
|
expect(stats).toHaveProperty('totalEntities');
|
||||||
|
expect(stats).toHaveProperty('totalSystems');
|
||||||
|
expect(stats).toHaveProperty('memoryUsage');
|
||||||
|
expect(stats).toHaveProperty('performance');
|
||||||
|
expect(stats.totalSystems).toBeGreaterThanOrEqual(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('自动清理功能', () => {
|
||||||
|
test('自动清理应该移除空闲Scene', async () => {
|
||||||
|
// 创建一个启用自动清理的World
|
||||||
|
const autoCleanWorld = new World({
|
||||||
|
name: 'AutoCleanWorld',
|
||||||
|
autoCleanup: true,
|
||||||
|
maxScenes: 10
|
||||||
|
});
|
||||||
|
|
||||||
|
// 创建一个空Scene
|
||||||
|
autoCleanWorld.createScene('empty-scene');
|
||||||
|
autoCleanWorld.start();
|
||||||
|
|
||||||
|
// 手动触发清理检查
|
||||||
|
autoCleanWorld.updateScenes();
|
||||||
|
|
||||||
|
// 由于清理策略基于时间,这里主要测试不会出错
|
||||||
|
expect(() => autoCleanWorld.updateScenes()).not.toThrow();
|
||||||
|
|
||||||
|
autoCleanWorld.destroy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('错误处理', () => {
|
||||||
|
test('Scene ID为空时应该创建默认ID', () => {
|
||||||
|
expect(() => {
|
||||||
|
world.createScene('');
|
||||||
|
}).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('极限情况下的资源管理', () => {
|
||||||
|
// 创建大量Scene
|
||||||
|
for (let i = 0; i < 5; i++) {
|
||||||
|
world.createScene(`scene_${i}`);
|
||||||
|
world.setSceneActive(`scene_${i}`, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加多个全局System
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
world.addGlobalSystem(new TestGlobalSystem());
|
||||||
|
}
|
||||||
|
|
||||||
|
world.start();
|
||||||
|
|
||||||
|
// 测试批量清理
|
||||||
|
expect(() => world.destroy()).not.toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
464
packages/core/tests/ECS/WorldManager.test.ts
Normal file
464
packages/core/tests/ECS/WorldManager.test.ts
Normal file
@@ -0,0 +1,464 @@
|
|||||||
|
import { WorldManager, IWorldManagerConfig } from '../../src/ECS/WorldManager';
|
||||||
|
import { World, IWorldConfig } from '../../src/ECS/World';
|
||||||
|
import { Scene } from '../../src/ECS/Scene';
|
||||||
|
import { EntitySystem } from '../../src/ECS/Systems/EntitySystem';
|
||||||
|
import { Component } from '../../src/ECS/Component';
|
||||||
|
import { Matcher } from '../../src/ECS/Utils/Matcher';
|
||||||
|
|
||||||
|
// 测试用组件
|
||||||
|
class TestComponent extends Component {
|
||||||
|
public value: number = 0;
|
||||||
|
|
||||||
|
constructor(value: number = 0) {
|
||||||
|
super();
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试用全局系统
|
||||||
|
class TestGlobalSystem {
|
||||||
|
public readonly name = 'TestGlobalSystem';
|
||||||
|
public updateCount: number = 0;
|
||||||
|
|
||||||
|
public initialize(): void {
|
||||||
|
// 初始化
|
||||||
|
}
|
||||||
|
|
||||||
|
public update(): void {
|
||||||
|
this.updateCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public reset(): void {
|
||||||
|
this.updateCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public destroy(): void {
|
||||||
|
// 销毁
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('WorldManager', () => {
|
||||||
|
let worldManager: WorldManager;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
// 重置单例
|
||||||
|
WorldManager['_instance'] = null;
|
||||||
|
worldManager = WorldManager.getInstance();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
// 清理所有World
|
||||||
|
if (worldManager) {
|
||||||
|
const worldIds = worldManager.getWorldIds();
|
||||||
|
worldIds.forEach(id => {
|
||||||
|
worldManager.removeWorld(id);
|
||||||
|
});
|
||||||
|
// 清理定时器
|
||||||
|
worldManager.destroy();
|
||||||
|
}
|
||||||
|
WorldManager['_instance'] = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('单例模式', () => {
|
||||||
|
test('获取实例应该返回相同的实例', () => {
|
||||||
|
const instance1 = WorldManager.getInstance();
|
||||||
|
const instance2 = WorldManager.getInstance();
|
||||||
|
|
||||||
|
expect(instance1).toBe(instance2);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('使用配置创建实例应该正确', () => {
|
||||||
|
WorldManager['_instance'] = null;
|
||||||
|
|
||||||
|
const config: IWorldManagerConfig = {
|
||||||
|
maxWorlds: 10,
|
||||||
|
autoCleanup: true,
|
||||||
|
debug: false
|
||||||
|
};
|
||||||
|
|
||||||
|
const instance = WorldManager.getInstance(config);
|
||||||
|
|
||||||
|
expect(instance).toBeDefined();
|
||||||
|
expect(instance).toBe(WorldManager.getInstance());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('World管理', () => {
|
||||||
|
test('创建World应该成功', () => {
|
||||||
|
const world = worldManager.createWorld('test-world');
|
||||||
|
|
||||||
|
expect(world).toBeDefined();
|
||||||
|
expect(world.name).toBe('test-world');
|
||||||
|
expect(worldManager.getWorld('test-world')).toBeDefined();
|
||||||
|
expect(worldManager.getWorldIds()).toContain('test-world');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('创建World时传入配置应该正确', () => {
|
||||||
|
const worldConfig: IWorldConfig = {
|
||||||
|
name: 'ConfiguredWorld',
|
||||||
|
debug: true,
|
||||||
|
maxScenes: 5,
|
||||||
|
autoCleanup: false
|
||||||
|
};
|
||||||
|
|
||||||
|
const world = worldManager.createWorld('configured-world', worldConfig);
|
||||||
|
|
||||||
|
expect(world.name).toBe('ConfiguredWorld');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('重复的World ID应该抛出错误', () => {
|
||||||
|
worldManager.createWorld('duplicate-world');
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
worldManager.createWorld('duplicate-world');
|
||||||
|
}).toThrow("World ID 'duplicate-world' 已存在");
|
||||||
|
});
|
||||||
|
|
||||||
|
test('超出最大World数量应该抛出错误', () => {
|
||||||
|
WorldManager['_instance'] = null;
|
||||||
|
const limitedManager = WorldManager.getInstance({ maxWorlds: 2 });
|
||||||
|
|
||||||
|
limitedManager.createWorld('world1');
|
||||||
|
limitedManager.createWorld('world2');
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
limitedManager.createWorld('world3');
|
||||||
|
}).toThrow("已达到最大World数量限制: 2");
|
||||||
|
|
||||||
|
// 清理
|
||||||
|
limitedManager.removeWorld('world1');
|
||||||
|
limitedManager.removeWorld('world2');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('获取World应该正确', () => {
|
||||||
|
const world = worldManager.createWorld('get-world');
|
||||||
|
const retrievedWorld = worldManager.getWorld('get-world');
|
||||||
|
|
||||||
|
expect(retrievedWorld).toBe(world);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('获取不存在的World应该返回null', () => {
|
||||||
|
const world = worldManager.getWorld('non-existent');
|
||||||
|
expect(world).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('检查World存在性应该正确', () => {
|
||||||
|
expect(worldManager.getWorld('non-existent')).toBeNull();
|
||||||
|
|
||||||
|
worldManager.createWorld('exists');
|
||||||
|
expect(worldManager.getWorld('exists')).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('销毁World应该正确清理', () => {
|
||||||
|
const world = worldManager.createWorld('destroy-world');
|
||||||
|
world.start();
|
||||||
|
|
||||||
|
const destroyed = worldManager.removeWorld('destroy-world');
|
||||||
|
|
||||||
|
expect(destroyed).toBe(true);
|
||||||
|
expect(worldManager.getWorld('destroy-world')).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('销毁不存在的World应该返回false', () => {
|
||||||
|
const destroyed = worldManager.removeWorld('non-existent');
|
||||||
|
expect(destroyed).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('获取所有World ID应该正确', () => {
|
||||||
|
worldManager.createWorld('world1');
|
||||||
|
worldManager.createWorld('world2');
|
||||||
|
worldManager.createWorld('world3');
|
||||||
|
|
||||||
|
const worldIds = worldManager.getWorldIds();
|
||||||
|
|
||||||
|
expect(worldIds).toHaveLength(3);
|
||||||
|
expect(worldIds).toContain('world1');
|
||||||
|
expect(worldIds).toContain('world2');
|
||||||
|
expect(worldIds).toContain('world3');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('活跃World管理', () => {
|
||||||
|
test('启动World应该加入活跃列表', () => {
|
||||||
|
const world = worldManager.createWorld('active-world');
|
||||||
|
|
||||||
|
worldManager.setWorldActive('active-world', true);
|
||||||
|
|
||||||
|
const activeWorlds = worldManager.getActiveWorlds();
|
||||||
|
expect(activeWorlds).toHaveLength(1);
|
||||||
|
expect(activeWorlds[0]).toBe(world);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('停止World应该从活跃列表移除', () => {
|
||||||
|
const world = worldManager.createWorld('inactive-world');
|
||||||
|
worldManager.setWorldActive('inactive-world', true);
|
||||||
|
|
||||||
|
worldManager.setWorldActive('inactive-world', false);
|
||||||
|
|
||||||
|
const activeWorlds = worldManager.getActiveWorlds();
|
||||||
|
expect(activeWorlds).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('销毁激活的World应该从活跃列表移除', () => {
|
||||||
|
const world = worldManager.createWorld('destroy-active');
|
||||||
|
worldManager.setWorldActive('destroy-active', true);
|
||||||
|
|
||||||
|
worldManager.removeWorld('destroy-active');
|
||||||
|
|
||||||
|
const activeWorlds = worldManager.getActiveWorlds();
|
||||||
|
expect(activeWorlds).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('多个World的激活状态应该独立管理', () => {
|
||||||
|
const world1 = worldManager.createWorld('world1');
|
||||||
|
const world2 = worldManager.createWorld('world2');
|
||||||
|
const world3 = worldManager.createWorld('world3');
|
||||||
|
|
||||||
|
worldManager.setWorldActive('world1', true);
|
||||||
|
worldManager.setWorldActive('world3', true);
|
||||||
|
// world2 保持未启动
|
||||||
|
|
||||||
|
const activeWorlds = worldManager.getActiveWorlds();
|
||||||
|
|
||||||
|
expect(activeWorlds).toHaveLength(2);
|
||||||
|
expect(activeWorlds).toContain(world1);
|
||||||
|
expect(activeWorlds).toContain(world3);
|
||||||
|
expect(activeWorlds).not.toContain(world2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('统计和监控', () => {
|
||||||
|
test('获取WorldManager状态应该正确', () => {
|
||||||
|
worldManager.createWorld('status-world1');
|
||||||
|
const world2 = worldManager.createWorld('status-world2');
|
||||||
|
worldManager.setWorldActive('status-world2', true);
|
||||||
|
|
||||||
|
const status = worldManager.getStats();
|
||||||
|
|
||||||
|
expect(status.totalWorlds).toBe(2);
|
||||||
|
expect(status.activeWorlds).toBe(1);
|
||||||
|
expect(status.config.maxWorlds).toBeGreaterThan(0);
|
||||||
|
expect(status.memoryUsage).toBeGreaterThanOrEqual(0);
|
||||||
|
expect(status.isRunning).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('获取所有World统计应该包含详细信息', () => {
|
||||||
|
const world1 = worldManager.createWorld('stats-world1');
|
||||||
|
const world2 = worldManager.createWorld('stats-world2');
|
||||||
|
|
||||||
|
// 为world1添加一些内容
|
||||||
|
const scene1 = world1.createScene('scene1');
|
||||||
|
scene1.createEntity('entity1');
|
||||||
|
worldManager.setWorldActive('stats-world1', true);
|
||||||
|
|
||||||
|
// world2保持空
|
||||||
|
|
||||||
|
const allStats = worldManager.getDetailedStatus().worlds;
|
||||||
|
|
||||||
|
expect(allStats).toHaveLength(2);
|
||||||
|
|
||||||
|
const world1Stats = allStats.find(stat => stat.id === 'stats-world1');
|
||||||
|
const world2Stats = allStats.find(stat => stat.id === 'stats-world2');
|
||||||
|
|
||||||
|
expect(world1Stats).toBeDefined();
|
||||||
|
expect(world2Stats).toBeDefined();
|
||||||
|
expect(world1Stats?.isActive).toBe(true);
|
||||||
|
expect(world2Stats?.isActive).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('空WorldManager的统计应该正确', () => {
|
||||||
|
const status = worldManager.getStats();
|
||||||
|
const allStats = worldManager.getDetailedStatus().worlds;
|
||||||
|
|
||||||
|
expect(status.totalWorlds).toBe(0);
|
||||||
|
expect(status.activeWorlds).toBe(0);
|
||||||
|
expect(allStats).toHaveLength(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('清理功能', () => {
|
||||||
|
test('清理空闲World应该移除符合条件的World', () => {
|
||||||
|
// 创建一个空的World
|
||||||
|
const emptyWorld = worldManager.createWorld('empty-world');
|
||||||
|
|
||||||
|
// 创建一个有内容的World
|
||||||
|
const fullWorld = worldManager.createWorld('full-world');
|
||||||
|
const scene = fullWorld.createScene('scene');
|
||||||
|
scene.createEntity('entity');
|
||||||
|
fullWorld.start();
|
||||||
|
|
||||||
|
// 执行清理
|
||||||
|
const cleanedCount = worldManager.cleanup();
|
||||||
|
|
||||||
|
// 由于清理逻辑可能基于时间或其他条件,这里主要测试不会出错
|
||||||
|
expect(cleanedCount).toBeGreaterThanOrEqual(0);
|
||||||
|
expect(() => worldManager.cleanup()).not.toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('World更新协调', () => {
|
||||||
|
test('更新所有活跃World应该正确', () => {
|
||||||
|
const world1 = worldManager.createWorld('update-world1');
|
||||||
|
const world2 = worldManager.createWorld('update-world2');
|
||||||
|
const world3 = worldManager.createWorld('update-world3');
|
||||||
|
|
||||||
|
// 添加一些内容到World中
|
||||||
|
const scene1 = world1.createScene('scene1');
|
||||||
|
const scene2 = world2.createScene('scene2');
|
||||||
|
|
||||||
|
scene1.createEntity('entity1');
|
||||||
|
scene2.createEntity('entity2');
|
||||||
|
|
||||||
|
// 启动部分World
|
||||||
|
worldManager.setWorldActive('update-world1', true);
|
||||||
|
worldManager.setWorldActive('update-world2', true);
|
||||||
|
// world3保持未启动
|
||||||
|
|
||||||
|
// 手动调用更新(通常由Core.update()调用)
|
||||||
|
const activeWorlds = worldManager.getActiveWorlds();
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
activeWorlds.forEach(world => {
|
||||||
|
world.updateGlobalSystems();
|
||||||
|
world.updateScenes();
|
||||||
|
});
|
||||||
|
}).not.toThrow();
|
||||||
|
|
||||||
|
expect(activeWorlds).toHaveLength(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('边界情况和错误处理', () => {
|
||||||
|
test('World ID为空字符串应该抛出错误', () => {
|
||||||
|
expect(() => {
|
||||||
|
worldManager.createWorld('');
|
||||||
|
}).toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('World ID为null或undefined应该抛出错误', () => {
|
||||||
|
expect(() => {
|
||||||
|
worldManager.createWorld(null as any);
|
||||||
|
}).toThrow();
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
worldManager.createWorld(undefined as any);
|
||||||
|
}).toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('极限情况下的大量World管理', () => {
|
||||||
|
const worldCount = 50;
|
||||||
|
const worldIds: string[] = [];
|
||||||
|
|
||||||
|
// 创建大量World
|
||||||
|
for (let i = 0; i < worldCount; i++) {
|
||||||
|
const worldId = `mass-world-${i}`;
|
||||||
|
worldIds.push(worldId);
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
worldManager.createWorld(worldId);
|
||||||
|
}).not.toThrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(worldManager.getWorldIds()).toHaveLength(worldCount);
|
||||||
|
|
||||||
|
// 启动一半的World
|
||||||
|
for (let i = 0; i < worldCount / 2; i++) {
|
||||||
|
worldManager.setWorldActive(worldIds[i], true);
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(worldManager.getActiveWorlds()).toHaveLength(worldCount / 2);
|
||||||
|
|
||||||
|
// 批量清理
|
||||||
|
worldIds.forEach(id => {
|
||||||
|
expect(() => {
|
||||||
|
worldManager.removeWorld(id);
|
||||||
|
}).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(worldManager.getWorldIds()).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('销毁后获取World应该返回null', () => {
|
||||||
|
worldManager.createWorld('temp-world');
|
||||||
|
worldManager.removeWorld('temp-world');
|
||||||
|
|
||||||
|
expect(worldManager.getWorld('temp-world')).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('内存管理', () => {
|
||||||
|
test('销毁所有World后内存应该被释放', () => {
|
||||||
|
// 创建多个World并添加内容
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
const world = worldManager.createWorld(`memory-world-${i}`);
|
||||||
|
const scene = world.createScene('scene');
|
||||||
|
|
||||||
|
// 添加一些实体和系统
|
||||||
|
for (let j = 0; j < 5; j++) {
|
||||||
|
const entity = scene.createEntity(`entity-${j}`);
|
||||||
|
entity.addComponent(new TestComponent(j));
|
||||||
|
}
|
||||||
|
|
||||||
|
world.addGlobalSystem(new TestGlobalSystem());
|
||||||
|
worldManager.setWorldActive(`memory-world-${i}`, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const beforeCleanup = worldManager.getStats();
|
||||||
|
expect(beforeCleanup.totalWorlds).toBe(10);
|
||||||
|
expect(beforeCleanup.activeWorlds).toBe(10);
|
||||||
|
|
||||||
|
// 清理所有World
|
||||||
|
const worldIds = worldManager.getWorldIds();
|
||||||
|
worldIds.forEach(id => {
|
||||||
|
worldManager.removeWorld(id);
|
||||||
|
});
|
||||||
|
|
||||||
|
const afterCleanup = worldManager.getStats();
|
||||||
|
expect(afterCleanup.totalWorlds).toBe(0);
|
||||||
|
expect(afterCleanup.activeWorlds).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('配置验证', () => {
|
||||||
|
test('无效的maxWorlds配置应该使用默认值', () => {
|
||||||
|
WorldManager['_instance'] = null;
|
||||||
|
|
||||||
|
const invalidConfig: IWorldManagerConfig = {
|
||||||
|
maxWorlds: -1,
|
||||||
|
autoCleanup: true,
|
||||||
|
debug: true
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
WorldManager.getInstance(invalidConfig);
|
||||||
|
}).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('配置更新应该影响后续操作', () => {
|
||||||
|
WorldManager['_instance'] = null;
|
||||||
|
|
||||||
|
const config: IWorldManagerConfig = {
|
||||||
|
maxWorlds: 3,
|
||||||
|
autoCleanup: true,
|
||||||
|
debug: true
|
||||||
|
};
|
||||||
|
|
||||||
|
const manager = WorldManager.getInstance(config);
|
||||||
|
|
||||||
|
// 创建到限制数量的World
|
||||||
|
manager.createWorld('world1');
|
||||||
|
manager.createWorld('world2');
|
||||||
|
manager.createWorld('world3');
|
||||||
|
|
||||||
|
// 第四个应该失败
|
||||||
|
expect(() => {
|
||||||
|
manager.createWorld('world4');
|
||||||
|
}).toThrow();
|
||||||
|
|
||||||
|
// 清理
|
||||||
|
manager.removeWorld('world1');
|
||||||
|
manager.removeWorld('world2');
|
||||||
|
manager.removeWorld('world3');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user