328 lines
9.7 KiB
TypeScript
328 lines
9.7 KiB
TypeScript
import { Component } from '@esengine/ecs-framework';
|
||
import { Vec3 } from 'cc';
|
||
|
||
/**
|
||
* AI组件 - 复杂的AI行为和状态管理
|
||
*/
|
||
export class AIComponent extends Component {
|
||
/** AI状态 */
|
||
public currentState: 'idle' | 'patrol' | 'chase' | 'attack' | 'flee' | 'dead' = 'idle';
|
||
|
||
/** 目标ID(避免循环引用) */
|
||
public targetId: number | null = null;
|
||
|
||
/** AI伙伴ID列表(避免循环引用) */
|
||
public allyIds: number[] = [];
|
||
|
||
/** 敌人ID列表 */
|
||
public enemyIds: number[] = [];
|
||
|
||
/** 复杂的AI配置 */
|
||
public config: {
|
||
personality: {
|
||
aggression: number; // 攻击性 0-1
|
||
curiosity: number; // 好奇心 0-1
|
||
loyalty: number; // 忠诚度 0-1
|
||
intelligence: number; // 智力 0-1
|
||
};
|
||
capabilities: {
|
||
sightRange: number;
|
||
hearingRange: number;
|
||
movementSpeed: number;
|
||
attackDamage: number;
|
||
health: number;
|
||
};
|
||
behaviorTree: {
|
||
rootNode: BehaviorNode;
|
||
blackboard: Map<string, any>;
|
||
executionHistory: BehaviorExecution[];
|
||
};
|
||
memory: {
|
||
lastSeenEnemyPosition: Vec3 | null;
|
||
lastSeenEnemyTime: number;
|
||
knownLocations: Array<{
|
||
position: Vec3;
|
||
type: 'safe' | 'danger' | 'resource' | 'patrol';
|
||
confidence: number;
|
||
lastVisited: number;
|
||
}>;
|
||
relationships: Map<number, {
|
||
entityId: number;
|
||
relationship: 'ally' | 'enemy' | 'neutral';
|
||
trustLevel: number;
|
||
lastInteraction: number;
|
||
interactionHistory: Array<{
|
||
type: 'friendly' | 'hostile' | 'neutral';
|
||
timestamp: number;
|
||
impact: number;
|
||
}>;
|
||
}>;
|
||
};
|
||
};
|
||
|
||
/** 状态机 */
|
||
public stateMachine: {
|
||
states: Map<string, AIState>;
|
||
transitions: Map<string, Array<{
|
||
targetState: string;
|
||
condition: () => boolean;
|
||
priority: number;
|
||
}>>;
|
||
stateHistory: Array<{
|
||
state: string;
|
||
enterTime: number;
|
||
exitTime: number;
|
||
data: any;
|
||
}>;
|
||
};
|
||
|
||
/** 感知系统 */
|
||
public perception: {
|
||
visibleEntities: Array<{
|
||
entityId: number;
|
||
position: Vec3;
|
||
distance: number;
|
||
angle: number;
|
||
lastSeen: number;
|
||
componentId?: number; // 使用组件ID避免循环引用
|
||
}>;
|
||
audibleSounds: Array<{
|
||
source: Vec3;
|
||
volume: number;
|
||
type: string;
|
||
timestamp: number;
|
||
}>;
|
||
tacticleInfo: Array<{
|
||
entityId: number;
|
||
contactPoint: Vec3;
|
||
force: number;
|
||
timestamp: number;
|
||
}>;
|
||
};
|
||
|
||
constructor() {
|
||
super();
|
||
|
||
// 初始化AI配置
|
||
this.config = {
|
||
personality: {
|
||
aggression: Math.random(),
|
||
curiosity: Math.random(),
|
||
loyalty: Math.random(),
|
||
intelligence: Math.random()
|
||
},
|
||
capabilities: {
|
||
sightRange: 100 + Math.random() * 100,
|
||
hearingRange: 50 + Math.random() * 50,
|
||
movementSpeed: 80 + Math.random() * 40,
|
||
attackDamage: 10 + Math.random() * 20,
|
||
health: 80 + Math.random() * 40
|
||
},
|
||
behaviorTree: {
|
||
rootNode: new BehaviorNode('root'),
|
||
blackboard: new Map(),
|
||
executionHistory: []
|
||
},
|
||
memory: {
|
||
lastSeenEnemyPosition: null,
|
||
lastSeenEnemyTime: 0,
|
||
knownLocations: [],
|
||
relationships: new Map()
|
||
}
|
||
};
|
||
|
||
// 初始化状态机
|
||
this.stateMachine = {
|
||
states: new Map(),
|
||
transitions: new Map(),
|
||
stateHistory: []
|
||
};
|
||
|
||
// 初始化感知系统
|
||
this.perception = {
|
||
visibleEntities: [],
|
||
audibleSounds: [],
|
||
tacticleInfo: []
|
||
};
|
||
|
||
this.initializeStateMachine();
|
||
this.initializeBehaviorTree();
|
||
}
|
||
|
||
/**
|
||
* 初始化状态机
|
||
*/
|
||
private initializeStateMachine(): void {
|
||
// 添加基本状态
|
||
this.stateMachine.states.set('idle', new AIState('idle', this));
|
||
this.stateMachine.states.set('patrol', new AIState('patrol', this));
|
||
this.stateMachine.states.set('chase', new AIState('chase', this));
|
||
this.stateMachine.states.set('attack', new AIState('attack', this));
|
||
this.stateMachine.states.set('flee', new AIState('flee', this));
|
||
|
||
// 设置状态转换
|
||
this.stateMachine.transitions.set('idle', [
|
||
{ targetState: 'patrol', condition: () => Math.random() > 0.8, priority: 1 },
|
||
{ targetState: 'chase', condition: () => this.perception.visibleEntities.length > 0, priority: 3 }
|
||
]);
|
||
|
||
this.stateMachine.transitions.set('patrol', [
|
||
{ targetState: 'idle', condition: () => Math.random() > 0.9, priority: 1 },
|
||
{ targetState: 'chase', condition: () => this.hasVisibleEnemies(), priority: 3 }
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 初始化行为树
|
||
*/
|
||
private initializeBehaviorTree(): void {
|
||
const root = this.config.behaviorTree.rootNode;
|
||
|
||
// 构建简单的行为树结构
|
||
const selectorNode = new BehaviorNode('selector');
|
||
const sequenceNode = new BehaviorNode('sequence');
|
||
const conditionNode = new BehaviorNode('condition');
|
||
const actionNode = new BehaviorNode('action');
|
||
|
||
root.addChild(selectorNode);
|
||
selectorNode.addChild(sequenceNode);
|
||
sequenceNode.addChild(conditionNode);
|
||
sequenceNode.addChild(actionNode);
|
||
|
||
// 设置黑板数据
|
||
this.config.behaviorTree.blackboard.set('lastPatrolPoint', new Vec3());
|
||
this.config.behaviorTree.blackboard.set('alertLevel', 0);
|
||
this.config.behaviorTree.blackboard.set('energy', 100);
|
||
}
|
||
|
||
/**
|
||
* 添加盟友(避免循环引用)
|
||
*/
|
||
public addAlly(allyEntityId: number): void {
|
||
if (!this.allyIds.includes(allyEntityId)) {
|
||
this.allyIds.push(allyEntityId);
|
||
|
||
// 更新关系记录
|
||
this.config.memory.relationships.set(allyEntityId, {
|
||
entityId: allyEntityId,
|
||
relationship: 'ally',
|
||
trustLevel: 0.8,
|
||
lastInteraction: Date.now(),
|
||
interactionHistory: []
|
||
});
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 设置目标(避免循环引用)
|
||
*/
|
||
public setTarget(targetEntityId: number): void {
|
||
this.targetId = targetEntityId;
|
||
// 不再需要双向引用
|
||
}
|
||
|
||
/**
|
||
* 更新感知信息
|
||
*/
|
||
public updatePerception(deltaTime: number): void {
|
||
// 清理过期的感知信息
|
||
const currentTime = Date.now();
|
||
this.perception.visibleEntities = this.perception.visibleEntities.filter(
|
||
entity => currentTime - entity.lastSeen < 5000
|
||
);
|
||
|
||
this.perception.audibleSounds = this.perception.audibleSounds.filter(
|
||
sound => currentTime - sound.timestamp < 2000
|
||
);
|
||
|
||
// 更新记忆中的位置信息
|
||
this.config.memory.knownLocations.forEach(location => {
|
||
location.confidence *= 0.999; // 随时间衰减可信度
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 检查是否有可见敌人
|
||
*/
|
||
private hasVisibleEnemies(): boolean {
|
||
return this.perception.visibleEntities.some(entity =>
|
||
this.config.memory.relationships.get(entity.entityId)?.relationship === 'enemy'
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 重置组件
|
||
*/
|
||
public reset(): void {
|
||
// 清理ID数组(不再需要处理循环引用)
|
||
this.allyIds = [];
|
||
this.enemyIds = [];
|
||
this.targetId = null;
|
||
this.currentState = 'idle';
|
||
|
||
this.config.behaviorTree.blackboard.clear();
|
||
this.config.memory.relationships.clear();
|
||
this.perception.visibleEntities = [];
|
||
this.perception.audibleSounds = [];
|
||
this.perception.tacticleInfo = [];
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 行为树节点
|
||
*/
|
||
class BehaviorNode {
|
||
public name: string;
|
||
public children: BehaviorNode[] = [];
|
||
public parent: BehaviorNode | null = null;
|
||
public data: Map<string, any> = new Map();
|
||
|
||
constructor(name: string) {
|
||
this.name = name;
|
||
}
|
||
|
||
public addChild(child: BehaviorNode): void {
|
||
this.children.push(child);
|
||
child.parent = this;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 行为执行记录
|
||
*/
|
||
interface BehaviorExecution {
|
||
nodeName: string;
|
||
startTime: number;
|
||
endTime: number;
|
||
result: 'success' | 'failure' | 'running';
|
||
data: any;
|
||
}
|
||
|
||
/**
|
||
* AI状态
|
||
*/
|
||
class AIState {
|
||
public name: string;
|
||
public owner: AIComponent;
|
||
public enterTime: number = 0;
|
||
public data: Map<string, any> = new Map();
|
||
|
||
constructor(name: string, owner: AIComponent) {
|
||
this.name = name;
|
||
this.owner = owner;
|
||
}
|
||
|
||
public enter(): void {
|
||
this.enterTime = Date.now();
|
||
}
|
||
|
||
public exit(): void {
|
||
// 记录状态历史
|
||
this.owner.stateMachine.stateHistory.push({
|
||
state: this.name,
|
||
enterTime: this.enterTime,
|
||
exitTime: Date.now(),
|
||
data: Object.fromEntries(this.data)
|
||
});
|
||
}
|
||
}
|