新增cocos-debug-profiler
This commit is contained in:
99
extensions/cocos/cocos-ecs/assets/scripts/ecs/ECSManager.ts
Normal file
99
extensions/cocos/cocos-ecs/assets/scripts/ecs/ECSManager.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "c82e7909-01e6-49ca-b68d-9ee98837e238",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
153
extensions/cocos/cocos-ecs/assets/scripts/ecs/README.md
Normal file
153
extensions/cocos/cocos-ecs/assets/scripts/ecs/README.md
Normal 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。
|
||||
11
extensions/cocos/cocos-ecs/assets/scripts/ecs/README.md.meta
Normal file
11
extensions/cocos/cocos-ecs/assets/scripts/ecs/README.md.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"ver": "1.0.1",
|
||||
"importer": "text",
|
||||
"imported": true,
|
||||
"uuid": "0932496e-f7fe-4cb9-86e2-ebd7d2a3d047",
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.2.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "3d8cbc91-5bc5-4d17-b53a-01fda26e4660",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "0f20d48a-7b30-4081-a9de-709432b6737b",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "ab10dc4c-c8a3-4fd2-83d6-433d4195966b",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "e6ee57d6-d0eb-43f2-a601-9b7a2812de66",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -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-1,1为无阻尼) */
|
||||
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; // 避免浮点数精度问题
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "784d7c28-2b72-427c-8b04-da0fcf775acf",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.2.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "6a2d6231-acf9-47b8-a020-d45a7433a95d",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.2.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "879b4e07-dd6b-4445-adb2-a970b97c6d6f",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -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("✅ 场景清理完成");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "da87facc-89a0-47da-a0ef-423255200a51",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "8fee85be-2224-4200-a898-d3ae2406fb1d",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.2.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "a5e3a8c9-3d0b-4a36-9d20-6f70f1380131",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -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';
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "455c12d1-52a8-41ac-b1b5-0d2b93c079aa",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "a8712467-efe0-46ec-a246-a9fa07d203d9",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -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 已清理");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "7b69a39f-926a-4260-94ba-e15e31b324b5",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
Reference in New Issue
Block a user