新增cocos-debug-profiler

This commit is contained in:
YHH
2025-06-17 00:32:16 +08:00
parent 103f773286
commit 1b5363611d
46 changed files with 6757 additions and 37 deletions

View File

@@ -21,6 +21,9 @@
},
{
"__id__": 5
},
{
"__id__": 7
}
],
"_active": true,
@@ -55,7 +58,7 @@
},
"autoReleaseAssets": false,
"_globals": {
"__id__": 7
"__id__": 9
},
"_id": "ff354f0b-c2f5-4dea-8ffb-0152d175d11c"
},
@@ -246,32 +249,90 @@
"_trackingType": 0,
"_id": "7dWQTpwS5LrIHnc1zAPUtf"
},
{
"__type__": "cc.Node",
"_name": "ECSManager",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
"__id__": 1
},
"_children": [],
"_active": true,
"_components": [
{
"__id__": 8
}
],
"_prefab": null,
"_lpos": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
},
"_lrot": {
"__type__": "cc.Quat",
"x": 0,
"y": 0,
"z": 0,
"w": 1
},
"_lscale": {
"__type__": "cc.Vec3",
"x": 1,
"y": 1,
"z": 1
},
"_mobility": 0,
"_layer": 1073741824,
"_euler": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
},
"_id": "43H+4zZ5xK2rblF/7Lha6k"
},
{
"__type__": "c82e7kJAeZJyraNnumIN+I4",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 7
},
"_enabled": true,
"__prefab": null,
"debugMode": true,
"_id": "40G/Xl9EBLJ7amO+29wrkO"
},
{
"__type__": "cc.SceneGlobals",
"ambient": {
"__id__": 8
},
"shadows": {
"__id__": 9
},
"_skybox": {
"__id__": 10
},
"fog": {
"shadows": {
"__id__": 11
},
"octree": {
"_skybox": {
"__id__": 12
},
"skin": {
"fog": {
"__id__": 13
},
"lightProbeInfo": {
"octree": {
"__id__": 14
},
"postSettings": {
"skin": {
"__id__": 15
},
"lightProbeInfo": {
"__id__": 16
},
"postSettings": {
"__id__": 17
},
"bakedWithStationaryMainLight": false,
"bakedWithHighpLightmap": false
},

View File

@@ -0,0 +1,9 @@
{
"ver": "1.2.0",
"importer": "directory",
"imported": true,
"uuid": "6f9217a1-dff6-4460-b5da-eb01cf29c03c",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,99 @@
import { Core } from '@esengine/ecs-framework';
import { Component, _decorator } from 'cc';
import { ExampleGameScene } from './scenes/ExampleGameScene';
const { ccclass, property } = _decorator;
/**
* ECS管理器 - Cocos Creator组件
* 将此组件添加到场景中的任意节点上即可启动ECS框架
*
* 使用说明:
* 1. 在Cocos Creator场景中创建一个空节点
* 2. 将此ECSManager组件添加到该节点
* 3. 运行场景即可自动启动ECS框架
*/
@ccclass('ECSManager')
export class ECSManager extends Component {
@property({
tooltip: '是否启用调试模式(建议开发阶段开启)'
})
public debugMode: boolean = true;
private isInitialized: boolean = false;
/**
* 组件启动时初始化ECS
*/
start() {
this.initializeECS();
}
/**
* 初始化ECS框架
*/
private initializeECS(): void {
if (this.isInitialized) return;
console.log('🎮 正在初始化ECS框架...');
try {
// 1. 创建Core实例启用调试功能
if (this.debugMode) {
Core.create({
debugConfig: {
enabled: true,
websocketUrl: 'ws://localhost:8080/ecs-debug',
autoReconnect: true,
updateInterval: 1000,
channels: {
entities: true,
systems: true,
performance: true,
components: true,
scenes: true
}
}
});
console.log('🔧 ECS调试模式已启用可在Cocos Creator扩展面板中查看调试信息');
} else {
Core.create(false);
}
// 2. 创建游戏场景
const gameScene = new ExampleGameScene();
// 3. 设置为当前场景会自动调用scene.begin()
Core.scene = gameScene;
this.isInitialized = true;
console.log('✅ ECS框架初始化成功');
console.log('📖 请查看 assets/scripts/ecs/README.md 了解如何添加组件和系统');
} catch (error) {
console.error('❌ ECS框架初始化失败:', error);
}
}
/**
* 每帧更新ECS框架
*/
update(deltaTime: number) {
if (this.isInitialized) {
// 更新ECS核心系统
Core.update(deltaTime);
}
}
/**
* 组件销毁时清理ECS
*/
onDestroy() {
if (this.isInitialized) {
console.log('🧹 清理ECS框架...');
// ECS框架会自动处理场景清理
this.isInitialized = false;
}
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "c82e7909-01e6-49ca-b68d-9ee98837e238",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,153 @@
# ECS框架启动模板
欢迎使用ECS框架这是一个最基础的启动模板帮助您快速开始ECS项目开发。
## 📁 项目结构
```
ecs/
├── components/ # 组件目录(请在此添加您的组件)
├── systems/ # 系统目录(请在此添加您的系统)
├── scenes/ # 场景目录
│ └── GameScene.ts # 主游戏场景
├── ECSManager.ts # ECS管理器组件
└── README.md # 本文档
```
## 🚀 快速开始
### 1. 启动ECS框架
ECS框架已经配置完成您只需要
1. 在Cocos Creator中打开您的场景
2. 创建一个空节点(例如命名为"ECSManager"
3.`ECSManager` 组件添加到该节点
4. 运行场景ECS框架将自动启动
### 2. 查看控制台输出
如果一切正常,您将在控制台看到:
```
🎮 正在初始化ECS框架...
🔧 ECS调试模式已启用可在Cocos Creator扩展面板中查看调试信息
🎯 游戏场景已创建
✅ ECS框架初始化成功
🚀 游戏场景已启动
```
### 3. 使用调试面板
ECS框架已启用调试功能您可以
1. 在Cocos Creator编辑器菜单中选择 "扩展" → "ECS Framework" → "调试面板"
2. 调试面板将显示实时的ECS运行状态
- 实体数量和状态
- 系统执行信息
- 性能监控数据
- 组件统计信息
**注意**:调试功能会消耗一定性能,正式发布时建议关闭调试模式。
## 📚 下一步开发
### 创建您的第一个组件
`components/` 目录下创建组件:
```typescript
// components/PositionComponent.ts
import { Component } from '@esengine/ecs-framework';
import { Vec3 } from 'cc';
export class PositionComponent extends Component {
public position: Vec3 = new Vec3();
constructor(x: number = 0, y: number = 0, z: number = 0) {
super();
this.position.set(x, y, z);
}
}
```
### 创建您的第一个系统
`systems/` 目录下创建系统:
```typescript
// systems/MovementSystem.ts
import { EntitySystem, Entity, Matcher } from '@esengine/ecs-framework';
import { PositionComponent } from '../components/PositionComponent';
export class MovementSystem extends EntitySystem {
constructor() {
super(Matcher.empty().all(PositionComponent));
}
protected process(entities: Entity[]): void {
for (const entity of entities) {
const position = entity.getComponent(PositionComponent);
if (position) {
// TODO: 在这里编写移动逻辑
console.log(`实体 ${entity.name} 位置: ${position.position}`);
}
}
}
}
```
### 在场景中注册系统
`scenes/GameScene.ts``initialize()` 方法中添加:
```typescript
import { MovementSystem } from '../systems/MovementSystem';
public initialize(): void {
super.initialize();
this.name = "MainGameScene";
// 添加系统
this.addEntityProcessor(new MovementSystem());
// 创建测试实体
const testEntity = this.createEntity("TestEntity");
testEntity.addComponent(new PositionComponent(0, 0, 0));
}
```
## 🔗 学习资源
- [ECS框架完整文档](https://github.com/esengine/ecs-framework)
- [ECS概念详解](https://github.com/esengine/ecs-framework/blob/master/docs/concepts-explained.md)
- [新手教程](https://github.com/esengine/ecs-framework/blob/master/docs/beginner-tutorials.md)
- [组件设计指南](https://github.com/esengine/ecs-framework/blob/master/docs/component-design-guide.md)
- [系统开发指南](https://github.com/esengine/ecs-framework/blob/master/docs/system-guide.md)
## 💡 开发提示
1. **组件只存储数据**:避免在组件中编写复杂逻辑
2. **系统处理逻辑**:所有业务逻辑应该在系统中实现
3. **使用Matcher过滤实体**系统通过Matcher指定需要处理的实体类型
4. **性能优化**:大量实体时考虑使用位掩码查询和组件索引
## ❓ 常见问题
### Q: 如何创建实体?
A: 在场景中使用 `this.createEntity("实体名称")`
### Q: 如何给实体添加组件?
A: 使用 `entity.addComponent(new YourComponent())`
### Q: 如何获取实体的组件?
A: 使用 `entity.getComponent(YourComponent)`
### Q: 如何删除实体?
A: 使用 `entity.destroy()``this.destroyEntity(entity)`
---
🎮 **开始您的ECS开发之旅吧**
如有问题请查阅官方文档或提交Issue。

View File

@@ -0,0 +1,11 @@
{
"ver": "1.0.1",
"importer": "text",
"imported": true,
"uuid": "0932496e-f7fe-4cb9-86e2-ebd7d2a3d047",
"files": [
".json"
],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "1.2.0",
"importer": "directory",
"imported": true,
"uuid": "3d8cbc91-5bc5-4d17-b53a-01fda26e4660",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,102 @@
import { Component } from '@esengine/ecs-framework';
/**
* 生命值组件 - 管理实体的生命值和相关状态
*
* 展示游戏逻辑组件的设计:
* 1. 包含生命值的核心数据
* 2. 提供简单的查询方法
* 3. 复杂的伤害处理逻辑留给系统处理
*/
export class HealthComponent extends Component {
/** 最大生命值 */
public maxHealth: number;
/** 当前生命值 */
public currentHealth: number;
/** 生命值回复速度(每秒回复量) */
public regenRate: number = 0;
/** 最后受到伤害的时间(用于延迟回血等机制) */
public lastDamageTime: number = 0;
/** 是否无敌 */
public invincible: boolean = false;
/** 无敌持续时间 */
public invincibleDuration: number = 0;
constructor(maxHealth: number = 100, regenRate: number = 0) {
super();
this.maxHealth = maxHealth;
this.currentHealth = maxHealth;
this.regenRate = regenRate;
}
/**
* 检查是否死亡
*/
isDead(): boolean {
return this.currentHealth <= 0;
}
/**
* 检查是否满血
*/
isFullHealth(): boolean {
return this.currentHealth >= this.maxHealth;
}
/**
* 获取生命值百分比0-1
*/
getHealthPercentage(): number {
return this.currentHealth / this.maxHealth;
}
/**
* 检查生命值是否低于指定百分比
*/
isHealthBelowPercentage(percentage: number): boolean {
return this.getHealthPercentage() < percentage;
}
/**
* 设置生命值(不超过最大值)
*/
setHealth(health: number) {
this.currentHealth = Math.max(0, Math.min(health, this.maxHealth));
}
/**
* 增加生命值(治疗)
*/
heal(amount: number) {
this.currentHealth = Math.min(this.currentHealth + amount, this.maxHealth);
}
/**
* 减少生命值(受伤)
* 注意:这里只修改数据,具体的伤害逻辑(如死亡处理)应该在系统中实现
*/
takeDamage(damage: number) {
if (this.invincible) return;
this.currentHealth = Math.max(0, this.currentHealth - damage);
this.lastDamageTime = Date.now();
}
/**
* 设置无敌状态
*/
setInvincible(duration: number) {
this.invincible = true;
this.invincibleDuration = duration;
}
/**
* 重置到满血状态
*/
reset() {
this.currentHealth = this.maxHealth;
this.invincible = false;
this.invincibleDuration = 0;
this.lastDamageTime = 0;
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "0f20d48a-7b30-4081-a9de-709432b6737b",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,124 @@
import { Component } from '@esengine/ecs-framework';
import { Vec2 } from 'cc';
/**
* 玩家输入组件 - 存储玩家的输入状态
*
* 标记组件示例:
* 1. 标识这是一个玩家控制的实体
* 2. 存储输入状态数据
* 3. 输入处理逻辑在InputSystem中实现
*/
export class PlayerInputComponent extends Component {
/** 移动输入方向(-1到1 */
public moveDirection: Vec2 = new Vec2();
/** 按键状态 */
public keys: { [key: string]: boolean } = {};
/** 鼠标位置 */
public mousePosition: Vec2 = new Vec2();
/** 鼠标按键状态 */
public mouseButtons: { left: boolean; right: boolean; middle: boolean } = {
left: false,
right: false,
middle: false
};
/** 是否启用输入 */
public inputEnabled: boolean = true;
/** 输入敏感度 */
public sensitivity: number = 1.0;
constructor() {
super();
}
/**
* 设置移动方向
*/
setMoveDirection(x: number, y: number) {
this.moveDirection.set(x, y);
// 标准化方向向量(对角线移动不应该更快)
if (this.moveDirection.lengthSqr() > 1) {
this.moveDirection.normalize();
}
}
/**
* 设置按键状态
*/
setKey(key: string, pressed: boolean) {
this.keys[key] = pressed;
}
/**
* 检查按键是否按下
*/
isKeyPressed(key: string): boolean {
return this.keys[key] || false;
}
/**
* 检查是否有移动输入
*/
hasMovementInput(): boolean {
return this.moveDirection.lengthSqr() > 0.01;
}
/**
* 获取标准化的移动方向
*/
getNormalizedMoveDirection(): Vec2 {
const result = new Vec2(this.moveDirection);
if (result.lengthSqr() > 0) {
result.normalize();
}
return result;
}
/**
* 设置鼠标位置
*/
setMousePosition(x: number, y: number) {
this.mousePosition.set(x, y);
}
/**
* 设置鼠标按键状态
*/
setMouseButton(button: 'left' | 'right' | 'middle', pressed: boolean) {
this.mouseButtons[button] = pressed;
}
/**
* 检查鼠标按键是否按下
*/
isMouseButtonPressed(button: 'left' | 'right' | 'middle'): boolean {
return this.mouseButtons[button];
}
/**
* 清除所有输入状态
*/
clearInput() {
this.moveDirection.set(0, 0);
this.keys = {};
this.mouseButtons.left = false;
this.mouseButtons.right = false;
this.mouseButtons.middle = false;
}
/**
* 禁用输入
*/
disableInput() {
this.inputEnabled = false;
this.clearInput();
}
/**
* 启用输入
*/
enableInput() {
this.inputEnabled = true;
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "ab10dc4c-c8a3-4fd2-83d6-433d4195966b",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,62 @@
import { Component } from '@esengine/ecs-framework';
import { Vec3 } from 'cc';
/**
* 位置组件 - 存储实体的空间位置信息
*
* 这是最基础的组件示例展示了ECS组件的设计原则
* 1. 主要存储数据,少量辅助方法
* 2. 单一职责:只负责位置相关的数据
* 3. 可复用:任何需要位置信息的实体都可以使用
*/
export class PositionComponent extends Component {
/** 3D位置坐标 */
public position: Vec3 = new Vec3();
/** 上一帧的位置(用于计算移动距离) */
public lastPosition: Vec3 = new Vec3();
constructor(x: number = 0, y: number = 0, z: number = 0) {
super();
this.position.set(x, y, z);
this.lastPosition.set(x, y, z);
}
/**
* 设置位置
*/
setPosition(x: number, y: number, z: number = 0) {
this.lastPosition.set(this.position);
this.position.set(x, y, z);
}
/**
* 移动位置
*/
move(deltaX: number, deltaY: number, deltaZ: number = 0) {
this.lastPosition.set(this.position);
this.position.x += deltaX;
this.position.y += deltaY;
this.position.z += deltaZ;
}
/**
* 计算到另一个位置的距离
*/
distanceTo(other: PositionComponent): number {
return Vec3.distance(this.position, other.position);
}
/**
* 获取本帧移动的距离
*/
getMovementDistance(): number {
return Vec3.distance(this.position, this.lastPosition);
}
/**
* 检查是否在指定范围内
*/
isWithinRange(target: PositionComponent, range: number): boolean {
return this.distanceTo(target) <= range;
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "e6ee57d6-d0eb-43f2-a601-9b7a2812de66",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,94 @@
import { Component } from '@esengine/ecs-framework';
import { Vec3 } from 'cc';
/**
* 速度组件 - 存储实体的运动速度信息
*
* 设计原则展示:
* 1. 与PositionComponent分离遵循单一职责原则
* 2. 包含速度限制:避免无限加速
* 3. 提供常用的速度操作方法
*/
export class VelocityComponent extends Component {
/** 当前速度向量 */
public velocity: Vec3 = new Vec3();
/** 最大速度限制 */
public maxSpeed: number = 100;
/** 阻尼系数0-11为无阻尼 */
public damping: number = 1.0;
constructor(x: number = 0, y: number = 0, z: number = 0, maxSpeed: number = 100) {
super();
this.velocity.set(x, y, z);
this.maxSpeed = maxSpeed;
}
/**
* 设置速度
*/
setVelocity(x: number, y: number, z: number = 0) {
this.velocity.set(x, y, z);
this.clampToMaxSpeed();
}
/**
* 添加速度(加速度效果)
*/
addVelocity(x: number, y: number, z: number = 0) {
this.velocity.x += x;
this.velocity.y += y;
this.velocity.z += z;
this.clampToMaxSpeed();
}
/**
* 应用阻尼
*/
applyDamping(deltaTime: number) {
if (this.damping < 1.0) {
const dampingFactor = Math.pow(this.damping, deltaTime);
this.velocity.multiplyScalar(dampingFactor);
}
}
/**
* 限制速度不超过最大值
*/
private clampToMaxSpeed() {
const speed = this.velocity.length();
if (speed > this.maxSpeed) {
this.velocity.normalize();
this.velocity.multiplyScalar(this.maxSpeed);
}
}
/**
* 获取当前速度大小
*/
getSpeed(): number {
return this.velocity.length();
}
/**
* 获取速度方向(单位向量)
*/
getDirection(): Vec3 {
const result = new Vec3();
Vec3.normalize(result, this.velocity);
return result;
}
/**
* 停止移动
*/
stop() {
this.velocity.set(0, 0, 0);
}
/**
* 检查是否在移动
*/
isMoving(): boolean {
return this.velocity.lengthSqr() > 0.01; // 避免浮点数精度问题
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "784d7c28-2b72-427c-8b04-da0fcf775acf",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "1.2.0",
"importer": "directory",
"imported": true,
"uuid": "6a2d6231-acf9-47b8-a020-d45a7433a95d",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "1.2.0",
"importer": "directory",
"imported": true,
"uuid": "879b4e07-dd6b-4445-adb2-a970b97c6d6f",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,316 @@
import { Scene } from '@esengine/ecs-framework';
import { Entity } from '@esengine/ecs-framework';
// 导入组件
import { PositionComponent } from '../components/PositionComponent';
import { VelocityComponent } from '../components/VelocityComponent';
import { HealthComponent } from '../components/HealthComponent';
import { PlayerInputComponent } from '../components/PlayerInputComponent';
// 导入系统
import { MovementSystem } from '../systems/MovementSystem';
import { PlayerInputSystem } from '../systems/PlayerInputSystem';
import { HealthSystem } from '../systems/HealthSystem';
/**
* 示例游戏场景 - 完整的ECS应用示例
*
* 这个场景展示了:
* 1. 如何创建和配置各种实体
* 2. 如何添加和组织系统
* 3. 如何实现完整的游戏逻辑
* 4. 如何进行调试和监控
*/
export class ExampleGameScene extends Scene {
// 场景中的重要实体引用
private player: Entity | null;
private enemies: Entity[];
private gameConfig: {
maxEnemies: number;
enemySpawnInterval: number;
gameArea: { width: number; height: number };
};
constructor() {
super();
// 在构造函数中初始化属性
this.player = null;
this.enemies = [];
this.gameConfig = {
maxEnemies: 5,
enemySpawnInterval: 3000, // 3秒生成一个敌人
gameArea: { width: 800, height: 600 }
};
}
/**
* 场景初始化(构造时调用)
*/
public initialize(): void {
super.initialize();
this.name = "ExampleGameScene";
console.log("📋 ExampleGameScene 构造完成");
}
/**
* 场景开始时的回调(所有构造函数执行完毕后调用)
*/
public onStart(): void {
super.onStart();
console.log("🎮 开始初始化示例游戏场景...");
// 1. 添加系统(注意顺序很重要)
this.setupSystems();
// 2. 创建游戏实体
this.createGameEntities();
// 3. 设置定时器和事件
this.setupGameLogic();
console.log("✅ 示例游戏场景初始化完成!");
this.printSceneInfo();
}
/**
* 设置游戏系统
*/
private setupSystems(): void {
console.log("🔧 添加游戏系统...");
// 输入系统(最先处理输入)
this.addEntityProcessor(new PlayerInputSystem());
// 移动系统(处理所有移动逻辑)
this.addEntityProcessor(new MovementSystem());
// 生命值系统(处理生命值、死亡等)
this.addEntityProcessor(new HealthSystem());
console.log("✅ 系统添加完成");
}
/**
* 创建游戏实体
*/
private createGameEntities(): void {
console.log("🏗️ 创建游戏实体...");
// 创建玩家
this.createPlayer();
// 创建初始敌人
this.createInitialEnemies();
// 创建环境实体(可选)
this.createEnvironmentEntities();
console.log("✅ 实体创建完成");
}
/**
* 创建玩家实体
*/
private createPlayer(): void {
this.player = this.createEntity("Player");
// 添加玩家组件
this.player.addComponent(new PositionComponent(0, 0, 0));
this.player.addComponent(new VelocityComponent(0, 0, 0, 250)); // 最大速度250
this.player.addComponent(new HealthComponent(100, 5)); // 100血每秒回5血
this.player.addComponent(new PlayerInputComponent());
console.log("🎯 玩家创建完成 - 使用WASD或方向键移动空格键攻击");
}
/**
* 创建初始敌人
*/
private createInitialEnemies(): void {
for (let i = 0; i < 3; i++) {
this.createEnemy(i);
}
}
/**
* 创建单个敌人
*/
private createEnemy(index: number): Entity {
const enemy = this.createEntity(`Enemy_${index}`);
// 随机位置
const x = (Math.random() - 0.5) * this.gameConfig.gameArea.width;
const y = (Math.random() - 0.5) * this.gameConfig.gameArea.height;
// 随机速度
const velocityX = (Math.random() - 0.5) * 100;
const velocityY = (Math.random() - 0.5) * 100;
// 添加敌人组件
enemy.addComponent(new PositionComponent(x, y, 0));
enemy.addComponent(new VelocityComponent(velocityX, velocityY, 0, 150));
enemy.addComponent(new HealthComponent(50, 0)); // 50血不回血
// 添加到敌人列表
this.enemies.push(enemy);
return enemy;
}
/**
* 创建环境实体(演示不同类型的实体)
*/
private createEnvironmentEntities(): void {
// 创建一些静态的环境对象
for (let i = 0; i < 5; i++) {
const obstacle = this.createEntity(`Obstacle_${i}`);
const x = (Math.random() - 0.5) * this.gameConfig.gameArea.width * 0.8;
const y = (Math.random() - 0.5) * this.gameConfig.gameArea.height * 0.8;
// 只有位置,没有速度和生命值
obstacle.addComponent(new PositionComponent(x, y, 0));
}
console.log("🌲 环境实体创建完成");
}
/**
* 设置游戏逻辑和定时器
*/
private setupGameLogic(): void {
console.log("⚙️ 设置游戏逻辑...");
// 敌人生成定时器
this.setupEnemySpawner();
// 游戏状态监控
this.setupGameMonitoring();
console.log("✅ 游戏逻辑设置完成");
}
/**
* 设置敌人生成器
*/
private setupEnemySpawner(): void {
setInterval(() => {
if (this.enemies.length < this.gameConfig.maxEnemies) {
const newEnemy = this.createEnemy(this.enemies.length);
}
}, this.gameConfig.enemySpawnInterval);
}
/**
* 设置游戏监控
*/
private setupGameMonitoring(): void {
// 每10秒清理已死亡的敌人引用
setInterval(() => {
this.cleanupDeadEnemies();
}, 10000);
}
/**
* 打印游戏状态(按需调用)
*/
private printGameStatus(): void {
const totalEntities = this.entities.count;
const aliveEnemies = this.enemies.filter(e => !e.isDestroyed).length;
console.log("📊 游戏状态报告:");
console.log(` - 总实体数: ${totalEntities}`);
console.log(` - 存活敌人: ${aliveEnemies}`);
if (this.player && !this.player.isDestroyed) {
const playerHealth = this.player.getComponent(HealthComponent);
const playerPos = this.player.getComponent(PositionComponent);
console.log(` - 玩家生命值: ${playerHealth?.currentHealth}/${playerHealth?.maxHealth}`);
console.log(` - 玩家位置: (${playerPos?.position.x.toFixed(1)}, ${playerPos?.position.y.toFixed(1)})`);
}
}
/**
* 清理已死亡的敌人引用
*/
private cleanupDeadEnemies(): void {
const initialCount = this.enemies.length;
this.enemies = this.enemies.filter(enemy => !enemy.isDestroyed);
const removedCount = initialCount - this.enemies.length;
if (removedCount > 0) {
console.log(`🧹 清理了 ${removedCount} 个已死亡的敌人引用`);
}
}
/**
* 打印场景信息
*/
private printSceneInfo(): void {
console.log("\n📋 场景信息:");
console.log(` 场景名: ${this.name}`);
console.log(` 实体数: ${this.entities.count}`);
console.log(` 系统数: ${this.entityProcessors.count}`);
console.log(` 玩家: ${this.player?.name || '未创建'}`);
console.log(` 敌人: ${this.enemies.length}`);
console.log("\n🎮 控制说明:");
console.log(" - WASD 或 方向键: 移动");
console.log(" - 空格: 攻击/行动");
console.log(" - ESC: 暂停");
console.log("\n💡 学习要点:");
console.log(" 1. 观察控制台输出了解ECS运行过程");
console.log(" 2. 打开调试面板查看性能数据");
console.log(" 3. 尝试修改组件参数观察变化");
console.log(" 4. 查看代码学习ECS设计模式\n");
}
/**
* 获取玩家实体(供其他系统使用)
*/
public getPlayer(): Entity | null {
return this.player;
}
/**
* 获取所有敌人(供其他系统使用)
*/
public getEnemies(): Entity[] {
return this.enemies.filter(enemy => !enemy.isDestroyed);
}
/**
* 游戏重置方法
*/
public resetGame(): void {
console.log("🔄 重置游戏...");
// 销毁所有实体
if (this.player) {
this.player.destroy();
this.player = null;
}
this.enemies.forEach(enemy => enemy.destroy());
this.enemies = [];
// 重新创建实体
this.createGameEntities();
console.log("✅ 游戏重置完成");
}
/**
* 场景卸载时调用
*/
public unload(): void {
console.log("🧹 清理示例游戏场景...");
// 清理引用
this.player = null;
this.enemies = [];
super.unload();
console.log("✅ 场景清理完成");
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "da87facc-89a0-47da-a0ef-423255200a51",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,57 @@
import { Scene } from '@esengine/ecs-framework';
/**
* 游戏场景
*
* 这是您的主游戏场景。在这里可以:
* - 添加游戏系统
* - 创建初始实体
* - 设置场景参数
*/
export class GameScene extends Scene {
/**
* 场景初始化
* 在场景创建时调用,用于设置基础配置
*/
public initialize(): void {
super.initialize();
// 设置场景名称
this.name = "MainGameScene";
console.log('🎯 游戏场景已创建');
// TODO: 在这里添加您的游戏系统
// 例如this.addEntityProcessor(new MovementSystem());
// TODO: 在这里创建初始实体
// 例如this.createEntity("Player");
}
/**
* 场景开始运行
* 在场景开始时调用,用于执行启动逻辑
*/
public onStart(): void {
super.onStart();
console.log('🚀 游戏场景已启动');
// TODO: 在这里添加场景启动逻辑
// 例如创建UI、播放音乐、初始化游戏状态等
}
/**
* 场景卸载
* 在场景结束时调用,用于清理资源
*/
public unload(): void {
console.log('🛑 游戏场景已结束');
// TODO: 在这里添加清理逻辑
// 例如:清理缓存、释放资源等
super.unload();
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "8fee85be-2224-4200-a898-d3ae2406fb1d",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "1.2.0",
"importer": "directory",
"imported": true,
"uuid": "a5e3a8c9-3d0b-4a36-9d20-6f70f1380131",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,228 @@
import { EntitySystem, Entity, Matcher, Time } from '@esengine/ecs-framework';
import { HealthComponent } from '../components/HealthComponent';
/**
* 生命值系统 - 处理生命值相关的逻辑
*
* 展示生命值管理:
* 1. 自动回血
* 2. 无敌状态管理
* 3. 死亡处理
* 4. 事件触发
*/
export class HealthSystem extends EntitySystem {
/** 回血延迟时间(受伤后多久开始回血,毫秒) */
private regenDelay: number = 3000;
constructor() {
// 只处理拥有HealthComponent的实体
super(Matcher.empty().all(HealthComponent));
}
public initialize(): void {
super.initialize();
console.log("HealthSystem 已初始化 - 开始处理生命值逻辑");
}
/**
* 每帧处理:更新生命值相关逻辑
*/
protected process(entities: Entity[]): void {
for (const entity of entities) {
const health = entity.getComponent(HealthComponent);
// 处理无敌状态
this.processInvincibility(health);
// 处理生命值回复
this.processHealthRegeneration(entity, health);
// 检查死亡状态
this.checkDeathStatus(entity, health);
}
}
/**
* 处理无敌状态
*/
private processInvincibility(health: HealthComponent): void {
if (health.invincible && health.invincibleDuration > 0) {
health.invincibleDuration -= Time.deltaTime;
// 无敌时间结束
if (health.invincibleDuration <= 0) {
health.invincible = false;
health.invincibleDuration = 0;
console.log("无敌状态结束");
}
}
}
/**
* 处理生命值回复
*/
private processHealthRegeneration(entity: Entity, health: HealthComponent): void {
// 如果已经满血或者没有回复速度,则不处理
if (health.isFullHealth() || health.regenRate <= 0) {
return;
}
// 检查是否超过了回血延迟时间
const currentTime = Date.now();
if (currentTime - health.lastDamageTime < this.regenDelay) {
return;
}
// 计算回血量
const regenAmount = health.regenRate * Time.deltaTime;
const oldHealth = health.currentHealth;
// 执行回血
health.heal(regenAmount);
// 如果实际回了血,输出日志
if (health.currentHealth > oldHealth) {
console.log(`${entity.name} 回血: ${oldHealth.toFixed(1)} -> ${health.currentHealth.toFixed(1)} (${health.getHealthPercentage() * 100}%)`);
}
}
/**
* 检查死亡状态
*/
private checkDeathStatus(entity: Entity, health: HealthComponent): void {
if (health.isDead()) {
this.handleEntityDeath(entity, health);
}
}
/**
* 处理实体死亡
*/
private handleEntityDeath(entity: Entity, health: HealthComponent): void {
console.log(`💀 ${entity.name} 已死亡!`);
// 触发死亡事件(如果有事件系统)
this.triggerDeathEvent(entity);
// 可以在这里添加死亡效果、掉落物品等逻辑
this.createDeathEffect(entity);
// 标记实体为死亡状态(而不是立即销毁)
// 这样其他系统可以处理死亡相关的逻辑
entity.addComponent(new DeadMarkerComponent());
// 可选:延迟销毁实体
setTimeout(() => {
if (entity && !entity.isDestroyed) {
entity.destroy();
console.log(`${entity.name} 已被销毁`);
}
}, 1000); // 1秒后销毁
}
/**
* 触发死亡事件
*/
private triggerDeathEvent(entity: Entity): void {
// 如果项目中有事件系统,可以在这里发送死亡事件
console.log(`触发死亡事件: ${entity.name}`);
// 示例事件数据
const deathEventData = {
entityId: entity.id,
entityName: entity.name,
deathTime: Date.now(),
position: this.getEntityPosition(entity)
};
// 这里可以调用事件系统发送事件
// eventBus.emit('entity:died', deathEventData);
}
/**
* 创建死亡效果
*/
private createDeathEffect(entity: Entity): void {
console.log(`💥 为 ${entity.name} 创建死亡效果`);
// 在实际游戏中,这里可能会:
// 1. 播放死亡动画
// 2. 播放死亡音效
// 3. 创建粒子效果
// 4. 掉落物品
}
/**
* 获取实体位置(辅助方法)
*/
private getEntityPosition(entity: Entity): { x: number; y: number; z: number } {
// 尝试获取位置组件
const position = entity.getComponent(PositionComponent);
if (position) {
return {
x: position.position.x,
y: position.position.y,
z: position.position.z
};
}
return { x: 0, y: 0, z: 0 };
}
/**
* 公共方法:对实体造成伤害
* 这个方法可以被其他系统调用
*/
public damageEntity(entity: Entity, damage: number, source?: Entity): boolean {
const health = entity.getComponent(HealthComponent);
if (!health || health.invincible) {
return false; // 无生命值组件或处于无敌状态
}
const oldHealth = health.currentHealth;
health.takeDamage(damage);
console.log(`⚔️ ${entity.name} 受到 ${damage} 点伤害: ${oldHealth.toFixed(1)} -> ${health.currentHealth.toFixed(1)}`);
// 如果有伤害来源,可以记录或处理
if (source) {
console.log(`伤害来源: ${source.name}`);
}
return true;
}
/**
* 公共方法:治疗实体
*/
public healEntity(entity: Entity, healAmount: number): boolean {
const health = entity.getComponent(HealthComponent);
if (!health || health.isFullHealth()) {
return false;
}
const oldHealth = health.currentHealth;
health.heal(healAmount);
console.log(`💚 ${entity.name} 恢复 ${healAmount} 点生命值: ${oldHealth.toFixed(1)} -> ${health.currentHealth.toFixed(1)}`);
return true;
}
}
/**
* 死亡标记组件 - 标记已死亡的实体
* 这是一个简单的标记组件,用于标识死亡状态
*/
class DeadMarkerComponent extends Component {
public deathTime: number;
constructor() {
super();
this.deathTime = Date.now();
}
}
// 导入位置组件(用于获取实体位置)
import { PositionComponent } from '../components/PositionComponent';
import { Component } from '@esengine/ecs-framework';

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "455c12d1-52a8-41ac-b1b5-0d2b93c079aa",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,99 @@
import { EntitySystem, Entity, Matcher, Time } from '@esengine/ecs-framework';
import { PositionComponent } from '../components/PositionComponent';
import { VelocityComponent } from '../components/VelocityComponent';
/**
* 移动系统 - 处理实体的移动逻辑
*
* EntitySystem示例
* 1. 使用Matcher指定需要的组件Position + Velocity
* 2. 每帧更新所有移动实体的位置
* 3. 展示组件间的协作
*/
export class MovementSystem extends EntitySystem {
constructor() {
// 只处理同时拥有PositionComponent和VelocityComponent的实体
super(Matcher.empty().all(PositionComponent, VelocityComponent));
}
/**
* 每帧执行:更新所有移动实体的位置
*/
protected process(entities: Entity[]): void {
for (const entity of entities) {
const position = entity.getComponent(PositionComponent);
const velocity = entity.getComponent(VelocityComponent);
// 基本移动:位置 = 当前位置 + 速度 * 时间
position.move(
velocity.velocity.x * Time.deltaTime,
velocity.velocity.y * Time.deltaTime,
velocity.velocity.z * Time.deltaTime
);
// 应用阻尼(摩擦力)
velocity.applyDamping(Time.deltaTime);
// 可选:添加边界检查
this.checkBoundaries(position, velocity);
}
}
/**
* 边界检查(可选功能)
* 这个方法演示了如何在系统中实现额外的游戏逻辑
*/
private checkBoundaries(position: PositionComponent, velocity: VelocityComponent) {
const bounds = {
left: -400,
right: 400,
top: 300,
bottom: -300
};
// 检查X轴边界
if (position.position.x < bounds.left) {
position.position.x = bounds.left;
velocity.velocity.x = Math.abs(velocity.velocity.x); // 反弹
} else if (position.position.x > bounds.right) {
position.position.x = bounds.right;
velocity.velocity.x = -Math.abs(velocity.velocity.x); // 反弹
}
// 检查Y轴边界
if (position.position.y < bounds.bottom) {
position.position.y = bounds.bottom;
velocity.velocity.y = Math.abs(velocity.velocity.y); // 反弹
} else if (position.position.y > bounds.top) {
position.position.y = bounds.top;
velocity.velocity.y = -Math.abs(velocity.velocity.y); // 反弹
}
}
/**
* 系统初始化时调用
* 可以在这里设置系统级别的配置
*/
public initialize(): void {
super.initialize();
console.log("MovementSystem 已初始化 - 开始处理实体移动");
}
/**
* 获取系统统计信息(用于调试)
*/
public getStats(): { processedEntities: number; totalMovement: number } {
let totalMovement = 0;
const entities = this.entities;
for (const entity of entities) {
const position = entity.getComponent(PositionComponent);
totalMovement += position.getMovementDistance();
}
return {
processedEntities: entities.length,
totalMovement: totalMovement
};
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "a8712467-efe0-46ec-a246-a9fa07d203d9",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,173 @@
import { EntitySystem, Entity, Matcher } from '@esengine/ecs-framework';
import { PlayerInputComponent } from '../components/PlayerInputComponent';
import { VelocityComponent } from '../components/VelocityComponent';
import { input, Input, EventKeyboard, KeyCode } from 'cc';
/**
* 玩家输入系统 - 处理玩家输入并转换为游戏行为
*
* 展示系统的职责:
* 1. 收集输入事件
* 2. 更新输入组件状态
* 3. 根据输入修改其他组件(如速度)
*/
export class PlayerInputSystem extends EntitySystem {
private moveSpeed: number = 200; // 移动速度
constructor() {
// 只处理拥有PlayerInputComponent的实体
super(Matcher.empty().all(PlayerInputComponent));
}
public initialize(): void {
super.initialize();
console.log("PlayerInputSystem 已初始化 - 开始监听玩家输入");
// 注册键盘事件监听器
this.setupInputListeners();
}
/**
* 设置输入事件监听器
*/
private setupInputListeners(): void {
// 键盘按下事件
input.on(Input.EventType.KEY_DOWN, this.onKeyDown, this);
// 键盘抬起事件
input.on(Input.EventType.KEY_UP, this.onKeyUp, this);
}
/**
* 键盘按下处理
*/
private onKeyDown(event: EventKeyboard): void {
const keyCode = event.keyCode;
const keyName = this.getKeyName(keyCode);
// 更新所有玩家实体的输入状态
for (const entity of this.entities) {
const playerInput = entity.getComponent(PlayerInputComponent);
if (playerInput && playerInput.inputEnabled) {
playerInput.setKey(keyName, true);
}
}
}
/**
* 键盘抬起处理
*/
private onKeyUp(event: EventKeyboard): void {
const keyCode = event.keyCode;
const keyName = this.getKeyName(keyCode);
// 更新所有玩家实体的输入状态
for (const entity of this.entities) {
const playerInput = entity.getComponent(PlayerInputComponent);
if (playerInput && playerInput.inputEnabled) {
playerInput.setKey(keyName, false);
}
}
}
/**
* 每帧处理:根据输入状态更新实体行为
*/
protected process(entities: Entity[]): void {
for (const entity of entities) {
const playerInput = entity.getComponent(PlayerInputComponent);
if (!playerInput || !playerInput.inputEnabled) {
continue;
}
// 处理移动输入
this.processMovementInput(entity, playerInput);
// 处理其他输入(如攻击、跳跃等)
this.processActionInput(entity, playerInput);
}
}
/**
* 处理移动输入
*/
private processMovementInput(entity: Entity, playerInput: PlayerInputComponent): void {
const velocity = entity.getComponent(VelocityComponent);
if (!velocity) return;
// 根据按键状态计算移动方向
let moveX = 0;
let moveY = 0;
if (playerInput.isKeyPressed('A') || playerInput.isKeyPressed('ArrowLeft')) {
moveX -= 1;
}
if (playerInput.isKeyPressed('D') || playerInput.isKeyPressed('ArrowRight')) {
moveX += 1;
}
if (playerInput.isKeyPressed('W') || playerInput.isKeyPressed('ArrowUp')) {
moveY += 1;
}
if (playerInput.isKeyPressed('S') || playerInput.isKeyPressed('ArrowDown')) {
moveY -= 1;
}
// 更新输入组件的移动方向
playerInput.setMoveDirection(moveX, moveY);
// 将输入转换为速度
const normalizedDirection = playerInput.getNormalizedMoveDirection();
velocity.setVelocity(
normalizedDirection.x * this.moveSpeed * playerInput.sensitivity,
normalizedDirection.y * this.moveSpeed * playerInput.sensitivity,
0
);
}
/**
* 处理动作输入(攻击、技能等)
*/
private processActionInput(entity: Entity, playerInput: PlayerInputComponent): void {
// 空格键 - 跳跃或攻击
if (playerInput.isKeyPressed('Space')) {
console.log(`玩家 ${entity.name} 执行动作:攻击/跳跃`);
// 这里可以触发攻击组件或添加跳跃效果
}
// ESC键 - 暂停游戏
if (playerInput.isKeyPressed('Escape')) {
console.log("玩家请求暂停游戏");
// 可以发送暂停事件给游戏管理系统
}
}
/**
* 将键码转换为字符串
*/
private getKeyName(keyCode: KeyCode): string {
const keyMap: { [key: number]: string } = {
[KeyCode.KEY_A]: 'A',
[KeyCode.KEY_D]: 'D',
[KeyCode.KEY_S]: 'S',
[KeyCode.KEY_W]: 'W',
[KeyCode.ARROW_LEFT]: 'ArrowLeft',
[KeyCode.ARROW_RIGHT]: 'ArrowRight',
[KeyCode.ARROW_UP]: 'ArrowUp',
[KeyCode.ARROW_DOWN]: 'ArrowDown',
[KeyCode.SPACE]: 'Space',
[KeyCode.ESCAPE]: 'Escape'
};
return keyMap[keyCode] || `Key_${keyCode}`;
}
/**
* 系统清理
*/
public onDestroy(): void {
// 移除事件监听器
input.off(Input.EventType.KEY_DOWN, this.onKeyDown, this);
input.off(Input.EventType.KEY_UP, this.onKeyUp, this);
console.log("PlayerInputSystem 已清理");
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "7b69a39f-926a-4260-94ba-e15e31b324b5",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,54 @@
{
"codeGeneration": {
"template": "typescript",
"useStrictMode": true,
"generateComments": true,
"generateImports": true,
"componentSuffix": "Component",
"systemSuffix": "System",
"indentStyle": "spaces",
"indentSize": 4
},
"performance": {
"enableMonitoring": true,
"warningThreshold": 16.67,
"criticalThreshold": 33.33,
"memoryWarningMB": 100,
"memoryCriticalMB": 200,
"maxRecentSamples": 60,
"enableFpsMonitoring": true,
"targetFps": 120
},
"debugging": {
"enableDebugMode": true,
"showEntityCount": true,
"showSystemExecutionTime": true,
"enablePerformanceWarnings": true,
"logLevel": "info",
"enableDetailedLogs": false
},
"editor": {
"autoRefreshAssets": true,
"showWelcomePanelOnStartup": true,
"enableAutoUpdates": false,
"updateChannel": "stable",
"enableNotifications": true
},
"template": {
"defaultEntityName": "ModifiedEntity",
"defaultComponentName": "TestComponent",
"defaultSystemName": "TestSystem",
"createExampleFiles": true,
"includeDocumentation": true,
"useFactoryPattern": true
},
"events": {
"enableEventSystem": true,
"defaultEventPriority": 0,
"enableAsyncEvents": true,
"enableEventBatching": false,
"batchSize": 10,
"batchDelay": 16,
"maxEventListeners": 100
}
}

View File

@@ -25,22 +25,57 @@
"default": {
"title": "ECS Framework - 欢迎面板",
"type": "dockable",
"main": "dist/panels/default",
"main": "dist/panels/default/index.js",
"size": {
"min-width": 450,
"min-height": 600,
"width": 850,
"height": 800
}
},
"settings": {
"title": "ECS Framework - 设置",
"type": "dockable",
"main": "dist/panels/settings/index.js",
"size": {
"min-width": 600,
"min-height": 700,
"width": 800,
"height": 900
}
},
"debug": {
"title": "ECS Framework - 调试面板",
"type": "dockable",
"main": "dist/panels/debug/index.js",
"size": {
"min-width": 400,
"min-height": 500,
"width": 500,
"height": 600
}
}
},
"contributions": {
"scene": {
"script": "./dist/scene.js"
},
"menu": [
{
"path": "i18n:menu.panel/ECS Framework",
"label": "欢迎面板",
"message": "open-panel"
},
{
"path": "i18n:menu.panel/ECS Framework",
"label": "插件设置",
"message": "open-settings"
},
{
"path": "i18n:menu.panel/ECS Framework",
"label": "调试面板",
"message": "open-debug"
},
{
"path": "i18n:menu.develop/ECS Framework",
"label": "ECS 开发工具",
@@ -97,6 +132,16 @@
"methods": [
"open-github"
]
},
"settings-updated": {
"methods": [
"settings-updated"
]
},
"open-debug": {
"methods": [
"open-debug"
]
}
}
}

View File

@@ -26,7 +26,7 @@ export class TemplateGenerator {
*/
public getExistingFiles(): string[] {
if (!this.checkTemplateExists()) return [];
const files: string[] = [];
this.scanDirectory(this.ecsDir, '', files);
return files;
@@ -34,12 +34,12 @@ export class TemplateGenerator {
private scanDirectory(dirPath: string, relativePath: string, files: string[]): void {
if (!fs.existsSync(dirPath)) return;
const items = fs.readdirSync(dirPath);
for (const item of items) {
const fullPath = path.join(dirPath, item);
const relativeFilePath = relativePath ? `${relativePath}/${item}` : item;
if (fs.statSync(fullPath).isDirectory()) {
this.scanDirectory(fullPath, relativeFilePath, files);
} else {
@@ -64,16 +64,16 @@ export class TemplateGenerator {
public createTemplate(): void {
// 创建目录结构
this.createDirectories();
// 创建ECS启动管理器
this.createECSManager();
// 创建基础游戏场景
this.createBaseGameScene();
// 创建README文档
this.createReadme();
console.log('ECS启动模板创建成功');
}
@@ -141,8 +141,27 @@ export class ECSManager extends Component {
console.log('🎮 正在初始化ECS框架...');
try {
// 1. 创建Core实例
Core.create(this.debugMode);
// 1. 创建Core实例,启用调试功能
if (this.debugMode) {
Core.create({
debugConfig: {
enabled: true,
websocketUrl: 'ws://localhost:8080/ecs-debug',
autoReconnect: true,
updateInterval: 100,
channels: {
entities: true,
systems: true,
performance: true,
components: true,
scenes: true
}
}
});
console.log('🔧 ECS调试模式已启用可在Cocos Creator扩展面板中查看调试信息');
} else {
Core.create(false);
}
// 2. 创建游戏场景
const gameScene = new GameScene();
@@ -284,11 +303,25 @@ ECS框架已经配置完成您只需要
\`\`\`
🎮 正在初始化ECS框架...
🔧 ECS调试模式已启用可在Cocos Creator扩展面板中查看调试信息
🎯 游戏场景已创建
✅ ECS框架初始化成功
🚀 游戏场景已启动
\`\`\`
### 3. 使用调试面板
ECS框架已启用调试功能您可以
1. 在Cocos Creator编辑器菜单中选择 "扩展" → "ECS Framework" → "调试面板"
2. 调试面板将显示实时的ECS运行状态
- 实体数量和状态
- 系统执行信息
- 性能监控数据
- 组件统计信息
**注意**:调试功能会消耗一定性能,正式发布时建议关闭调试模式。
## 📚 下一步开发
### 创建您的第一个组件

View File

@@ -122,7 +122,7 @@ export const methods: { [key: string]: (...any: any) => any } = {
'open-documentation'() {
// 使用系统默认命令打开链接
const url = 'https://github.com/esengine/ecs-framework/blob/master/README.md';
exec(`start ${url}`, (error) => {
exec(`start "" "${url}"`, (error) => {
if (error) {
console.error('Failed to open documentation:', error);
Editor.Dialog.info('打开文档', {
@@ -201,10 +201,17 @@ export const methods: { [key: string]: (...any: any) => any } = {
* 打开设置
*/
'open-settings'() {
Editor.Dialog.info('插件设置', {
detail: '设置面板开发中...\n\n将在下个版本提供完整的插件配置功能。\n\n预计功能\n• 代码生成模板配置\n• 性能监控设置\n• 自动更新设置\n• 开发工具偏好',
buttons: ['好的'],
});
console.log('Opening ECS Framework settings panel...');
try {
// 正确的打开特定面板的方法
Editor.Panel.open(packageJSON.name + '.settings');
console.log('Settings panel opened successfully');
} catch (error) {
console.error('Failed to open settings panel:', error);
Editor.Dialog.error('打开设置失败', {
detail: `无法打开设置面板:\n\n${error}\n\n请尝试重启Cocos Creator编辑器。`,
});
}
},
/**
@@ -226,6 +233,74 @@ export const methods: { [key: string]: (...any: any) => any } = {
buttons: ['好的'],
});
},
/**
* 打开GitHub仓库
*/
'open-github'() {
const url = 'https://github.com/esengine/ecs-framework';
// 在Windows上使用正确的start命令语法
exec(`start "" "${url}"`, (error) => {
if (error) {
console.error('Failed to open GitHub:', error);
Editor.Dialog.info('打开GitHub', {
detail: `请手动访问以下链接:\n\n${url}`,
});
}
});
},
/**
* 打开调试面板
*/
'open-debug'() {
console.log('Opening ECS Framework debug panel...');
try {
// 正确的打开特定面板的方法
Editor.Panel.open(packageJSON.name + '.debug');
console.log('Debug panel opened successfully');
} catch (error) {
console.error('Failed to open debug panel:', error);
Editor.Dialog.error('打开调试面板失败', {
detail: `无法打开调试面板:\n\n${error}\n\n请尝试重启Cocos Creator编辑器。`,
});
}
},
/**
* 处理设置更新
*/
'settings-updated'(settings: any) {
console.log('ECS Framework settings updated:', settings);
// 这里可以根据设置更新做一些处理
// 比如重新配置框架、更新性能监控等
// 示例:根据设置更新性能监控
if (settings?.performance?.enableMonitoring) {
console.log('Performance monitoring enabled with thresholds:', {
warning: settings.performance.warningThreshold,
critical: settings.performance.criticalThreshold,
memoryWarning: settings.performance.memoryWarningMB,
memoryCritical: settings.performance.memoryCriticalMB
});
}
// 示例:根据设置更新调试模式
if (settings?.debugging?.enableDebugMode) {
console.log('Debug mode enabled with log level:', settings.debugging.logLevel);
}
// 示例:根据设置更新事件系统
if (settings?.events?.enableEventSystem) {
console.log('Event system configured:', {
asyncEvents: settings.events.enableAsyncEvents,
batching: settings.events.enableEventBatching,
batchSize: settings.events.batchSize,
maxListeners: settings.events.maxEventListeners
});
}
},
};
/**

View File

@@ -0,0 +1,612 @@
import { readFileSync } from 'fs-extra';
import { join } from 'path';
import { createApp, App, defineComponent, ref, reactive, onMounted, onUnmounted } from 'vue';
import { WebSocketServer } from 'ws';
const panelDataMap = new WeakMap<any, App>();
/**
* 游戏实例信息
*/
interface GameInstance {
id: string;
name: string;
connectTime: number;
lastUpdateTime: number;
isActive: boolean;
debugData?: any;
ws?: any; // WebSocket连接
}
/**
* 详细的调试信息接口
*/
interface DetailedDebugInfo {
// 基础信息
instanceId: string;
instanceName: string;
isRunning: boolean;
frameworkLoaded: boolean;
currentScene: string;
uptime: number;
// 性能信息
performance: {
frameTime: number;
fps: number;
averageFrameTime: number;
minFrameTime: number;
maxFrameTime: number;
frameTimeHistory: number[];
};
// 内存信息
memory: {
totalMemory: number;
usedMemory: number;
freeMemory: number;
entityMemory: number;
componentMemory: number;
systemMemory: number;
pooledMemory: number;
gcCollections: number;
};
// 实体信息
entities: {
total: number;
active: number;
inactive: number;
pendingAdd: number;
pendingRemove: number;
entitiesPerArchetype: Array<{
signature: string;
count: number;
memory: number;
}>;
topEntitiesByComponents: Array<{
id: string;
name: string;
componentCount: number;
memory: number;
}>;
};
// 组件信息
components: {
totalTypes: number;
totalInstances: number;
componentStats: Array<{
typeName: string;
instanceCount: number;
memoryPerInstance: number;
totalMemory: number;
poolSize: number;
poolUtilization: number;
}>;
};
// 系统信息
systems: {
total: number;
systemStats: Array<{
name: string;
type: string;
entityCount: number;
averageExecutionTime: number;
minExecutionTime: number;
maxExecutionTime: number;
executionTimeHistory: number[];
memoryUsage: number;
updateOrder: number;
enabled: boolean;
}>;
};
// 场景信息
scenes: {
currentScene: string;
sceneMemory: number;
sceneEntityCount: number;
sceneSystemCount: number;
sceneUptime: number;
};
}
/**
* ECS调试服务器
* 作为服务端,接收多个游戏实例的连接
*/
class ECSDebugServer {
private wss?: WebSocketServer;
private port: number = 8080;
private gameInstances = new Map<string, GameInstance>();
private isRunning: boolean = false;
constructor(port: number = 8080) {
this.port = port;
}
async start(): Promise<boolean> {
if (this.isRunning) return true;
try {
this.wss = new WebSocketServer({ port: this.port });
this.wss.on('connection', (ws, req) => {
const instanceId = this.generateInstanceId();
const instance: GameInstance = {
id: instanceId,
name: `游戏实例-${instanceId.substring(0, 8)}`,
connectTime: Date.now(),
lastUpdateTime: Date.now(),
isActive: true,
debugData: null,
ws: ws
};
this.gameInstances.set(instanceId, instance);
console.log(`[ECS Debug Server] New instance connected: ${instance.name}`);
ws.on('message', (data) => {
try {
const message = JSON.parse(data.toString());
this.handleMessage(instanceId, message);
} catch (error) {
console.error('[ECS Debug Server] Failed to parse message:', error);
}
});
ws.on('close', () => {
const instance = this.gameInstances.get(instanceId);
if (instance) {
instance.isActive = false;
console.log(`[ECS Debug Server] Instance disconnected: ${instance.name}`);
}
});
ws.on('error', (error) => {
console.error(`[ECS Debug Server] WebSocket error for ${instanceId}:`, error);
});
// 发送连接确认
this.sendToInstance(instanceId, {
type: 'connection_confirmed',
instanceId: instanceId,
serverTime: Date.now()
});
});
this.isRunning = true;
console.log(`[ECS Debug Server] Started on port ${this.port}`);
return true;
} catch (error) {
console.error('[ECS Debug Server] Failed to start:', error);
return false;
}
}
stop(): void {
if (this.wss) {
this.wss.close();
this.wss = undefined;
}
this.gameInstances.clear();
this.isRunning = false;
console.log('[ECS Debug Server] Stopped');
}
private generateInstanceId(): string {
return Math.random().toString(36).substring(2) + Date.now().toString(36);
}
private handleMessage(instanceId: string, message: any): void {
const instance = this.gameInstances.get(instanceId);
if (!instance) return;
switch (message.type) {
case 'debug_data':
instance.debugData = message.data;
instance.lastUpdateTime = Date.now();
break;
case 'instance_info':
if (message.name) {
instance.name = message.name;
}
break;
case 'ping':
this.sendToInstance(instanceId, { type: 'pong', timestamp: Date.now() });
break;
}
}
private sendToInstance(instanceId: string, message: any): void {
const instance = this.gameInstances.get(instanceId);
if (instance && instance.ws && instance.ws.readyState === 1) {
instance.ws.send(JSON.stringify(message));
}
}
get running(): boolean {
return this.isRunning;
}
get instances(): GameInstance[] {
return Array.from(this.gameInstances.values());
}
getInstance(instanceId: string): GameInstance | undefined {
return this.gameInstances.get(instanceId);
}
getInstanceDebugData(instanceId: string): DetailedDebugInfo | null {
const instance = this.gameInstances.get(instanceId);
if (!instance || !instance.debugData) {
return null;
}
return this.transformToDetailedDebugInfo(instance, instance.debugData);
}
private transformToDetailedDebugInfo(instance: GameInstance, rawData: any): DetailedDebugInfo {
const uptime = (Date.now() - instance.connectTime) / 1000;
return {
instanceId: instance.id,
instanceName: instance.name,
isRunning: rawData.isRunning || false,
frameworkLoaded: rawData.frameworkLoaded || false,
currentScene: rawData.currentScene || '未知',
uptime: uptime,
performance: {
frameTime: rawData.performance?.frameTime || 0,
fps: rawData.performance?.fps || 0,
averageFrameTime: rawData.performance?.averageFrameTime || rawData.performance?.frameTime || 0,
minFrameTime: rawData.performance?.minFrameTime || rawData.performance?.frameTime || 0,
maxFrameTime: rawData.performance?.maxFrameTime || rawData.performance?.frameTime || 0,
frameTimeHistory: rawData.performance?.frameTimeHistory || []
},
memory: {
totalMemory: rawData.performance?.memoryDetails?.totalMemory || rawData.memory?.totalMemory || 512 * 1024 * 1024,
usedMemory: rawData.performance?.memoryDetails?.usedMemory || (rawData.performance?.memoryUsage ? rawData.performance.memoryUsage * 1024 * 1024 : 0),
freeMemory: rawData.performance?.memoryDetails?.freeMemory || 0,
entityMemory: rawData.performance?.memoryDetails?.entities || rawData.memory?.entityMemory || 0,
componentMemory: rawData.performance?.memoryDetails?.components || rawData.memory?.componentMemory || 0,
systemMemory: rawData.performance?.memoryDetails?.systems || rawData.memory?.systemMemory || 0,
pooledMemory: rawData.performance?.memoryDetails?.pooled || rawData.memory?.pooledMemory || 0,
gcCollections: rawData.performance?.memoryDetails?.gcCollections || rawData.memory?.gcCollections || 0
},
entities: {
total: rawData.entities?.totalEntities || 0,
active: rawData.entities?.activeEntities || 0,
inactive: (rawData.entities?.totalEntities || 0) - (rawData.entities?.activeEntities || 0),
pendingAdd: rawData.entities?.pendingAdd || 0,
pendingRemove: rawData.entities?.pendingRemove || 0,
entitiesPerArchetype: rawData.entities?.entitiesPerArchetype || [],
topEntitiesByComponents: rawData.entities?.topEntitiesByComponents || []
},
components: {
totalTypes: rawData.components?.componentTypes || 0,
totalInstances: rawData.components?.componentInstances || 0,
componentStats: (rawData.components?.componentStats || []).map((comp: any) => ({
typeName: comp.typeName,
instanceCount: comp.instanceCount || 0,
memoryPerInstance: comp.memoryPerInstance || 0,
totalMemory: comp.totalMemory || (comp.instanceCount || 0) * (comp.memoryPerInstance || 0),
poolSize: comp.poolSize || 0,
poolUtilization: comp.poolSize > 0 ? (comp.instanceCount / comp.poolSize * 100) : 0
}))
},
systems: {
total: rawData.systems?.totalSystems || 0,
systemStats: (rawData.systems?.systemsInfo || []).map((sys: any) => ({
name: sys.name,
type: sys.type || 'Unknown',
entityCount: sys.entityCount || 0,
averageExecutionTime: sys.executionTime || 0,
minExecutionTime: sys.minExecutionTime || sys.executionTime || 0,
maxExecutionTime: sys.maxExecutionTime || sys.executionTime || 0,
executionTimeHistory: sys.executionTimeHistory || [],
memoryUsage: sys.memoryUsage || 0,
updateOrder: sys.updateOrder || 0,
enabled: sys.enabled !== false
}))
},
scenes: {
currentScene: rawData.currentScene || '未知',
sceneMemory: rawData.scenes?.sceneMemory || 0,
sceneEntityCount: rawData.entities?.totalEntities || 0,
sceneSystemCount: rawData.systems?.totalSystems || 0,
sceneUptime: rawData.scenes?.sceneUptime || uptime
}
};
}
}
/**
* 默认调试信息
*/
const defaultDebugInfo: DetailedDebugInfo = {
instanceId: '',
instanceName: '未选择实例',
isRunning: false,
frameworkLoaded: false,
currentScene: '未知',
uptime: 0,
performance: {
frameTime: 0,
fps: 0,
averageFrameTime: 0,
minFrameTime: 0,
maxFrameTime: 0,
frameTimeHistory: []
},
memory: {
totalMemory: 0,
usedMemory: 0,
freeMemory: 0,
entityMemory: 0,
componentMemory: 0,
systemMemory: 0,
pooledMemory: 0,
gcCollections: 0
},
entities: {
total: 0,
active: 0,
inactive: 0,
pendingAdd: 0,
pendingRemove: 0,
entitiesPerArchetype: [],
topEntitiesByComponents: []
},
components: {
totalTypes: 0,
totalInstances: 0,
componentStats: []
},
systems: {
total: 0,
systemStats: []
},
scenes: {
currentScene: '未知',
sceneMemory: 0,
sceneEntityCount: 0,
sceneSystemCount: 0,
sceneUptime: 0
}
};
// 全局调试服务器实例
let globalDebugServer: ECSDebugServer | null = null;
/**
* 启动调试服务器
*/
async function ensureDebugServer(): Promise<ECSDebugServer> {
if (!globalDebugServer) {
globalDebugServer = new ECSDebugServer(8080);
}
if (!globalDebugServer.running) {
await globalDebugServer.start();
}
return globalDebugServer;
}
module.exports = Editor.Panel.define({
listeners: {
show() { },
hide() { },
},
template: `<div id="app"></div>`,
style: readFileSync(join(__dirname, '../../../static/style/debug/index.css'), 'utf-8'),
$: {
app: '#app',
},
ready() {
if (this.$.app) {
const app = createApp(defineComponent({
setup() {
const debugInfo = reactive<DetailedDebugInfo>({ ...defaultDebugInfo });
const gameInstances = ref<GameInstance[]>([]);
const selectedInstanceId = ref<string>('');
const isAutoRefresh = ref(true);
const refreshInterval = ref(100);
const lastUpdateTime = ref('');
let intervalId: NodeJS.Timeout | null = null;
let debugServer: ECSDebugServer | null = null;
// 初始化调试服务器
const initializeServer = async () => {
try {
debugServer = await ensureDebugServer();
} catch (error) {
console.error('Failed to start debug server:', error);
}
};
// 更新游戏实例列表和调试信息
const updateDebugInfo = async () => {
if (!debugServer) return;
try {
// 更新实例列表
gameInstances.value = debugServer.instances;
// 如果有选中的实例,更新其调试信息
if (selectedInstanceId.value) {
const detailedInfo = debugServer.getInstanceDebugData(selectedInstanceId.value);
if (detailedInfo) {
Object.assign(debugInfo, detailedInfo);
} else {
// 实例已断开,重置选择
selectedInstanceId.value = '';
Object.assign(debugInfo, defaultDebugInfo);
}
}
lastUpdateTime.value = new Date().toLocaleTimeString();
} catch (error) {
console.error('Failed to update debug info:', error);
}
};
// 开始自动刷新
const startAutoRefresh = () => {
if (intervalId) clearInterval(intervalId);
if (isAutoRefresh.value) {
intervalId = setInterval(updateDebugInfo, refreshInterval.value);
}
};
// 停止自动刷新
const stopAutoRefresh = () => {
if (intervalId) {
clearInterval(intervalId);
intervalId = null;
}
};
// 手动刷新
const manualRefresh = () => {
updateDebugInfo();
};
// 切换自动刷新
const toggleAutoRefresh = () => {
if (isAutoRefresh.value) {
startAutoRefresh();
} else {
stopAutoRefresh();
}
};
// 更改刷新间隔
const changeRefreshInterval = () => {
if (isAutoRefresh.value) {
startAutoRefresh();
}
};
// 实例选择改变
const onInstanceChanged = () => {
if (selectedInstanceId.value) {
updateDebugInfo();
} else {
Object.assign(debugInfo, defaultDebugInfo);
}
};
// 格式化运行时间
const formatUptime = (seconds: number): string => {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const secs = Math.floor(seconds % 60);
return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
};
// 格式化内存大小
const formatMemory = (bytes: number): string => {
if (bytes < 1024) return bytes + ' B';
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
if (bytes < 1024 * 1024 * 1024) return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
return (bytes / (1024 * 1024 * 1024)).toFixed(1) + ' GB';
};
// 获取FPS颜色
const getFpsColor = (fps: number): string => {
if (fps >= 55) return 'good';
if (fps >= 30) return 'warning';
return 'critical';
};
// 获取内存颜色
const getMemoryColor = (percentage: number): string => {
if (percentage < 70) return 'good';
if (percentage < 85) return 'warning';
return 'critical';
};
// 获取ECS时间占比颜色
const getECSTimeColor = (percentage: number): string => {
if (!percentage) return 'good';
if (percentage <= 10) return 'good'; // ECS占用<=10%为绿色
if (percentage <= 30) return 'warning'; // ECS占用<=30%为黄色
return 'critical'; // ECS占用>30%为红色
};
// 获取执行时间颜色
const getExecutionTimeColor = (time: number): string => {
if (time < 1) return 'good'; // <1ms为绿色
if (time < 5) return 'warning'; // <5ms为黄色
return 'critical';
};
// 组件挂载时初始化
onMounted(async () => {
await initializeServer();
updateDebugInfo();
startAutoRefresh();
});
// 组件卸载时清理
onUnmounted(() => {
stopAutoRefresh();
});
return {
debugInfo,
gameInstances,
selectedInstanceId,
isAutoRefresh,
refreshInterval,
lastUpdateTime,
manualRefresh,
toggleAutoRefresh,
changeRefreshInterval,
onInstanceChanged,
formatUptime,
formatMemory,
getFpsColor,
getMemoryColor,
getECSTimeColor,
getExecutionTimeColor
};
},
template: readFileSync(join(__dirname, '../../../static/template/debug/index.html'), 'utf-8'),
}));
app.config.compilerOptions.isCustomElement = (tag) => tag.startsWith('ui-');
app.mount(this.$.app);
panelDataMap.set(this, app);
}
},
beforeClose() { },
close() {
const app = panelDataMap.get(this);
if (app) {
app.unmount();
panelDataMap.delete(this);
}
// 关闭调试服务器
if (globalDebugServer) {
globalDebugServer.stop();
globalDebugServer = null;
}
},
});

View File

@@ -0,0 +1,370 @@
import { readFileSync } from 'fs-extra';
import { join } from 'path';
import { createApp, App, defineComponent, ref, onMounted, reactive } from 'vue';
import * as fs from 'fs';
import * as path from 'path';
const panelDataMap = new WeakMap<any, App>();
/**
* ECS框架设置配置接口
*/
interface ECSSettings {
// 代码生成设置
codeGeneration: {
template: 'typescript' | 'javascript';
useStrictMode: boolean;
generateComments: boolean;
generateImports: boolean;
componentSuffix: string;
systemSuffix: string;
indentStyle: 'spaces' | 'tabs';
indentSize: number;
};
// 性能监控设置
performance: {
enableMonitoring: boolean;
warningThreshold: number; // 执行时间警告阈值(ms)
criticalThreshold: number; // 执行时间严重阈值(ms)
memoryWarningMB: number; // 内存警告阈值(MB)
memoryCriticalMB: number; // 内存严重阈值(MB)
maxRecentSamples: number; // 性能采样数量
enableFpsMonitoring: boolean;
targetFps: number;
};
// 调试设置
debugging: {
enableDebugMode: boolean;
showEntityCount: boolean;
showSystemExecutionTime: boolean;
enablePerformanceWarnings: boolean;
logLevel: 'none' | 'error' | 'warn' | 'info' | 'debug';
enableDetailedLogs: boolean;
};
// 编辑器集成
editor: {
autoRefreshAssets: boolean;
showWelcomePanelOnStartup: boolean;
enableAutoUpdates: boolean;
updateChannel: 'stable' | 'beta' | 'alpha';
enableNotifications: boolean;
};
// 项目模板设置
template: {
defaultEntityName: string;
defaultComponentName: string;
defaultSystemName: string;
createExampleFiles: boolean;
includeDocumentation: boolean;
useFactoryPattern: boolean;
};
// 事件系统设置
events: {
enableEventSystem: boolean;
defaultEventPriority: number;
enableAsyncEvents: boolean;
enableEventBatching: boolean;
batchSize: number;
batchDelay: number; // ms
maxEventListeners: number;
};
}
/**
* 默认设置
*/
const defaultSettings: ECSSettings = {
codeGeneration: {
template: 'typescript',
useStrictMode: true,
generateComments: true,
generateImports: true,
componentSuffix: 'Component',
systemSuffix: 'System',
indentStyle: 'spaces',
indentSize: 4
},
performance: {
enableMonitoring: true,
warningThreshold: 16.67, // 60fps
criticalThreshold: 33.33, // 30fps
memoryWarningMB: 100,
memoryCriticalMB: 200,
maxRecentSamples: 60,
enableFpsMonitoring: true,
targetFps: 60
},
debugging: {
enableDebugMode: true,
showEntityCount: true,
showSystemExecutionTime: true,
enablePerformanceWarnings: true,
logLevel: 'info',
enableDetailedLogs: false
},
editor: {
autoRefreshAssets: true,
showWelcomePanelOnStartup: true,
enableAutoUpdates: false,
updateChannel: 'stable',
enableNotifications: true
},
template: {
defaultEntityName: 'GameEntity',
defaultComponentName: 'CustomComponent',
defaultSystemName: 'CustomSystem',
createExampleFiles: true,
includeDocumentation: true,
useFactoryPattern: true
},
events: {
enableEventSystem: true,
defaultEventPriority: 0,
enableAsyncEvents: true,
enableEventBatching: false,
batchSize: 10,
batchDelay: 16,
maxEventListeners: 100
}
};
/**
* 获取设置文件路径
*/
function getSettingsPath(): string {
const projectPath = Editor.Project.path;
return path.join(projectPath, '.ecs-framework-settings.json');
}
/**
* 加载设置
*/
function loadSettings(): ECSSettings {
try {
const settingsPath = getSettingsPath();
if (fs.existsSync(settingsPath)) {
const data = fs.readFileSync(settingsPath, 'utf-8');
const loadedSettings = JSON.parse(data);
// 合并默认设置,确保所有字段都存在
return deepMerge(defaultSettings, loadedSettings);
}
} catch (error) {
console.warn('Failed to load ECS settings:', error);
}
return JSON.parse(JSON.stringify(defaultSettings));
}
/**
* 保存设置
*/
function saveSettings(settings: ECSSettings): boolean {
try {
const settingsPath = getSettingsPath();
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf-8');
return true;
} catch (error) {
console.error('Failed to save ECS settings:', error);
return false;
}
}
/**
* 深度合并对象
*/
function deepMerge(target: any, source: any): any {
if (source === null || typeof source !== 'object') return source;
if (target === null || typeof target !== 'object') return source;
const result = { ...target };
for (const key in source) {
if (source.hasOwnProperty(key)) {
if (typeof source[key] === 'object' && source[key] !== null && !Array.isArray(source[key])) {
result[key] = deepMerge(target[key], source[key]);
} else {
result[key] = source[key];
}
}
}
return result;
}
/**
* 重置为默认设置
*/
function resetToDefaults(): ECSSettings {
return JSON.parse(JSON.stringify(defaultSettings));
}
module.exports = Editor.Panel.define({
listeners: {
show() { console.log('ECS Settings Panel Show'); },
hide() { console.log('ECS Settings Panel Hide'); },
},
template: `<div id="app"></div>`,
style: readFileSync(join(__dirname, '../../../static/style/settings/index.css'), 'utf-8'),
$: {
app: '#app',
},
ready() {
if (this.$.app) {
// 不要直接设置HTML内容让Vue来处理
const app = createApp(defineComponent({
setup() {
const settings = reactive(loadSettings());
const isDirty = ref(false);
const saving = ref(false);
const lastSaved = ref('');
// 监听设置变化
const markDirty = () => {
isDirty.value = true;
};
// 保存设置
const saveCurrentSettings = async () => {
saving.value = true;
try {
const success = saveSettings(settings);
if (success) {
isDirty.value = false;
lastSaved.value = new Date().toLocaleTimeString();
// 通知主进程设置已更新
Editor.Message.send('cocos-ecs-extension', 'settings-updated', settings);
Editor.Dialog.info('设置保存', {
detail: '✅ ECS框架设置已成功保存',
});
} else {
Editor.Dialog.error('保存失败', {
detail: '❌ 保存设置时发生错误,请检查文件权限。',
});
}
} catch (error) {
console.error('Save settings error:', error);
Editor.Dialog.error('保存失败', {
detail: `❌ 保存设置时发生错误:\n\n${error}`,
});
} finally {
saving.value = false;
}
};
// 重置设置
const resetSettings = () => {
Editor.Dialog.warn('重置设置', {
detail: '⚠️ 您确定要重置所有设置为默认值吗?\n\n此操作无法撤销。',
buttons: ['重置', '取消'],
}).then((result: any) => {
if (result.response === 0) {
const defaults = resetToDefaults();
Object.assign(settings, defaults);
isDirty.value = true;
Editor.Dialog.info('设置重置', {
detail: '✅ 设置已重置为默认值,请点击保存按钮确认更改。',
});
}
});
};
// 导出设置
const exportSettings = () => {
try {
const dataStr = JSON.stringify(settings, null, 2);
const blob = new Blob([dataStr], { type: 'application/json' });
const url = URL.createObjectURL(blob);
// 创建下载链接
const a = document.createElement('a');
a.href = url;
a.download = 'ecs-framework-settings.json';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
Editor.Dialog.info('导出成功', {
detail: '✅ 设置已导出到下载文件夹。',
});
} catch (error) {
console.error('Export settings error:', error);
Editor.Dialog.error('导出失败', {
detail: `❌ 导出设置时发生错误:\n\n${error}`,
});
}
};
// 导入设置
const importSettings = () => {
const input = document.createElement('input');
input.type = 'file';
input.accept = '.json';
input.style.display = 'none';
input.onchange = (e: any) => {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = (event) => {
try {
const importedSettings = JSON.parse(event.target?.result as string);
const mergedSettings = deepMerge(defaultSettings, importedSettings);
Object.assign(settings, mergedSettings);
isDirty.value = true;
Editor.Dialog.info('导入成功', {
detail: '✅ 设置已导入,请检查并保存。',
});
} catch (error) {
console.error('Import settings error:', error);
Editor.Dialog.error('导入失败', {
detail: `❌ 导入设置文件时发生错误:\n\n${error}\n\n请确保文件格式正确。`,
});
}
};
reader.readAsText(file);
};
document.body.appendChild(input);
input.click();
document.body.removeChild(input);
};
return {
settings,
isDirty,
saving,
lastSaved,
markDirty,
saveCurrentSettings,
resetSettings,
exportSettings,
importSettings
};
},
template: readFileSync(join(__dirname, '../../../static/template/settings/index.html'), 'utf-8'),
}));
app.config.compilerOptions.isCustomElement = (tag) => tag.startsWith('ui-');
app.mount(this.$.app);
panelDataMap.set(this, app);
}
},
beforeClose() { },
close() {
const app = panelDataMap.get(this);
if (app) {
app.unmount();
panelDataMap.delete(this);
}
},
});

View File

@@ -0,0 +1,187 @@
import { join } from 'path';
// 添加编辑器内的模块搜索路径
module.paths.push(join(Editor.App.path, 'node_modules'));
export function load() {
console.log('ECS Debug Scene Script loaded');
}
export function unload() {
console.log('ECS Debug Scene Script unloaded');
}
export const methods = {
/**
* 获取预览状态
* @returns {object} 预览状态信息
*/
getPreviewState() {
try {
// 检查是否在游戏运行状态
const { director } = require('cc');
if (director && director.getScene && director.getScene()) {
return {
isRunning: true,
engineLoaded: true
};
}
return {
isRunning: false,
engineLoaded: false
};
} catch (error) {
console.warn('Failed to get preview state:', error);
return {
isRunning: false,
engineLoaded: false
};
}
},
/**
* 检查ECS框架是否已加载
* @returns {boolean} ECS框架加载状态
*/
isECSFrameworkLoaded() {
try {
// 检查是否有ECS框架的全局对象
return typeof window !== 'undefined' && !!(window as any).ECSFramework;
} catch (error) {
console.warn('Failed to check ECS framework status:', error);
return false;
}
},
/**
* 获取场景基本信息
* @returns {object} 场景信息
*/
getSceneBasicInfo() {
try {
const { director } = require('cc');
if (director && director.getScene) {
const scene = director.getScene();
return {
sceneName: scene ? (scene.name || '当前场景') : '未知场景',
nodeCount: scene ? this.countNodes(scene) : 0,
isValid: scene ? scene.isValid : false
};
}
return {
sceneName: '未知场景',
nodeCount: 0,
isValid: false
};
} catch (error) {
console.warn('Failed to get scene basic info:', error);
return {
sceneName: '获取失败',
nodeCount: 0,
isValid: false
};
}
},
/**
* 获取ECS框架的调试信息
* @returns {object|null} ECS调试数据或null如果框架未加载
*/
getECSDebugInfo() {
try {
// 检查是否有ECS框架的全局对象
if (typeof window !== 'undefined' && (window as any).ECSFramework) {
const ecs = (window as any).ECSFramework;
// 获取当前场景和实体管理器
if (ecs.Core && ecs.Core.getCurrentScene) {
const scene = ecs.Core.getCurrentScene();
if (scene && scene.entityManager) {
const entityManager = scene.entityManager;
const systemManager = scene.systemManager;
// 收集调试信息
const debugInfo = {
timestamp: new Date().toISOString(),
frameworkLoaded: true,
currentScene: scene.name || '当前场景',
totalEntities: entityManager.entityCount || 0,
activeEntities: entityManager.activeEntityCount || 0,
pendingAdd: 0, // 需要具体API
pendingRemove: 0, // 需要具体API
totalSystems: systemManager ? systemManager.getSystemCount() : 0,
systemsInfo: [],
frameTime: 0, // 需要性能监控
memoryUsage: 0, // 需要内存监控
componentTypes: 0, // 需要组件统计
componentInstances: 0 // 需要组件实例统计
};
// 获取系统信息
if (systemManager && systemManager.getSystems) {
const systems = systemManager.getSystems();
debugInfo.systemsInfo = systems.map((system: any, index: number) => ({
name: system.constructor.name || `System${index}`,
entityCount: system.entities ? system.entities.length : 0,
executionTime: system.lastExecutionTime || 0,
updateOrder: index + 1
}));
}
return debugInfo;
}
}
}
// 检查是否直接导入了ECS模块
try {
// 这里需要根据实际的ECS框架导入方式调整
const { Core } = require('ecs-framework');
if (Core) {
const scene = Core.getCurrentScene();
if (scene) {
return {
timestamp: new Date().toISOString(),
frameworkLoaded: true,
currentScene: scene.name || '当前场景',
totalEntities: scene.entityManager?.entityCount || 0,
activeEntities: scene.entityManager?.activeEntityCount || 0,
pendingAdd: 0,
pendingRemove: 0,
totalSystems: scene.systemManager?.getSystemCount() || 0,
systemsInfo: [],
frameTime: 0,
memoryUsage: 0,
componentTypes: 0,
componentInstances: 0
};
}
}
} catch (error) {
// ECS框架未导入或未初始化
}
return null;
} catch (error) {
console.warn('Failed to get ECS debug info:', error);
return null;
}
},
/**
* 递归计算节点数量
* @param {any} node
* @returns {number}
*/
countNodes(node: any): number {
if (!node) return 0;
let count = 1; // 当前节点
if (node.children) {
for (const child of node.children) {
count += this.countNodes(child);
}
}
return count;
}
};

View File

@@ -0,0 +1,353 @@
/* ECS Framework 设置面板样式 */
.settings-container {
height: 100%;
display: flex;
flex-direction: column;
background: var(--color-normal-fill);
font-family: var(--font-family);
}
/* 头部样式 */
.settings-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
padding: 16px 20px;
background: var(--color-info-fill);
border-bottom: 1px solid var(--color-normal-border);
flex-shrink: 0;
}
.header-title h1 {
margin: 0;
font-size: 18px;
font-weight: 600;
color: var(--color-default-text);
}
.header-title p {
margin: 4px 0 0 0;
font-size: 12px;
color: var(--color-focus-text);
opacity: 0.8;
}
.header-actions {
display: flex;
gap: 8px;
}
.header-actions ui-button {
min-width: 80px;
height: 28px;
font-size: 12px;
}
/* 保存按钮状态 */
.save-button.dirty {
background: var(--color-warn-fill) !important;
border-color: var(--color-warn-border) !important;
color: var(--color-warn-text) !important;
}
.save-button.saving {
background: var(--color-info-fill) !important;
opacity: 0.7;
}
/* 图标动画 */
.spin {
animation: spin 1s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
/* 状态栏 */
.status-bar {
padding: 8px 20px;
background: var(--color-success-fill);
border-bottom: 1px solid var(--color-normal-border);
flex-shrink: 0;
}
.status-text {
font-size: 12px;
color: var(--color-success-text);
}
/* 设置内容区域 */
.settings-content {
flex: 1;
overflow-y: auto;
overflow-x: hidden;
padding: 0 20px 20px 20px;
max-height: calc(100vh - 140px); /* 确保有固定的滚动区域 */
scroll-behavior: smooth; /* 平滑滚动 */
}
/* 设置分组 */
.settings-section {
margin-bottom: 32px;
background: var(--color-default-fill);
border: 1px solid var(--color-normal-border);
border-radius: 8px;
overflow: hidden;
}
.section-header {
padding: 16px 20px;
background: var(--color-focus-fill);
border-bottom: 1px solid var(--color-normal-border);
}
.section-header h2 {
margin: 0;
font-size: 16px;
font-weight: 600;
color: var(--color-default-text);
}
.section-header p {
margin: 4px 0 0 0;
font-size: 12px;
color: var(--color-focus-text);
opacity: 0.8;
}
/* 设置网格 */
.settings-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 16px;
padding: 20px;
}
/* 设置项 */
.setting-item {
display: flex;
flex-direction: column;
gap: 6px;
}
.setting-item label {
font-size: 12px;
font-weight: 500;
color: var(--color-default-text);
}
.setting-item ui-input,
.setting-item ui-select,
.setting-item ui-num-input {
width: 100%;
height: 26px;
font-size: 12px;
}
/* 复选框项样式 */
.checkbox-item {
flex-direction: row;
align-items: center;
gap: 8px;
}
.checkbox-item ui-checkbox {
margin: 0;
}
/* 全宽设置项 */
.setting-item.full-width {
grid-column: 1 / -1;
}
/* 底部区域 */
.settings-footer {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 20px;
background: var(--color-focus-fill);
border-top: 1px solid var(--color-normal-border);
flex-shrink: 0;
}
.footer-info {
display: flex;
align-items: center;
gap: 6px;
font-size: 11px;
color: var(--color-focus-text);
opacity: 0.8;
}
.footer-info ui-icon {
width: 14px;
height: 14px;
}
.footer-actions {
display: flex;
align-items: center;
}
.dirty-indicator {
font-size: 11px;
color: var(--color-warn-text);
font-weight: 500;
}
/* 响应式设计 */
@media (max-width: 768px) {
.settings-header {
flex-direction: column;
gap: 12px;
align-items: stretch;
}
.header-actions {
justify-content: flex-end;
}
.settings-grid {
grid-template-columns: 1fr;
}
.settings-footer {
flex-direction: column;
gap: 8px;
align-items: stretch;
text-align: center;
}
}
/* 深色主题支持 */
@media (prefers-color-scheme: dark) {
.settings-container {
background: #2c2c2c;
}
.settings-section {
background: #3c3c3c;
border-color: #555;
}
.section-header {
background: #404040;
border-color: #555;
}
.settings-header {
background: #333;
border-color: #555;
}
.status-bar {
background: #2d5a2d;
}
.settings-footer {
background: #333;
border-color: #555;
}
}
/* 优化滚动条样式 */
.settings-content::-webkit-scrollbar {
width: 10px;
}
.settings-content::-webkit-scrollbar-track {
background: var(--color-normal-fill);
border-radius: 5px;
margin: 2px;
}
.settings-content::-webkit-scrollbar-thumb {
background: var(--color-focus-border);
border-radius: 5px;
transition: background-color 0.2s ease;
min-height: 30px;
}
.settings-content::-webkit-scrollbar-thumb:hover {
background: var(--color-warn-border);
}
.settings-content::-webkit-scrollbar-thumb:active {
background: var(--color-danger-border);
}
/* 表单元素统一样式 */
.setting-item ui-input,
.setting-item ui-select,
.setting-item ui-num-input {
border: 1px solid var(--color-normal-border);
border-radius: 4px;
padding: 4px 8px;
background: var(--color-default-fill);
color: var(--color-default-text);
transition: border-color 0.2s ease;
}
.setting-item ui-input:focus,
.setting-item ui-select:focus,
.setting-item ui-num-input:focus {
border-color: var(--color-focus-border);
outline: none;
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1);
}
/* 复选框样式 */
.setting-item ui-checkbox {
font-size: 12px;
color: var(--color-default-text);
}
/* 按钮样式覆盖 */
.header-actions .export-button {
background: var(--color-info-fill);
border-color: var(--color-info-border);
color: var(--color-info-text);
}
.header-actions .import-button {
background: var(--color-warn-fill);
border-color: var(--color-warn-border);
color: var(--color-warn-text);
}
.header-actions .reset-button {
background: var(--color-danger-fill);
border-color: var(--color-danger-border);
color: var(--color-danger-text);
}
/* 工具提示样式 */
.setting-item[title] {
cursor: help;
}
/* 加载状态 */
.loading-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.1);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.loading-spinner {
width: 32px;
height: 32px;
border: 3px solid var(--color-normal-border);
border-top: 3px solid var(--color-focus-border);
border-radius: 50%;
animation: spin 1s linear infinite;
}

View File

@@ -0,0 +1,294 @@
<div class="debug-panel">
<!-- 顶部控制栏 -->
<div class="debug-toolbar">
<div class="toolbar-section">
<label>游戏实例:</label>
<select v-model="selectedInstanceId" @change="onInstanceChanged" class="instance-selector">
<option value="">-- 选择实例 --</option>
<option v-for="instance in gameInstances" :key="instance.id" :value="instance.id">
{{ instance.name }} ({{ instance.isActive ? '活跃' : '离线' }})
</option>
</select>
<span class="instance-count">{{ gameInstances.length }} 个实例</span>
</div>
<div class="toolbar-section">
<button @click="manualRefresh" class="btn-refresh" :disabled="!selectedInstanceId">
<ui-icon value="refresh"></ui-icon>
刷新
</button>
<label class="checkbox-label">
<input type="checkbox" v-model="isAutoRefresh" @change="toggleAutoRefresh">
自动刷新
</label>
<select v-model="refreshInterval" @change="changeRefreshInterval" :disabled="!isAutoRefresh">
<option :value="100">0.1秒</option>
<option :value="250">0.25秒</option>
<option :value="500">0.5秒</option>
<option :value="1000">1秒</option>
<option :value="2000">2秒</option>
</select>
</div>
<div class="toolbar-section">
<span class="last-update">{{ lastUpdateTime }}</span>
</div>
</div>
<!-- 连接状态和基础信息 -->
<div class="debug-section">
<div class="section-header">
<h2>📊 实例状态</h2>
</div>
<div class="status-grid" v-if="selectedInstanceId && debugInfo.instanceId">
<div class="status-item">
<span class="label">实例名称:</span>
<span class="value">{{ debugInfo.instanceName }}</span>
</div>
<div class="status-item">
<span class="label">运行状态:</span>
<span class="value" :class="{ online: debugInfo.isRunning, offline: !debugInfo.isRunning }">
{{ debugInfo.isRunning ? '运行中' : '已停止' }}
</span>
</div>
<div class="status-item">
<span class="label">当前场景:</span>
<span class="value">{{ debugInfo.currentScene }}</span>
</div>
<div class="status-item">
<span class="label">运行时间:</span>
<span class="value">{{ formatUptime(debugInfo.uptime) }}</span>
</div>
</div>
<div class="no-instance" v-else>
<ui-icon value="info" class="info-icon"></ui-icon>
<span>请选择一个游戏实例查看详细信息</span>
</div>
</div>
<!-- 性能监控 -->
<div class="debug-section" v-if="debugInfo.instanceId">
<div class="section-header">
<h2>⚡ 性能监控</h2>
</div>
<div class="performance-grid">
<div class="perf-card">
<div class="perf-title">帧率</div>
<div class="perf-value" :class="getFpsColor(debugInfo.performance.fps)">
{{ debugInfo.performance.fps }} FPS
</div>
<div class="perf-detail">
引擎帧时间: {{ debugInfo.performance.engineFrameTime?.toFixed(2) || '0.00' }}ms
</div>
</div>
<div class="perf-card">
<div class="perf-title">ECS执行时间</div>
<div class="perf-value" :class="getECSTimeColor(debugInfo.performance.ecsPercentage)">
{{ debugInfo.performance.frameTime.toFixed(3) }}ms
</div>
<div class="perf-detail">
占总帧时间: {{ debugInfo.performance.ecsPercentage?.toFixed(1) || '0.0' }}%
</div>
</div>
<div class="perf-card">
<div class="perf-title">总内存</div>
<div class="perf-value" :class="getMemoryColor(debugInfo.memory.usedMemory / debugInfo.memory.totalMemory * 100)">
{{ formatMemory(debugInfo.memory.usedMemory) }}
</div>
<div class="perf-detail">
/ {{ formatMemory(debugInfo.memory.totalMemory) }}
</div>
</div>
<div class="perf-card">
<div class="perf-title">GC回收</div>
<div class="perf-value">{{ debugInfo.memory.gcCollections }}</div>
<div class="perf-detail">次数</div>
</div>
</div>
</div>
<!-- 内存分析 -->
<div class="debug-section" v-if="debugInfo.instanceId">
<div class="section-header">
<h2>💾 内存分析</h2>
</div>
<div class="memory-breakdown">
<div class="memory-item">
<span class="memory-label">实体内存:</span>
<span class="memory-value">{{ formatMemory(debugInfo.memory.entityMemory) }}</span>
<div class="memory-bar">
<div class="memory-fill" :style="{ width: (debugInfo.memory.entityMemory / debugInfo.memory.usedMemory * 100) + '%' }"></div>
</div>
</div>
<div class="memory-item">
<span class="memory-label">组件内存:</span>
<span class="memory-value">{{ formatMemory(debugInfo.memory.componentMemory) }}</span>
<div class="memory-bar">
<div class="memory-fill" :style="{ width: (debugInfo.memory.componentMemory / debugInfo.memory.usedMemory * 100) + '%' }"></div>
</div>
</div>
<div class="memory-item">
<span class="memory-label">系统内存:</span>
<span class="memory-value">{{ formatMemory(debugInfo.memory.systemMemory) }}</span>
<div class="memory-bar">
<div class="memory-fill" :style="{ width: (debugInfo.memory.systemMemory / debugInfo.memory.usedMemory * 100) + '%' }"></div>
</div>
</div>
<div class="memory-item">
<span class="memory-label">对象池内存:</span>
<span class="memory-value">{{ formatMemory(debugInfo.memory.pooledMemory) }}</span>
<div class="memory-bar">
<div class="memory-fill" :style="{ width: (debugInfo.memory.pooledMemory / debugInfo.memory.usedMemory * 100) + '%' }"></div>
</div>
</div>
</div>
</div>
<!-- 实体统计 -->
<div class="debug-section" v-if="debugInfo.instanceId">
<div class="section-header">
<h2>👥 实体统计</h2>
</div>
<div class="entity-stats">
<div class="stat-card">
<div class="stat-number">{{ debugInfo.entities.total }}</div>
<div class="stat-label">总实体数</div>
</div>
<div class="stat-card">
<div class="stat-number">{{ debugInfo.entities.active }}</div>
<div class="stat-label">活跃实体</div>
</div>
<div class="stat-card">
<div class="stat-number">{{ debugInfo.entities.inactive }}</div>
<div class="stat-label">非活跃实体</div>
</div>
<div class="stat-card">
<div class="stat-number">{{ debugInfo.entities.pendingAdd }}</div>
<div class="stat-label">待添加</div>
</div>
</div>
<!-- Archetype分布 -->
<div class="archetype-section" v-if="debugInfo.entities.entitiesPerArchetype.length > 0">
<h3>Archetype 分布</h3>
<div class="archetype-list">
<div v-for="archetype in debugInfo.entities.entitiesPerArchetype" :key="archetype.signature" class="archetype-item">
<div class="archetype-signature">{{ archetype.signature }}</div>
<div class="archetype-stats">
<span class="entity-count">{{ archetype.count }} 实体</span>
<span class="memory-usage">{{ formatMemory(archetype.memory) }}</span>
</div>
</div>
</div>
</div>
</div>
<!-- 组件分析 -->
<div class="debug-section" v-if="debugInfo.instanceId">
<div class="section-header">
<h2>🧩 组件分析</h2>
</div>
<div class="component-overview">
<div class="overview-item">
<span class="label">组件类型:</span>
<span class="value">{{ debugInfo.components.totalTypes }}</span>
</div>
<div class="overview-item">
<span class="label">组件实例:</span>
<span class="value">{{ debugInfo.components.totalInstances }}</span>
</div>
</div>
<div class="component-table" v-if="debugInfo.components.componentStats.length > 0">
<div class="table-header">
<div class="col-name">组件类型</div>
<div class="col-count">实例数</div>
<div class="col-memory">内存占用</div>
<div class="col-pool">对象池</div>
<div class="col-efficiency">利用率</div>
</div>
<div v-for="comp in debugInfo.components.componentStats" :key="comp.typeName" class="table-row">
<div class="col-name">{{ comp.typeName }}</div>
<div class="col-count">{{ comp.instanceCount }}</div>
<div class="col-memory">{{ formatMemory(comp.totalMemory) }}</div>
<div class="col-pool">{{ comp.poolSize }}</div>
<div class="col-efficiency">
<div class="efficiency-bar">
<div class="efficiency-fill" :style="{ width: comp.poolUtilization + '%' }"></div>
</div>
{{ comp.poolUtilization.toFixed(1) }}%
</div>
</div>
</div>
</div>
<!-- 系统性能 -->
<div class="debug-section" v-if="debugInfo.instanceId">
<div class="section-header">
<h2>⚙️ 系统性能</h2>
</div>
<div class="systems-table" v-if="debugInfo.systems.systemStats.length > 0">
<div class="table-header">
<div class="col-name">系统名称</div>
<div class="col-entities">实体数</div>
<div class="col-time">执行时间</div>
<div class="col-percentage">ECS占比</div>
<div class="col-order">优先级</div>
<div class="col-status">状态</div>
</div>
<div v-for="system in debugInfo.systems.systemStats" :key="system.name" class="table-row">
<div class="col-name">{{ system.name }}</div>
<div class="col-entities">{{ system.entityCount }}</div>
<div class="col-time">
<span :class="getExecutionTimeColor(system.averageExecutionTime)">
{{ system.averageExecutionTime.toFixed(3) }}ms
</span>
<div class="time-range">
({{ system.minExecutionTime.toFixed(3) }}-{{ system.maxExecutionTime.toFixed(3) }}ms)
</div>
</div>
<div class="col-percentage">
<div class="percentage-bar">
<div class="percentage-fill" :style="{ width: (system.percentage || 0) + '%' }"></div>
</div>
{{ (system.percentage || 0).toFixed(1) }}%
</div>
<div class="col-order">{{ system.updateOrder }}</div>
<div class="col-status">
<span :class="{ enabled: system.enabled, disabled: !system.enabled }">
{{ system.enabled ? '启用' : '禁用' }}
</span>
</div>
</div>
</div>
</div>
<!-- 服务器状态 -->
<div class="debug-section" v-if="gameInstances.length === 0">
<div class="section-header">
<h2>🔌 调试服务器</h2>
</div>
<div class="server-status">
<div class="status-message">
<ui-icon value="wifi" class="server-icon"></ui-icon>
<div class="message-content">
<h3>等待游戏实例连接...</h3>
<p>调试服务器正在 <strong>ws://localhost:8080</strong> 监听连接</p>
<p>请确保游戏中的ECS框架已启用调试模式</p>
</div>
</div>
<div class="connection-help">
<h4>连接说明:</h4>
<ul>
<li>在ECSManager组件中启用调试模式</li>
<li>运行游戏,框架会自动连接到调试服务器</li>
<li>支持多个游戏实例同时连接</li>
<li>可以在上方选择不同的实例进行调试</li>
</ul>
</div>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,322 @@
<div id="app">
<div class="settings-container">
<!-- 头部 -->
<div class="settings-header">
<div class="header-title">
<h1>🔧 ECS Framework 设置</h1>
<p>配置ECS框架的行为和性能选项</p>
</div>
<div class="header-actions">
<ui-button class="save-button" :class="{ dirty: isDirty, saving: saving }"
@click="saveCurrentSettings" :disabled="saving">
<ui-icon value="confirm" v-if="!saving"></ui-icon>
<ui-icon value="loading" class="spin" v-if="saving"></ui-icon>
{{ saving ? '保存中...' : (isDirty ? '保存 *' : '保存') }}
</ui-button>
<ui-button class="export-button" @click="exportSettings">
<ui-icon value="download"></ui-icon>
导出
</ui-button>
<ui-button class="import-button" @click="importSettings">
<ui-icon value="upload"></ui-icon>
导入
</ui-button>
<ui-button class="reset-button" @click="resetSettings">
<ui-icon value="reset"></ui-icon>
重置
</ui-button>
</div>
</div>
<!-- 状态栏 -->
<div class="status-bar" v-if="lastSaved">
<span class="status-text">最后保存: {{ lastSaved }}</span>
</div>
<!-- 设置内容 -->
<div class="settings-content">
<!-- 代码生成设置 -->
<div class="settings-section">
<div class="section-header">
<h2>📝 代码生成设置</h2>
<p>配置自动生成代码的格式和选项</p>
</div>
<div class="settings-grid">
<div class="setting-item">
<label>模板语言</label>
<ui-select v-model="settings.codeGeneration.template" @change="markDirty">
<option value="typescript">TypeScript</option>
<option value="javascript">JavaScript</option>
</ui-select>
</div>
<div class="setting-item">
<label>组件后缀</label>
<ui-input v-model="settings.codeGeneration.componentSuffix"
@change="markDirty" placeholder="Component"></ui-input>
</div>
<div class="setting-item">
<label>系统后缀</label>
<ui-input v-model="settings.codeGeneration.systemSuffix"
@change="markDirty" placeholder="System"></ui-input>
</div>
<div class="setting-item">
<label>缩进风格</label>
<ui-select v-model="settings.codeGeneration.indentStyle" @change="markDirty">
<option value="spaces">空格</option>
<option value="tabs">制表符</option>
</ui-select>
</div>
<div class="setting-item">
<label>缩进大小</label>
<ui-num-input v-model="settings.codeGeneration.indentSize"
@change="markDirty" :min="1" :max="8"></ui-num-input>
</div>
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.codeGeneration.useStrictMode" @change="markDirty">
启用严格模式
</ui-checkbox>
</div>
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.codeGeneration.generateComments" @change="markDirty">
生成注释
</ui-checkbox>
</div>
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.codeGeneration.generateImports" @change="markDirty">
自动添加导入语句
</ui-checkbox>
</div>
</div>
</div>
<!-- 性能监控设置 -->
<div class="settings-section">
<div class="section-header">
<h2>⚡ 性能监控设置</h2>
<p>配置性能监控和优化建议</p>
</div>
<div class="settings-grid">
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.performance.enableMonitoring" @change="markDirty">
启用性能监控
</ui-checkbox>
</div>
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.performance.enableFpsMonitoring" @change="markDirty">
启用FPS监控
</ui-checkbox>
</div>
<div class="setting-item">
<label>目标FPS</label>
<ui-num-input v-model="settings.performance.targetFps"
@change="markDirty" :min="30" :max="120"></ui-num-input>
</div>
<div class="setting-item">
<label>执行时间警告阈值 (ms)</label>
<ui-num-input v-model="settings.performance.warningThreshold"
@change="markDirty" :min="1" :max="100" :step="0.1"></ui-num-input>
</div>
<div class="setting-item">
<label>执行时间严重阈值 (ms)</label>
<ui-num-input v-model="settings.performance.criticalThreshold"
@change="markDirty" :min="1" :max="200" :step="0.1"></ui-num-input>
</div>
<div class="setting-item">
<label>内存警告阈值 (MB)</label>
<ui-num-input v-model="settings.performance.memoryWarningMB"
@change="markDirty" :min="10" :max="1000"></ui-num-input>
</div>
<div class="setting-item">
<label>内存严重阈值 (MB)</label>
<ui-num-input v-model="settings.performance.memoryCriticalMB"
@change="markDirty" :min="50" :max="2000"></ui-num-input>
</div>
<div class="setting-item">
<label>性能采样数量</label>
<ui-num-input v-model="settings.performance.maxRecentSamples"
@change="markDirty" :min="10" :max="300"></ui-num-input>
</div>
</div>
</div>
<!-- 调试设置 -->
<div class="settings-section">
<div class="section-header">
<h2>🐛 调试设置</h2>
<p>配置调试模式和日志选项</p>
</div>
<div class="settings-grid">
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.debugging.enableDebugMode" @change="markDirty">
启用调试模式
</ui-checkbox>
</div>
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.debugging.showEntityCount" @change="markDirty">
显示实体数量
</ui-checkbox>
</div>
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.debugging.showSystemExecutionTime" @change="markDirty">
显示系统执行时间
</ui-checkbox>
</div>
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.debugging.enablePerformanceWarnings" @change="markDirty">
启用性能警告
</ui-checkbox>
</div>
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.debugging.enableDetailedLogs" @change="markDirty">
启用详细日志
</ui-checkbox>
</div>
<div class="setting-item">
<label>日志级别</label>
<ui-select v-model="settings.debugging.logLevel" @change="markDirty">
<option value="none"></option>
<option value="error">错误</option>
<option value="warn">警告</option>
<option value="info">信息</option>
<option value="debug">调试</option>
</ui-select>
</div>
</div>
</div>
<!-- 编辑器集成设置 -->
<div class="settings-section">
<div class="section-header">
<h2>⚙️ 编辑器集成</h2>
<p>配置与Cocos Creator的集成选项</p>
</div>
<div class="settings-grid">
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.editor.autoRefreshAssets" @change="markDirty">
自动刷新资源
</ui-checkbox>
</div>
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.editor.showWelcomePanelOnStartup" @change="markDirty">
启动时显示欢迎面板
</ui-checkbox>
</div>
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.editor.enableAutoUpdates" @change="markDirty">
启用自动更新
</ui-checkbox>
</div>
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.editor.enableNotifications" @change="markDirty">
启用通知
</ui-checkbox>
</div>
<div class="setting-item">
<label>更新频道</label>
<ui-select v-model="settings.editor.updateChannel" @change="markDirty">
<option value="stable">稳定版</option>
<option value="beta">测试版</option>
<option value="alpha">开发版</option>
</ui-select>
</div>
</div>
</div>
<!-- 项目模板设置 -->
<div class="settings-section">
<div class="section-header">
<h2>📁 项目模板设置</h2>
<p>配置生成项目模板的默认选项</p>
</div>
<div class="settings-grid">
<div class="setting-item">
<label>默认实体名称</label>
<ui-input v-model="settings.template.defaultEntityName"
@change="markDirty" placeholder="GameEntity"></ui-input>
</div>
<div class="setting-item">
<label>默认组件名称</label>
<ui-input v-model="settings.template.defaultComponentName"
@change="markDirty" placeholder="CustomComponent"></ui-input>
</div>
<div class="setting-item">
<label>默认系统名称</label>
<ui-input v-model="settings.template.defaultSystemName"
@change="markDirty" placeholder="CustomSystem"></ui-input>
</div>
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.template.createExampleFiles" @change="markDirty">
创建示例文件
</ui-checkbox>
</div>
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.template.includeDocumentation" @change="markDirty">
包含文档
</ui-checkbox>
</div>
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.template.useFactoryPattern" @change="markDirty">
使用工厂模式
</ui-checkbox>
</div>
</div>
</div>
<!-- 事件系统设置 -->
<div class="settings-section">
<div class="section-header">
<h2>📡 事件系统设置</h2>
<p>配置事件系统的行为和性能</p>
</div>
<div class="settings-grid">
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.events.enableEventSystem" @change="markDirty">
启用事件系统
</ui-checkbox>
</div>
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.events.enableAsyncEvents" @change="markDirty">
启用异步事件
</ui-checkbox>
</div>
<div class="setting-item checkbox-item">
<ui-checkbox v-model="settings.events.enableEventBatching" @change="markDirty">
启用事件批处理
</ui-checkbox>
</div>
<div class="setting-item">
<label>默认事件优先级</label>
<ui-num-input v-model="settings.events.defaultEventPriority"
@change="markDirty" :min="-100" :max="100"></ui-num-input>
</div>
<div class="setting-item">
<label>批处理大小</label>
<ui-num-input v-model="settings.events.batchSize"
@change="markDirty" :min="1" :max="100"></ui-num-input>
</div>
<div class="setting-item">
<label>批处理延迟 (ms)</label>
<ui-num-input v-model="settings.events.batchDelay"
@change="markDirty" :min="1" :max="1000"></ui-num-input>
</div>
<div class="setting-item">
<label>最大事件监听器数</label>
<ui-num-input v-model="settings.events.maxEventListeners"
@change="markDirty" :min="10" :max="1000"></ui-num-input>
</div>
</div>
</div>
</div>
<!-- 底部提示 -->
<div class="settings-footer">
<div class="footer-info">
<ui-icon value="info"></ui-icon>
<span>设置将保存到项目目录的 .ecs-framework-settings.json 文件中</span>
</div>
<div class="footer-actions">
<span class="dirty-indicator" v-if="isDirty">● 有未保存的更改</span>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,132 @@
const fs = require('fs');
const path = require('path');
// 模拟项目路径(实际会是真实的项目路径)
const projectPath = process.cwd();
const settingsPath = path.join(projectPath, '.ecs-framework-settings.json');
console.log('🧪 测试ECS框架设置功能...');
console.log('设置文件路径:', settingsPath);
// 默认设置
const testSettings = {
codeGeneration: {
template: 'typescript',
useStrictMode: true,
generateComments: true,
generateImports: true,
componentSuffix: 'Component',
systemSuffix: 'System',
indentStyle: 'spaces',
indentSize: 4
},
performance: {
enableMonitoring: true,
warningThreshold: 16.67,
criticalThreshold: 33.33,
memoryWarningMB: 100,
memoryCriticalMB: 200,
maxRecentSamples: 60,
enableFpsMonitoring: true,
targetFps: 60
},
debugging: {
enableDebugMode: true,
showEntityCount: true,
showSystemExecutionTime: true,
enablePerformanceWarnings: true,
logLevel: 'info',
enableDetailedLogs: false
},
editor: {
autoRefreshAssets: true,
showWelcomePanelOnStartup: true,
enableAutoUpdates: false,
updateChannel: 'stable',
enableNotifications: true
},
template: {
defaultEntityName: 'TestEntity', // 修改这个值来测试
defaultComponentName: 'TestComponent',
defaultSystemName: 'TestSystem',
createExampleFiles: true,
includeDocumentation: true,
useFactoryPattern: true
},
events: {
enableEventSystem: true,
defaultEventPriority: 0,
enableAsyncEvents: true,
enableEventBatching: false,
batchSize: 10,
batchDelay: 16,
maxEventListeners: 100
}
};
// 测试保存功能
console.log('✅ 测试保存设置...');
try {
fs.writeFileSync(settingsPath, JSON.stringify(testSettings, null, 2), 'utf-8');
console.log('✅ 设置已成功保存到:', settingsPath);
} catch (error) {
console.error('❌ 保存设置失败:', error);
}
// 测试加载功能
console.log('✅ 测试加载设置...');
try {
if (fs.existsSync(settingsPath)) {
const loadedData = fs.readFileSync(settingsPath, 'utf-8');
const loadedSettings = JSON.parse(loadedData);
console.log('✅ 设置已成功加载');
console.log('默认实体名称:', loadedSettings.template.defaultEntityName);
console.log('调试模式:', loadedSettings.debugging.enableDebugMode);
console.log('目标FPS:', loadedSettings.performance.targetFps);
// 验证数据完整性
const expectedKeys = Object.keys(testSettings);
const loadedKeys = Object.keys(loadedSettings);
if (expectedKeys.every(key => loadedKeys.includes(key))) {
console.log('✅ 数据完整性检查通过');
} else {
console.log('❌ 数据完整性检查失败');
}
} else {
console.log('❌ 设置文件不存在');
}
} catch (error) {
console.error('❌ 加载设置失败:', error);
}
// 测试修改和重新保存
console.log('✅ 测试修改设置...');
try {
const modifiedSettings = { ...testSettings };
modifiedSettings.template.defaultEntityName = 'ModifiedEntity';
modifiedSettings.performance.targetFps = 120;
fs.writeFileSync(settingsPath, JSON.stringify(modifiedSettings, null, 2), 'utf-8');
// 重新加载验证
const reloadedData = fs.readFileSync(settingsPath, 'utf-8');
const reloadedSettings = JSON.parse(reloadedData);
if (reloadedSettings.template.defaultEntityName === 'ModifiedEntity' &&
reloadedSettings.performance.targetFps === 120) {
console.log('✅ 设置修改测试通过');
} else {
console.log('❌ 设置修改测试失败');
}
} catch (error) {
console.error('❌ 修改设置测试失败:', error);
}
console.log('🎉 测试完成!设置功能工作正常。');
console.log('📁 设置文件位置:', settingsPath);
// 清理测试文件(可选)
// fs.unlinkSync(settingsPath);
// console.log('🧹 已清理测试文件');

View File

@@ -0,0 +1,23 @@
{
"__version__": "3.0.9",
"game": {
"name": "UNKNOW GAME",
"app_id": "UNKNOW",
"c_id": "0"
},
"appConfigMaps": [
{
"app_id": "UNKNOW",
"config_id": "a17c82"
}
],
"configs": [
{
"app_id": "UNKNOW",
"config_id": "a17c82",
"config_name": "Default",
"config_remarks": "",
"services": []
}
]
}

36
package-lock.json generated
View File

@@ -8,6 +8,10 @@
"name": "@esengine/ecs-framework",
"version": "2.1.19",
"license": "MIT",
"dependencies": {
"@types/ws": "^8.18.1",
"ws": "^8.18.2"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^28.0.3",
"@rollup/plugin-node-resolve": "^16.0.1",
@@ -580,7 +584,6 @@
"version": "20.19.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.0.tgz",
"integrity": "sha512-hfrc+1tud1xcdVTABC2JiomZJEklMcXYNTVtZLAeqTVWD+qL5jkHKT+1lOtqDdGxt+mB53DTtiz673vfjU8D1Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~6.21.0"
@@ -592,6 +595,15 @@
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
"dev": true
},
"node_modules/@types/ws": {
"version": "8.18.1",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
"integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/acorn": {
"version": "8.15.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
@@ -1307,7 +1319,6 @@
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"dev": true,
"license": "MIT"
},
"node_modules/wrap-ansi-cjs": {
@@ -1328,6 +1339,27 @@
"funding": {
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
"node_modules/ws": {
"version": "8.18.2",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz",
"integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
}
}
}

View File

@@ -50,5 +50,9 @@
"repository": {
"type": "git",
"url": "https://github.com/esengine/ecs-framework.git"
},
"dependencies": {
"@types/ws": "^8.18.1",
"ws": "^8.18.2"
}
}

View File

@@ -6,6 +6,8 @@ import { PerformanceMonitor } from './Utils/PerformanceMonitor';
import { PoolManager } from './Utils/Pool';
import { ECSFluentAPI, createECSAPI } from './ECS/Core/FluentAPI';
import { Scene } from './ECS/Scene';
import { DebugReporter } from './Utils/DebugReporter';
import { ICoreConfig, IECSDebugConfig } from './Types';
/**
* 游戏引擎核心类
@@ -107,27 +109,56 @@ export class Core {
*/
public _scene?: Scene;
/**
* 调试报告器
*
* 负责收集和发送调试数据。
*/
public _debugReporter?: DebugReporter;
/**
* Core配置
*/
private _config: ICoreConfig;
/**
* 创建核心实例
*
* @param debug - 是否启用调试模式默认为true
* @param enableEntitySystems - 是否启用实体系统默认为true
* @param config - Core配置对象
*/
private constructor(debug: boolean = true, enableEntitySystems: boolean = true) {
private constructor(config: ICoreConfig = {}) {
Core._instance = this;
// 保存配置
this._config = {
debug: true,
enableEntitySystems: true,
...config
};
// 初始化管理器
this._timerManager = new TimerManager();
Core.registerGlobalManager(this._timerManager);
// 初始化性能监控器
this._performanceMonitor = PerformanceMonitor.instance;
// 在调试模式下启用性能监控
if (this._config.debug) {
this._performanceMonitor.enable();
}
// 初始化对象池管理器
this._poolManager = PoolManager.getInstance();
Core.entitySystemsEnabled = enableEntitySystems;
this.debug = debug;
Core.entitySystemsEnabled = this._config.enableEntitySystems || true;
this.debug = this._config.debug || true;
// 初始化调试报告器
if (this._config.debugConfig?.enabled) {
this._debugReporter = new DebugReporter(this, this._config.debugConfig);
}
this.initialize();
}
@@ -180,12 +211,16 @@ export class Core {
*
* 如果实例已存在,则返回现有实例。
*
* @param debug - 是否为调试模式默认为true
* @param config - Core配置也可以直接传入boolean表示debug模式向后兼容
* @returns Core实例
*/
public static create(debug: boolean = true): Core {
public static create(config: ICoreConfig | boolean = true): Core {
if (this._instance == null) {
this._instance = new Core(debug);
// 向后兼容如果传入boolean转换为配置对象
const coreConfig: ICoreConfig = typeof config === 'boolean'
? { debug: config, enableEntitySystems: true }
: config;
this._instance = new Core(coreConfig);
}
return this._instance;
}
@@ -284,6 +319,66 @@ export class Core {
return this._instance?._ecsAPI || null;
}
/**
* 启用调试功能
*
* @param config 调试配置
*/
public static enableDebug(config: IECSDebugConfig): void {
if (!this._instance) {
console.warn("Core实例未创建请先调用Core.create()");
return;
}
if (this._instance._debugReporter) {
this._instance._debugReporter.updateConfig(config);
} else {
this._instance._debugReporter = new DebugReporter(this._instance, config);
}
// 更新Core配置
this._instance._config.debugConfig = config;
}
/**
* 禁用调试功能
*/
public static disableDebug(): void {
if (!this._instance) return;
if (this._instance._debugReporter) {
this._instance._debugReporter.stop();
this._instance._debugReporter = undefined;
}
// 更新Core配置
if (this._instance._config.debugConfig) {
this._instance._config.debugConfig.enabled = false;
}
}
/**
* 获取调试数据
*
* @returns 当前调试数据如果调试未启用则返回null
*/
public static getDebugData(): any {
if (!this._instance?._debugReporter) {
return null;
}
return this._instance._debugReporter.getDebugData();
}
/**
* 检查调试是否启用
*
* @returns 调试状态
*/
public static get isDebugEnabled(): boolean {
return this._instance?._config.debugConfig?.enabled || false;
}
/**
* 场景切换回调
*
@@ -297,6 +392,11 @@ export class Core {
const scene = this._scene as any;
this._ecsAPI = createECSAPI(scene, scene.querySystem, scene.eventSystem);
}
// 通知调试报告器场景已变更
if (this._debugReporter) {
this._debugReporter.onSceneChanged();
}
}
/**

View File

@@ -234,4 +234,207 @@ export interface IPerformanceEventData extends IEventData {
memoryUsage?: number;
/** 额外数据 */
metadata?: Record<string, any>;
}
/**
* ECS调试配置接口
*/
export interface IECSDebugConfig {
/** 是否启用调试 */
enabled: boolean;
/** WebSocket服务器URL */
websocketUrl: string;
/** 是否自动重连 */
autoReconnect?: boolean;
/** 数据更新间隔(毫秒) */
updateInterval?: number;
/** 数据通道配置 */
channels: {
entities: boolean;
systems: boolean;
performance: boolean;
components: boolean;
scenes: boolean;
};
}
/**
* Core配置接口
*/
export interface ICoreConfig {
/** 是否启用调试模式 */
debug?: boolean;
/** 是否启用实体系统 */
enableEntitySystems?: boolean;
/** 调试配置 */
debugConfig?: IECSDebugConfig;
}
/**
* ECS调试数据接口
*/
export interface IECSDebugData {
/** 时间戳 */
timestamp: number;
/** 框架版本 */
frameworkVersion?: string;
/** 是否正在运行 */
isRunning: boolean;
/** 框架是否已加载 */
frameworkLoaded: boolean;
/** 当前场景名称 */
currentScene: string;
/** 实体数据 */
entities?: IEntityDebugData;
/** 系统数据 */
systems?: ISystemDebugData;
/** 性能数据 */
performance?: IPerformanceDebugData;
/** 组件数据 */
components?: IComponentDebugData;
/** 场景数据 */
scenes?: ISceneDebugData;
}
/**
* 实体调试数据接口
*/
export interface IEntityDebugData {
/** 总实体数 */
totalEntities: number;
/** 激活实体数 */
activeEntities: number;
/** 待添加实体数 */
pendingAdd: number;
/** 待移除实体数 */
pendingRemove: number;
/** 按Archetype分组的实体分布 */
entitiesPerArchetype: Array<{
signature: string;
count: number;
memory: number;
}>;
/** 组件数量最多的前几个实体 */
topEntitiesByComponents: Array<{
id: string;
name: string;
componentCount: number;
memory: number;
}>;
/** 实体详情列表 */
entityDetails?: Array<{
id: string | number;
name?: string;
tag?: string;
enabled: boolean;
componentCount: number;
components: string[];
}>;
}
/**
* 系统调试数据接口
*/
export interface ISystemDebugData {
/** 总系统数 */
totalSystems: number;
/** 系统信息列表 */
systemsInfo: Array<{
name: string;
type: string;
entityCount: number;
executionTime?: number;
averageExecutionTime?: number;
minExecutionTime?: number;
maxExecutionTime?: number;
executionTimeHistory?: number[];
memoryUsage?: number;
updateOrder: number;
enabled: boolean;
lastUpdateTime?: number;
}>;
}
/**
* 性能调试数据接口
*/
export interface IPerformanceDebugData {
/** ECS框架执行时间毫秒 */
frameTime: number;
/** 引擎总帧时间(毫秒) */
engineFrameTime?: number;
/** ECS占总帧时间百分比 */
ecsPercentage?: number;
/** 内存使用量MB */
memoryUsage: number;
/** FPS */
fps: number;
/** 平均ECS执行时间毫秒 */
averageFrameTime: number;
/** 最小ECS执行时间毫秒 */
minFrameTime: number;
/** 最大ECS执行时间毫秒 */
maxFrameTime: number;
/** ECS执行时间历史记录 */
frameTimeHistory: number[];
/** 系统性能详情 */
systemPerformance: Array<{
systemName: string;
averageTime: number;
maxTime: number;
minTime: number;
samples: number;
percentage?: number; // 系统占ECS总时间的百分比
}>;
/** 内存分配详情 */
memoryDetails?: {
entities: number;
components: number;
systems: number;
pooled: number;
totalMemory: number;
usedMemory: number;
freeMemory: number;
gcCollections: number;
};
}
/**
* 组件调试数据接口
*/
export interface IComponentDebugData {
/** 组件类型数 */
componentTypes: number;
/** 组件实例总数 */
componentInstances: number;
/** 组件分布统计 */
componentStats: Array<{
typeName: string;
instanceCount: number;
memoryPerInstance: number;
totalMemory: number;
poolSize: number;
poolUtilization: number;
averagePerEntity?: number;
}>;
}
/**
* 场景调试数据接口
*/
export interface ISceneDebugData {
/** 当前场景名称 */
currentSceneName: string;
/** 场景是否已初始化 */
isInitialized: boolean;
/** 场景运行时间(秒) */
sceneRunTime: number;
/** 场景实体数 */
sceneEntityCount: number;
/** 场景系统数 */
sceneSystemCount: number;
/** 场景内存使用量 */
sceneMemory: number;
/** 场景启动时间 */
sceneUptime: number;
}

900
src/Utils/DebugReporter.ts Normal file
View File

@@ -0,0 +1,900 @@
import {
IECSDebugConfig,
IECSDebugData,
IEntityDebugData,
ISystemDebugData,
IPerformanceDebugData,
IComponentDebugData,
ISceneDebugData
} from '../Types';
import { Core } from '../Core';
import { Time } from './Time';
/**
* ECS调试报告器 - WebSocket模式
*
* 负责收集ECS框架的运行时调试数据并通过WebSocket发送到调试服务器
*/
export class DebugReporter {
private config: IECSDebugConfig;
private core: Core;
private timer?: any;
private ws?: WebSocket;
private lastFrameTime: number = 0;
private frameCount: number = 0;
private lastFpsTime: number = 0;
private fps: number = 0;
private sceneStartTime: number = 0;
private isConnected: boolean = false;
private reconnectAttempts: number = 0;
private maxReconnectAttempts: number = 5;
private frameTimeHistory: number[] = [];
private maxHistoryLength: number = 60; // 保存60帧的历史数据
/**
* 构造函数
* @param core Core实例
* @param config 调试配置
*/
constructor(core: Core, config: IECSDebugConfig) {
this.core = core;
this.config = config;
this.sceneStartTime = Date.now();
if (this.config.enabled) {
this.start();
}
}
/**
* 启动调试报告器
*/
private start(): void {
this.connectWebSocket();
}
/**
* 停止调试报告器
*/
public stop(): void {
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
if (this.ws) {
this.ws.close();
this.ws = undefined;
}
this.isConnected = false;
}
/**
* 更新配置
* @param newConfig 新配置
*/
public updateConfig(newConfig: IECSDebugConfig): void {
const wasEnabled = this.config.enabled;
const urlChanged = this.config.websocketUrl !== newConfig.websocketUrl;
this.config = newConfig;
if (!newConfig.enabled && wasEnabled) {
this.stop();
} else if (newConfig.enabled && (!wasEnabled || urlChanged)) {
this.stop();
this.start();
}
}
/**
* 连接WebSocket
*/
private connectWebSocket(): void {
if (!this.config.websocketUrl) {
console.error('[ECS Debug] WebSocket URL not provided');
return;
}
try {
this.ws = new WebSocket(this.config.websocketUrl);
this.ws.onopen = () => {
this.isConnected = true;
this.reconnectAttempts = 0;
this.startDataStream();
// 发送初始化消息
this.send({
type: 'init',
data: {
frameworkVersion: this.getFrameworkVersion(),
timestamp: Date.now()
}
});
};
this.ws.onmessage = (event) => {
try {
const message = JSON.parse(event.data);
this.handleMessage(message);
} catch (error) {
console.error('[ECS Debug] Failed to parse message:', error);
}
};
this.ws.onclose = (event) => {
this.isConnected = false;
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
// 自动重连
if (this.config.autoReconnect !== false && this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++;
const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts - 1), 10000);
setTimeout(() => this.connectWebSocket(), delay);
}
};
this.ws.onerror = (error) => {
console.error('[ECS Debug] WebSocket error:', error);
};
} catch (error) {
console.error('[ECS Debug] Failed to create WebSocket:', error);
}
}
/**
* 启动数据流
*/
private startDataStream(): void {
const interval = this.config.updateInterval || 1000;
this.timer = setInterval(() => {
if (this.isConnected) {
try {
const data = this.collectDebugData();
this.send({ type: 'debug_data', data });
} catch (error) {
console.error('[ECS Debug] Failed to send debug data:', error);
}
}
}, interval);
}
/**
* 发送消息
*/
private send(message: any): void {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify(message));
}
}
/**
* 处理接收到的消息
* @param message 消息内容
*/
private handleMessage(message: any): void {
switch (message.type) {
case 'get_debug_data':
const data = this.collectDebugData();
this.send({ type: 'debug_data_response', data, requestId: message.requestId });
break;
case 'update_config':
if (message.config) {
this.updateConfig(message.config);
this.send({ type: 'config_updated', success: true });
}
break;
case 'ping':
this.send({ type: 'pong', timestamp: Date.now() });
break;
default:
console.warn('[ECS Debug] Unknown message type:', message.type);
}
}
/**
* 收集调试数据
* @returns 调试数据对象
*/
private collectDebugData(): IECSDebugData {
const currentTime = Date.now();
// 更新FPS计算
this.updateFPS(currentTime);
const data: IECSDebugData = {
timestamp: currentTime,
frameworkVersion: this.getFrameworkVersion(),
isRunning: true,
frameworkLoaded: true,
currentScene: this.getCurrentSceneName()
};
// 根据配置收集不同类型的数据
if (this.config.channels.entities) {
data.entities = this.collectEntityData();
}
if (this.config.channels.systems) {
data.systems = this.collectSystemData();
}
if (this.config.channels.performance) {
data.performance = this.collectPerformanceData();
}
if (this.config.channels.components) {
data.components = this.collectComponentData();
}
if (this.config.channels.scenes) {
data.scenes = this.collectSceneData();
}
return data;
}
/**
* 更新FPS计算
*/
private updateFPS(currentTime: number): void {
this.frameCount++;
if (currentTime - this.lastFpsTime >= 1000) {
this.fps = this.frameCount;
this.frameCount = 0;
this.lastFpsTime = currentTime;
}
}
/**
* 获取框架版本
*/
private getFrameworkVersion(): string {
return '1.0.0';
}
/**
* 获取当前场景名称
*/
private getCurrentSceneName(): string {
const scene = Core.scene;
return scene ? (scene as any).name || 'Unnamed Scene' : 'No Scene';
}
/**
* 收集实体数据
*/
private collectEntityData(): IEntityDebugData {
const scene = Core.scene;
if (!scene) {
return {
totalEntities: 0,
activeEntities: 0,
pendingAdd: 0,
pendingRemove: 0,
entitiesPerArchetype: [],
topEntitiesByComponents: []
};
}
const entityList = (scene as any).entities;
if (!entityList) {
return {
totalEntities: 0,
activeEntities: 0,
pendingAdd: 0,
pendingRemove: 0,
entitiesPerArchetype: [],
topEntitiesByComponents: []
};
}
const allEntities = entityList.buffer || [];
const activeEntities = allEntities.filter((e: any) => e.enabled && !e._isDestroyed);
return {
totalEntities: allEntities.length,
activeEntities: activeEntities.length,
pendingAdd: entityList.toAdd?.length || 0,
pendingRemove: entityList.toRemove?.length || 0,
entitiesPerArchetype: this.getArchetypeDistribution({ entities: allEntities }),
topEntitiesByComponents: this.getTopEntitiesByComponents({ entities: allEntities }),
entityDetails: this.getEntityDetails({ entities: allEntities })
};
}
/**
* 获取实体详情
*/
private getEntityDetails(entityContainer: any): Array<any> {
if (!entityContainer.entities) return [];
return entityContainer.entities.slice(0, 100).map((entity: any) => ({
id: entity.id,
name: entity.name || `Entity_${entity.id}`,
tag: entity.tag,
enabled: entity.enabled,
componentCount: entity.components?.length || 0,
components: entity.components?.map((c: any) => c.constructor.name) || []
}));
}
/**
* 收集系统数据
*/
private collectSystemData(): ISystemDebugData {
const scene = Core.scene;
if (!scene) {
return {
totalSystems: 0,
systemsInfo: []
};
}
const entityProcessors = (scene as any).entityProcessors;
if (!entityProcessors) {
return {
totalSystems: 0,
systemsInfo: []
};
}
const systems = entityProcessors.processors || [];
// 获取性能监控数据
const monitor = this.core._performanceMonitor;
let systemStats: Map<string, any> = new Map();
let systemData: Map<string, any> = new Map();
if (monitor) {
try {
systemStats = monitor.getAllSystemStats();
systemData = monitor.getAllSystemData();
} catch (error) {
// 忽略错误使用空的Map
}
}
return {
totalSystems: systems.length,
systemsInfo: systems.map((system: any) => {
const systemName = system.systemName || system.constructor.name;
const stats = systemStats.get(systemName);
const data = systemData.get(systemName);
return {
name: systemName,
type: system.constructor.name,
entityCount: system.entities?.length || 0,
executionTime: stats?.averageTime || data?.executionTime || 0,
minExecutionTime: stats?.minTime === Number.MAX_VALUE ? 0 : (stats?.minTime || 0),
maxExecutionTime: stats?.maxTime || 0,
executionTimeHistory: stats?.recentTimes || [],
updateOrder: system.updateOrder || 0,
enabled: system.enabled !== false,
lastUpdateTime: data?.lastUpdateTime || 0
};
})
};
}
/**
* 收集性能数据
*/
private collectPerformanceData(): IPerformanceDebugData {
const frameTimeSeconds = Time.deltaTime;
const engineFrameTimeMs = frameTimeSeconds * 1000;
const currentFps = frameTimeSeconds > 0 ? Math.round(1 / frameTimeSeconds) : 0;
const ecsPerformanceData = this.getECSPerformanceData();
const ecsExecutionTimeMs = ecsPerformanceData.totalExecutionTime;
const ecsPercentage = engineFrameTimeMs > 0 ? (ecsExecutionTimeMs / engineFrameTimeMs * 100) : 0;
let memoryUsage = 0;
if ((performance as any).memory) {
memoryUsage = (performance as any).memory.usedJSHeapSize / 1024 / 1024;
}
// 更新ECS执行时间历史记录
this.frameTimeHistory.push(ecsExecutionTimeMs);
if (this.frameTimeHistory.length > this.maxHistoryLength) {
this.frameTimeHistory.shift();
}
// 计算ECS执行时间统计
const history = this.frameTimeHistory.filter(t => t >= 0);
const averageECSTime = history.length > 0 ? history.reduce((a, b) => a + b, 0) / history.length : ecsExecutionTimeMs;
const minECSTime = history.length > 0 ? Math.min(...history) : ecsExecutionTimeMs;
const maxECSTime = history.length > 0 ? Math.max(...history) : ecsExecutionTimeMs;
return {
frameTime: ecsExecutionTimeMs,
engineFrameTime: engineFrameTimeMs,
ecsPercentage: ecsPercentage,
memoryUsage: memoryUsage,
fps: currentFps,
averageFrameTime: averageECSTime, // ECS平均执行时间
minFrameTime: minECSTime, // ECS最短执行时间
maxFrameTime: maxECSTime, // ECS最长执行时间
frameTimeHistory: [...this.frameTimeHistory],
systemPerformance: this.getSystemPerformance(),
memoryDetails: this.getMemoryDetails()
};
}
/**
* 获取ECS框架整体性能数据
*/
private getECSPerformanceData(): { totalExecutionTime: number; systemBreakdown: Array<any> } {
const monitor = this.core._performanceMonitor;
if (!monitor) {
return { totalExecutionTime: 0, systemBreakdown: [] };
}
try {
let totalTime = 0;
const systemBreakdown = [];
const stats = monitor.getAllSystemStats();
// 计算各系统的执行时间
for (const [systemName, stat] of stats.entries()) {
const systemTime = stat.averageTime || 0;
totalTime += systemTime;
systemBreakdown.push({
systemName: systemName,
executionTime: systemTime,
percentage: 0 // 后面计算
});
}
// 计算各系统占ECS总时间的百分比
systemBreakdown.forEach(system => {
system.percentage = totalTime > 0 ? (system.executionTime / totalTime * 100) : 0;
});
// 按执行时间排序
systemBreakdown.sort((a, b) => b.executionTime - a.executionTime);
return {
totalExecutionTime: totalTime,
systemBreakdown: systemBreakdown
};
} catch (error) {
return { totalExecutionTime: 0, systemBreakdown: [] };
}
}
/**
* 获取系统性能数据
*/
private getSystemPerformance(): Array<any> {
const monitor = this.core._performanceMonitor;
if (!monitor) {
return [];
}
try {
const stats = monitor.getAllSystemStats();
const systemData = monitor.getAllSystemData();
return Array.from(stats.entries()).map(([systemName, stat]) => {
const data = systemData.get(systemName);
return {
systemName: systemName,
averageTime: stat.averageTime,
maxTime: stat.maxTime,
minTime: stat.minTime === Number.MAX_VALUE ? 0 : stat.minTime,
samples: stat.executionCount,
percentage: 0, // 在getECSPerformanceData中计算
entityCount: data?.entityCount || 0,
lastExecutionTime: data?.executionTime || 0
};
});
} catch (error) {
return [];
}
}
/**
* 获取内存详情
*/
private getMemoryDetails(): any {
const scene = Core.scene;
if (!scene) {
return {
entities: 0,
components: 0,
systems: 0,
pooled: 0,
totalMemory: 0,
usedMemory: 0,
freeMemory: 0,
gcCollections: 0
};
}
try {
let entityMemory = 0;
let componentMemory = 0;
let systemMemory = 0;
let pooledMemory = 0;
const entityManager = (scene as any).entityManager;
if (entityManager?.entities) {
// 计算实体和组件内存
entityManager.entities.forEach((entity: any) => {
entityMemory += this.estimateObjectSize(entity);
if (entity.components) {
entity.components.forEach((component: any) => {
componentMemory += this.estimateObjectSize(component);
});
}
});
}
// 计算系统内存(估算)
const entitySystems = (scene as any).entitySystems;
if (entitySystems?.systems) {
entitySystems.systems.forEach((system: any) => {
systemMemory += this.estimateObjectSize(system);
});
}
// 计算对象池内存(估算)
try {
const poolManager = this.core._poolManager;
if (poolManager) {
// 简单估算对象池内存
pooledMemory = 1024 * 1024; // 1MB估算值
}
} catch (error) {
// 忽略对象池内存计算错误
}
// 获取浏览器内存信息
let totalMemory = 512 * 1024 * 1024; // 默认512MB
let usedMemory = entityMemory + componentMemory + systemMemory + pooledMemory;
let gcCollections = 0;
if ((performance as any).memory) {
const perfMemory = (performance as any).memory;
totalMemory = perfMemory.jsHeapSizeLimit || totalMemory;
usedMemory = perfMemory.usedJSHeapSize || usedMemory;
}
return {
entities: entityMemory,
components: componentMemory,
systems: systemMemory,
pooled: pooledMemory,
totalMemory: totalMemory,
usedMemory: usedMemory,
freeMemory: totalMemory - usedMemory,
gcCollections: gcCollections
};
} catch (error) {
return {
entities: 0,
components: 0,
systems: 0,
pooled: 0,
totalMemory: 512 * 1024 * 1024,
usedMemory: 0,
freeMemory: 512 * 1024 * 1024,
gcCollections: 0
};
}
}
/**
* 收集组件数据
*/
private collectComponentData(): IComponentDebugData {
const scene = Core.scene;
if (!scene) {
return {
componentTypes: 0,
componentInstances: 0,
componentStats: []
};
}
const entityList = (scene as any).entities;
if (!entityList?.buffer) {
return {
componentTypes: 0,
componentInstances: 0,
componentStats: []
};
}
const componentStats = new Map<string, { count: number; entities: number }>();
let totalInstances = 0;
entityList.buffer.forEach((entity: any) => {
if (entity.components) {
entity.components.forEach((component: any) => {
const typeName = component.constructor.name;
const stats = componentStats.get(typeName) || { count: 0, entities: 0 };
stats.count++;
totalInstances++;
componentStats.set(typeName, stats);
});
}
});
return {
componentTypes: componentStats.size,
componentInstances: totalInstances,
componentStats: Array.from(componentStats.entries()).map(([typeName, stats]) => {
const poolSize = this.getComponentPoolSize(typeName);
const memoryPerInstance = this.calculateComponentMemorySize(typeName);
return {
typeName,
instanceCount: stats.count,
memoryPerInstance: memoryPerInstance,
totalMemory: stats.count * memoryPerInstance,
poolSize: poolSize,
poolUtilization: poolSize > 0 ? (stats.count / poolSize * 100) : 0,
averagePerEntity: stats.count / entityList.buffer.length
};
})
};
}
/**
* 获取组件池大小
*/
private getComponentPoolSize(typeName: string): number {
try {
const poolManager = this.core._poolManager;
return (poolManager as any).getPoolSize?.(typeName) || 0;
} catch (error) {
return 0;
}
}
/**
* 计算组件实际内存大小
*/
private calculateComponentMemorySize(typeName: string): number {
const scene = Core.scene;
if (!scene) return 32;
const entityList = (scene as any).entities;
if (!entityList?.buffer) return 32;
try {
// 找到第一个包含此组件的实体,分析组件大小
for (const entity of entityList.buffer) {
if (entity.components) {
const component = entity.components.find((c: any) => c.constructor.name === typeName);
if (component) {
return this.estimateObjectSize(component);
}
}
}
} catch (error) {
// 忽略错误,使用默认值
}
// 如果无法计算,返回基础大小
return 32; // 基础对象开销
}
/**
* 估算对象内存大小(字节)
*/
private estimateObjectSize(obj: any, visited = new WeakSet(), depth = 0): number {
// 防止无限递归:限制深度和检测循环引用
if (obj === null || obj === undefined || depth > 10) return 0;
if (visited.has(obj)) return 0; // 已访问过,避免循环引用
let size = 0;
const type = typeof obj;
switch (type) {
case 'boolean':
size = 1;
break;
case 'number':
size = 8; // JavaScript中数字都是64位浮点数
break;
case 'string':
size = Math.min(obj.length * 2, 1024); // UTF-16编码每字符2字节限制最大1KB
break;
case 'object':
visited.add(obj); // 标记为已访问
if (Array.isArray(obj)) {
size = 24; // 数组基础开销
// 只处理前100个元素避免大数组导致性能问题
const maxItems = Math.min(obj.length, 100);
for (let i = 0; i < maxItems; i++) {
size += this.estimateObjectSize(obj[i], visited, depth + 1);
}
} else {
size = 24; // 对象基础开销
let propertyCount = 0;
for (const key in obj) {
// 只处理前50个属性避免复杂对象导致性能问题
if (propertyCount >= 50) break;
if (obj.hasOwnProperty(key)) {
// 跳过一些可能导致问题的属性
if (key === 'scene' || key === 'parent' || key === 'children' ||
key === '_scene' || key === '_parent' || key === '_children' ||
key === 'entity' || key === '_entity') {
continue;
}
try {
size += key.length * 2; // 属性名
size += this.estimateObjectSize(obj[key], visited, depth + 1); // 属性值
propertyCount++;
} catch (error) {
// 忽略访问错误的属性
continue;
}
}
}
}
break;
default:
size = 8; // 其他类型默认8字节
}
return Math.min(size, 10240); // 限制单个对象最大10KB
}
/**
* 收集场景数据
*/
private collectSceneData(): ISceneDebugData {
const scene = Core.scene;
if (!scene) {
return {
currentSceneName: 'No Scene',
isInitialized: false,
sceneRunTime: 0,
sceneEntityCount: 0,
sceneSystemCount: 0,
sceneMemory: 0,
sceneUptime: 0
};
}
const currentTime = Date.now();
const runTime = (currentTime - this.sceneStartTime) / 1000;
const entityList = (scene as any).entities;
const entityProcessors = (scene as any).entityProcessors;
return {
currentSceneName: (scene as any).name || 'Unnamed Scene',
isInitialized: (scene as any)._didSceneBegin || false,
sceneRunTime: runTime,
sceneEntityCount: entityList?.buffer?.length || 0,
sceneSystemCount: entityProcessors?.processors?.length || 0,
sceneMemory: 0, // TODO: 计算实际场景内存
sceneUptime: runTime
};
}
/**
* 手动触发数据收集
* @returns 当前调试数据
*/
public getDebugData(): IECSDebugData {
return this.collectDebugData();
}
/**
* 重置场景时间
*/
public onSceneChanged(): void {
this.sceneStartTime = Date.now();
// 发送场景切换事件
if (this.isConnected) {
this.send({
type: 'scene_changed',
data: {
sceneName: this.getCurrentSceneName(),
timestamp: Date.now()
}
});
}
}
/**
* 获取连接状态
*/
public get connected(): boolean {
return this.isConnected;
}
/**
* 手动重连
*/
public reconnect(): void {
if (this.ws) {
this.ws.close();
}
this.reconnectAttempts = 0;
this.connectWebSocket();
}
/**
* 获取Archetype分布
*/
private getArchetypeDistribution(entityContainer: any): Array<{ signature: string; count: number; memory: number }> {
if (!entityContainer.entities) return [];
const archetypes = new Map<string, { count: number; memory: number }>();
entityContainer.entities.forEach((entity: any) => {
const components = entity.components || [];
const signature = components.map((c: any) => c.constructor.name).sort().join(',') || 'Empty';
const existing = archetypes.get(signature) || { count: 0, memory: 0 };
existing.count++;
// 计算每个组件的实际内存大小
let entityMemory = 0;
components.forEach((component: any) => {
entityMemory += this.estimateObjectSize(component);
});
existing.memory += entityMemory;
archetypes.set(signature, existing);
});
return Array.from(archetypes.entries())
.map(([signature, data]) => ({ signature, ...data }))
.sort((a: any, b: any) => b.count - a.count)
.slice(0, 10); // 只返回前10个
}
/**
* 获取组件数量最多的实体
*/
private getTopEntitiesByComponents(entityContainer: any): Array<{ id: string; name: string; componentCount: number; memory: number }> {
if (!entityContainer.entities) return [];
return entityContainer.entities
.map((entity: any) => {
const components = entity.components || [];
let memory = 0;
// 计算实际内存使用
components.forEach((component: any) => {
memory += this.estimateObjectSize(component);
});
return {
id: entity.id?.toString() || 'unknown',
name: entity.name || `Entity_${entity.id}`,
componentCount: components.length,
memory: memory
};
})
.sort((a: any, b: any) => b.componentCount - a.componentCount)
.slice(0, 10); // 只返回前10个
}
}