Files
esengine/extensions/cocos/cocos-ecs/assets/scripts/components/UnitController.ts
2025-06-25 17:50:40 +08:00

353 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, MeshRenderer, Color, tween } from 'cc';
import { BehaviorTreeManager } from './BehaviorTreeManager';
import { RTSBehaviorHandler } from './RTSBehaviorHandler';
const { ccclass, property } = _decorator;
/**
* 单位配置接口
*/
export interface UnitConfig {
unitType: string;
behaviorTreeName: string;
maxHealth: number;
moveSpeed: number;
attackRange: number;
attackDamage: number;
color: string;
}
/**
* 单位控制器
*/
@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;
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';
// 体力系统属性
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;
private behaviorTreeManager: BehaviorTreeManager | null = null;
private behaviorHandler: Component | null = null;
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) {
console.warn('RTSBehaviorHandler组件添加失败', error);
}
}
/**
* 设置单位配置
*/
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}`;
// 初始化行为树
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);
}
}
/**
* 获取黑板变量值
*/
getBlackboardValue(key: string): any {
return this.behaviorTreeManager?.getBlackboardValue(key);
}
/**
* 设置移动目标
*/
setTarget(position: Vec3) {
this.targetPosition = position.clone();
this.isMoving = true;
this.moveStartTime = Date.now();
}
/**
* 清除移动目标
*/
clearTarget() {
this.targetPosition = Vec3.ZERO.clone();
this.isMoving = false;
}
/**
* 受到伤害
*/
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轴
*/
moveToTarget(targetPos: Vec3, speed?: number, deltaTime?: number): boolean {
const currentPos = this.node.worldPosition;
const distance = Vec3.distance(currentPos, targetPos);
if (distance < 0.5) {
this.isMoving = false;
return true;
}
const actualSpeed = speed || this.moveSpeed;
const actualDeltaTime = deltaTime || 0.016;
const direction = new Vec3();
Vec3.subtract(direction, targetPos, currentPos);
direction.normalize();
const moveDistance = actualSpeed * actualDeltaTime;
const newPosition = new Vec3();
Vec3.scaleAndAdd(newPosition, currentPos, direction, moveDistance);
this.node.setWorldPosition(newPosition);
this.isMoving = true;
return false;
}
/**
* 攻击目标
*/
attackTarget(): boolean {
const currentTime = Date.now();
if (currentTime - this.lastAttackTime < this.attackCooldown * 1000) {
return false;
}
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;
}
}
return false;
}
update(deltaTime: number) {
if (this.behaviorTreeManager) {
this.behaviorTreeManager.update(deltaTime);
}
if (this.isMoving && !this.targetPosition.equals(Vec3.ZERO)) {
const reached = this.moveToTarget(this.targetPosition, this.moveSpeed, deltaTime);
if (reached) {
this.clearTarget();
}
}
// 调试信息显示
if (this.showDebugInfo) {
this.updateDebugInfo();
}
}
/**
* 更新调试信息
*/
private updateDebugInfo() {
// 可以在这里添加调试信息的显示逻辑
// 比如在单位上方显示状态文本等
}
onDestroy() {
// 停止所有动画
tween(this.node).stop();
}
}