Files
esengine/extensions/cocos/cocos-ecs/assets/scripts/components/UnitController.ts

353 lines
10 KiB
TypeScript
Raw Normal View History

2025-06-25 17:50:40 +08:00
import { _decorator, Component, Node, Vec3, MeshRenderer, Color, tween } from 'cc';
2025-06-24 19:34:37 +08:00
import { BehaviorTreeManager } from './BehaviorTreeManager';
import { RTSBehaviorHandler } from './RTSBehaviorHandler';
2025-06-24 19:34:37 +08:00
const { ccclass, property } = _decorator;
/**
*
*/
export interface UnitConfig {
unitType: string;
behaviorTreeName: string;
maxHealth: number;
moveSpeed: number;
attackRange: number;
attackDamage: number;
color: string;
}
/**
2025-06-25 17:50:40 +08:00
*
2025-06-24 19:34:37 +08:00
*/
@ccclass('UnitController')
export class UnitController extends Component {
@property
showDebugInfo: boolean = true;
// 单位属性
public unitType: string = '';
public maxHealth: number = 100;
public currentHealth: number = 100;
public moveSpeed: number = 1.5;
2025-06-24 19:34:37 +08:00
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';
2025-06-25 17:50:40 +08:00
// 体力系统属性
public maxStamina: number = 100;
public currentStamina: number = 100;
public homePosition: Vec3 = Vec3.ZERO.clone();
public staminaRecoveryRate: number = 20; // 每秒恢复的体力
public staminaCostPerMining: number = 15; // 每次挖矿消耗的体力
// 移动状态管理
private isMoving: boolean = false;
private moveStartTime: number = 0;
private lastTargetUpdateTime: number = 0;
2025-06-24 19:34:37 +08:00
private behaviorTreeManager: BehaviorTreeManager | null = null;
private behaviorHandler: Component | null = null;
2025-06-24 19:34:37 +08:00
private meshRenderer: MeshRenderer | null = null;
onLoad() {
this.meshRenderer = this.getComponent(MeshRenderer);
// 创建行为树管理器
this.behaviorTreeManager = this.addComponent(BehaviorTreeManager);
// 添加RTS行为处理器
try {
// 添加RTSBehaviorHandler组件
this.behaviorHandler = this.addComponent(RTSBehaviorHandler);
} catch (error) {
2025-06-25 17:50:40 +08:00
console.warn('RTSBehaviorHandler组件添加失败', error);
}
2025-06-24 19:34:37 +08:00
}
/**
*
*/
setup(config: UnitConfig) {
this.unitType = config.unitType;
this.maxHealth = config.maxHealth;
this.currentHealth = config.maxHealth;
this.moveSpeed = config.moveSpeed;
this.attackRange = config.attackRange;
this.attackDamage = config.attackDamage;
this.color = config.color;
// 设置材质颜色
this.setUnitColor(config.color);
// 设置节点名称显示单位类型
this.node.name = `${config.unitType.toUpperCase()}_${this.node.name}`;
2025-06-24 19:34:37 +08:00
// 初始化行为树
if (this.behaviorTreeManager) {
this.behaviorTreeManager.initializeBehaviorTree(config.behaviorTreeName, this);
}
}
/**
*
*/
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) {
this.isSelected = selected;
// 视觉效果
if (selected) {
this.showSelectionEffect();
} else {
this.hideSelectionEffect();
}
// 更新行为树黑板
if (this.behaviorTreeManager) {
this.behaviorTreeManager.updateBlackboardValue('isSelected', selected);
}
}
/**
*
*/
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) {
this.currentCommand = command;
// 设置目标
if (target instanceof Vec3) {
this.targetPosition = target.clone();
this.targetNode = null;
} else if (target instanceof Node) {
this.targetPosition = target.worldPosition.clone();
this.targetNode = target;
}
// 更新行为树黑板
if (this.behaviorTreeManager) {
this.behaviorTreeManager.updateBlackboardValue('currentCommand', command);
this.behaviorTreeManager.updateBlackboardValue('hasTarget', target !== undefined);
this.behaviorTreeManager.updateBlackboardValue('targetPosition', this.targetPosition);
if (target instanceof Node) {
this.behaviorTreeManager.updateBlackboardValue('targetType',
target.name.includes('Resource') ? 'resource' :
target.name.includes('Building') ? 'building' : 'unit');
}
}
}
/**
*
*/
setBlackboardValue(key: string, value: any) {
if (this.behaviorTreeManager) {
this.behaviorTreeManager.updateBlackboardValue(key, value);
}
}
2025-06-25 17:50:40 +08:00
/**
*
*/
getBlackboardValue(key: string): any {
return this.behaviorTreeManager?.getBlackboardValue(key);
}
/**
*
*/
setTarget(position: Vec3) {
this.targetPosition = position.clone();
2025-06-25 17:50:40 +08:00
this.isMoving = true;
this.moveStartTime = Date.now();
}
/**
*
*/
clearTarget() {
this.targetPosition = Vec3.ZERO.clone();
2025-06-25 17:50:40 +08:00
this.isMoving = false;
}
2025-06-24 19:34:37 +08:00
/**
*
*/
takeDamage(damage: number) {
this.currentHealth = Math.max(0, this.currentHealth - damage);
// 更新行为树黑板
if (this.behaviorTreeManager) {
this.behaviorTreeManager.updateBlackboardValue('currentHealth', this.currentHealth);
this.behaviorTreeManager.updateBlackboardValue('healthPercentage', this.currentHealth / this.maxHealth);
this.behaviorTreeManager.updateBlackboardValue('isLowHealth', this.currentHealth < this.maxHealth * 0.3);
}
// 视觉效果
this.showDamageEffect();
if (this.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} 死亡`);
// 播放死亡动画后销毁节点
tween(this.node)
.to(0.5, { scale: Vec3.ZERO })
.call(() => {
this.node.destroy();
})
.start();
}
/**
* Y轴
2025-06-24 19:34:37 +08:00
*/
moveToTarget(targetPos: Vec3, speed?: number, deltaTime?: number): boolean {
2025-06-24 19:34:37 +08:00
const currentPos = this.node.worldPosition;
2025-06-25 17:50:40 +08:00
const distance = Vec3.distance(currentPos, targetPos);
2025-06-24 19:34:37 +08:00
2025-06-25 17:50:40 +08:00
if (distance < 0.5) {
this.isMoving = false;
2025-06-25 17:50:40 +08:00
return true;
2025-06-24 19:34:37 +08:00
}
2025-06-25 17:50:40 +08:00
const actualSpeed = speed || this.moveSpeed;
const actualDeltaTime = deltaTime || 0.016;
const direction = new Vec3();
Vec3.subtract(direction, targetPos, currentPos);
direction.normalize();
2025-06-25 17:50:40 +08:00
const moveDistance = actualSpeed * actualDeltaTime;
const newPosition = new Vec3();
Vec3.scaleAndAdd(newPosition, currentPos, direction, moveDistance);
2025-06-24 19:34:37 +08:00
this.node.setWorldPosition(newPosition);
this.isMoving = true;
2025-06-25 17:50:40 +08:00
return false;
2025-06-24 19:34:37 +08:00
}
/**
*
*/
attackTarget(): boolean {
2025-06-25 17:50:40 +08:00
const currentTime = Date.now();
if (currentTime - this.lastAttackTime < this.attackCooldown * 1000) {
return false;
2025-06-24 19:34:37 +08:00
}
2025-06-25 17:50:40 +08:00
if (this.targetNode && this.targetNode.isValid) {
const distance = Vec3.distance(this.node.worldPosition, this.targetNode.worldPosition);
if (distance <= this.attackRange) {
this.lastAttackTime = currentTime;
return true;
}
2025-06-24 19:34:37 +08:00
}
2025-06-25 17:50:40 +08:00
return false;
2025-06-24 19:34:37 +08:00
}
update(deltaTime: number) {
2025-06-25 17:50:40 +08:00
if (this.behaviorTreeManager) {
this.behaviorTreeManager.update(deltaTime);
}
2025-06-25 17:50:40 +08:00
if (this.isMoving && !this.targetPosition.equals(Vec3.ZERO)) {
const reached = this.moveToTarget(this.targetPosition, this.moveSpeed, deltaTime);
if (reached) {
this.clearTarget();
2025-06-24 19:34:37 +08:00
}
}
// 调试信息显示
if (this.showDebugInfo) {
this.updateDebugInfo();
}
}
/**
*
*/
private updateDebugInfo() {
// 可以在这里添加调试信息的显示逻辑
// 比如在单位上方显示状态文本等
}
onDestroy() {
// 停止所有动画
tween(this.node).stop();
}
}