Files
esengine/extensions/cocos/cocos-ecs/assets/scripts/components/UnitComponent.ts
2025-06-24 19:34:37 +08:00

345 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { _decorator, Component, Node, Vec3, Material, MeshRenderer, Color, tween } from 'cc';
import { BehaviorTreeComponent } from './BehaviorTreeComponent';
// 简化的ECS组件基类
export class ECSComponent {
public entity: any = null;
}
// 简化的Entity类
export class Entity {
public name: string = '';
private components: Map<any, any> = new Map();
constructor(name: string) {
this.name = name;
}
addComponent(component: any) {
this.components.set(component.constructor, component);
component.entity = this;
}
getComponent<T>(componentClass: any): T | null {
return this.components.get(componentClass) || null;
}
hasComponent(componentClass: any): boolean {
return this.components.has(componentClass);
}
}
// 简化的Core类
export class Core {
static entityManager = {
entities: [] as Entity[],
createEntity: (name: string) => {
const entity = new Entity(name);
Core.entityManager.entities.push(entity);
return entity;
},
destroyEntity: (entity: Entity) => {
const index = Core.entityManager.entities.indexOf(entity);
if (index !== -1) {
Core.entityManager.entities.splice(index, 1);
}
}
};
static create(config?: any) {
console.log('ECS Core initialized with config:', config);
}
static update(deltaTime: number) {
// 简化的更新逻辑
}
}
const { ccclass, property } = _decorator;
/**
* 单位配置接口
*/
export interface UnitConfig {
unitType: string;
behaviorTreeFile: string;
maxHealth: number;
moveSpeed: number;
attackRange: number;
attackDamage: number;
color: string;
}
/**
* 单位状态组件
*/
export class UnitStateComponent extends ECSComponent {
public unitType: string = '';
public maxHealth: number = 100;
public currentHealth: number = 100;
public moveSpeed: number = 3;
public attackRange: number = 2;
public attackDamage: number = 25;
public isSelected: boolean = false;
public currentCommand: string = 'idle';
public targetPosition: Vec3 = Vec3.ZERO.clone();
public targetNode: Node | null = null;
public lastAttackTime: number = 0;
public attackCooldown: number = 1.5;
public color: string = 'white';
}
/**
* 单位组件 - Cocos Creator组件管理单位的可视化和ECS实体
*/
@ccclass('UnitComponent')
export class UnitComponent extends Component {
@property
showDebugInfo: boolean = true;
private entity: Entity | null = null;
private unitState: UnitStateComponent | null = null;
private behaviorTreeComponent: BehaviorTreeComponent | null = null;
private meshRenderer: MeshRenderer | null = null;
private originalMaterial: Material | null = null;
private selectionMaterial: Material | null = null;
onLoad() {
this.meshRenderer = this.getComponent(MeshRenderer);
if (this.meshRenderer && this.meshRenderer.material) {
this.originalMaterial = this.meshRenderer.material;
}
}
/**
* 设置单位配置
*/
setup(config: UnitConfig) {
// 创建ECS实体
this.entity = Core.entityManager.createEntity(`Unit_${this.node.name}`);
// 添加单位状态组件
this.unitState = new UnitStateComponent();
this.unitState.unitType = config.unitType;
this.unitState.maxHealth = config.maxHealth;
this.unitState.currentHealth = config.maxHealth;
this.unitState.moveSpeed = config.moveSpeed;
this.unitState.attackRange = config.attackRange;
this.unitState.attackDamage = config.attackDamage;
this.unitState.color = config.color;
this.entity.addComponent(this.unitState);
// 添加行为树组件
this.behaviorTreeComponent = new BehaviorTreeComponent();
this.behaviorTreeComponent.behaviorTreeFile = config.behaviorTreeFile;
this.behaviorTreeComponent.cocosNode = this.node;
this.entity.addComponent(this.behaviorTreeComponent);
// 设置材质颜色
this.setUnitColor(config.color);
console.log(`单位 ${this.node.name} 设置完成 - 类型: ${config.unitType}, 行为树: ${config.behaviorTreeFile}`);
}
/**
* 设置单位颜色
*/
private setUnitColor(colorName: string) {
if (!this.meshRenderer || !this.meshRenderer.material) return;
const colorMap: { [key: string]: Color } = {
'red': Color.RED,
'green': Color.GREEN,
'blue': Color.BLUE,
'yellow': Color.YELLOW,
'white': Color.WHITE,
'cyan': Color.CYAN,
'magenta': Color.MAGENTA
};
const color = colorMap[colorName] || Color.WHITE;
this.meshRenderer.material.setProperty('mainColor', color);
}
/**
* 设置选择状态
*/
setSelected(selected: boolean) {
if (!this.unitState) return;
this.unitState.isSelected = selected;
// 视觉效果
if (selected) {
this.showSelectionEffect();
} else {
this.hideSelectionEffect();
}
}
/**
* 显示选择效果
*/
private showSelectionEffect() {
// 添加选择圈效果
tween(this.node)
.to(0.3, { scale: new Vec3(1.1, 1.1, 1.1) })
.to(0.3, { scale: Vec3.ONE })
.union()
.repeatForever()
.start();
}
/**
* 隐藏选择效果
*/
private hideSelectionEffect() {
// 停止所有缩放动画
tween(this.node).stop();
this.node.setScale(Vec3.ONE);
}
/**
* 发布命令
*/
issueCommand(command: string, target?: Vec3 | Node) {
if (!this.unitState || !this.behaviorTreeComponent) return;
this.unitState.currentCommand = command;
// 设置目标
if (target instanceof Vec3) {
this.unitState.targetPosition = target.clone();
this.unitState.targetNode = null;
} else if (target instanceof Node) {
this.unitState.targetPosition = target.worldPosition.clone();
this.unitState.targetNode = target;
}
// 通过黑板更新行为树状态
const blackboard = this.behaviorTreeComponent.getBlackboard();
if (blackboard) {
blackboard.setValue('currentCommand', command);
blackboard.setValue('hasTarget', target !== undefined);
blackboard.setValue('targetPosition', this.unitState.targetPosition);
if (target instanceof Node) {
blackboard.setValue('targetType', target.name.includes('Resource') ? 'resource' :
target.name.includes('Building') ? 'building' : 'unit');
}
}
console.log(`单位 ${this.node.name} 接收命令: ${command}`, target);
}
/**
* 获取单位状态
*/
getUnitState(): UnitStateComponent | null {
return this.unitState;
}
/**
* 获取行为树组件
*/
getBehaviorTreeComponent(): BehaviorTreeComponent | null {
return this.behaviorTreeComponent;
}
/**
* 受到伤害
*/
takeDamage(damage: number) {
if (!this.unitState) return;
this.unitState.currentHealth = Math.max(0, this.unitState.currentHealth - damage);
// 更新黑板
const blackboard = this.behaviorTreeComponent?.getBlackboard();
if (blackboard) {
blackboard.setValue('currentHealth', this.unitState.currentHealth);
blackboard.setValue('healthPercentage', this.unitState.currentHealth / this.unitState.maxHealth);
blackboard.setValue('isLowHealth', this.unitState.currentHealth < this.unitState.maxHealth * 0.3);
}
// 视觉效果
this.showDamageEffect();
if (this.unitState.currentHealth <= 0) {
this.die();
}
}
/**
* 显示受伤效果
*/
private showDamageEffect() {
if (!this.meshRenderer || !this.meshRenderer.material) return;
// 闪红效果
const originalColor = this.meshRenderer.material.getProperty('mainColor') as Color;
this.meshRenderer.material.setProperty('mainColor', Color.RED);
this.scheduleOnce(() => {
if (this.meshRenderer && this.meshRenderer.material) {
this.meshRenderer.material.setProperty('mainColor', originalColor);
}
}, 0.2);
}
/**
* 单位死亡
*/
private die() {
console.log(`单位 ${this.node.name} 死亡`);
// 从ECS系统中移除实体
if (this.entity) {
Core.entityManager.destroyEntity(this.entity);
}
// 播放死亡动画后销毁节点
tween(this.node)
.to(0.5, { scale: Vec3.ZERO })
.call(() => {
this.node.destroy();
})
.start();
}
update(deltaTime: number) {
if (!this.unitState || !this.behaviorTreeComponent) return;
// 更新黑板中的时间相关变量
const blackboard = this.behaviorTreeComponent.getBlackboard();
if (blackboard) {
blackboard.setValue('deltaTime', deltaTime);
blackboard.setValue('currentTime', Date.now() / 1000);
blackboard.setValue('worldPosition', this.node.worldPosition);
}
// 调试信息显示
if (this.showDebugInfo) {
this.updateDebugInfo();
}
}
/**
* 更新调试信息
*/
private updateDebugInfo() {
// 可以在这里添加调试信息的显示逻辑
// 比如在单位上方显示状态文本等
}
onDestroy() {
// 清理ECS实体
if (this.entity) {
Core.entityManager.destroyEntity(this.entity);
}
// 停止所有动画
tween(this.node).stop();
}
}