rts-demo
This commit is contained in:
@@ -1,228 +0,0 @@
|
||||
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';
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "455c12d1-52a8-41ac-b1b5-0d2b93c079aa",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
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
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "a8712467-efe0-46ec-a246-a9fa07d203d9",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
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 已清理");
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "7b69a39f-926a-4260-94ba-e15e31b324b5",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
Reference in New Issue
Block a user