14 KiB
目标选择策略
**本文档引用的文件** - [SkillConComp.ts](file://assets/script/game/hero/SkillConComp.ts) - [BoxSet.ts](file://assets/script/game/common/config/BoxSet.ts) - [HeroViewComp.ts](file://assets/script/hero/HeroViewComp.ts) - [BattleMoveSystem.ts](file://assets/script/game/common/ecs/position/BattleMoveSystem.ts) - [heroSet.ts](file://assets/script/game/common/config/heroSet.ts) - [Hero.ts](file://assets/script/hero/Hero.ts) - [Mon.ts](file://assets/script/hero/Mon.ts)目录
概述
本文档深入分析了《英雄传说》游戏中目标选择算法的核心实现,重点关注selectTargets方法的策略设计。该算法采用智能的目标分配机制,确保战斗系统的公平性和策略性。
目标选择算法的核心特点:
- 优先级策略:第一个目标总是最前排的单位
- 随机性机制:后续目标通过随机选择确保多样性
- 重复允许:支持同一目标被多次选中
- 边界处理:当无可用目标时返回默认位置
- 阵营感知:基于FacSet判断左右方向
项目结构
graph TB
subgraph "技能系统"
SkillConComp["SkillConComp<br/>技能控制器"]
SkillEnt["SkillEnt<br/>技能实体"]
end
subgraph "阵营系统"
FacSet["FacSet<br/>阵营枚举"]
HeroViewComp["HeroViewComp<br/>英雄视图组件"]
end
subgraph "ECS系统"
ECS["ECS框架"]
BattleMoveSystem["BattleMoveSystem<br/>战斗移动系统"]
end
subgraph "目标检测"
check_target["check_target()<br/>目标检测"]
get_front["get_front()<br/>前排检测"]
selectTargets["selectTargets()<br/>目标选择"]
end
SkillConComp --> check_target
SkillConComp --> get_front
SkillConComp --> selectTargets
check_target --> ECS
get_front --> ECS
BattleMoveSystem --> ECS
图表来源
章节来源
核心组件
SkillConComp - 技能控制器
SkillConComp是目标选择算法的核心控制器,负责协调整个目标选择流程。该组件继承自CCComp,实现了ECS注册功能。
主要职责:
- 技能施放管理:控制技能的触发时机和目标选择
- 目标选择协调:调用
selectTargets方法获取目标坐标 - 异常处理:确保节点有效性检查和资源清理
FacSet - 阵营系统
FacSet定义了游戏中的阵营概念,是目标选择算法的重要输入参数。
阵营常量:
HERO = 0:英雄阵营,位于战场左侧MON = 1:怪物阵营,位于战场右侧
阵营决定了目标选择的方向性逻辑,英雄会选择右侧的怪物作为目标,而怪物会选择左侧的英雄作为目标。
章节来源
架构概览
sequenceDiagram
participant SkillCon as "SkillConComp"
participant ECS as "ECS查询系统"
participant BattleMove as "BattleMoveSystem"
participant HeroView as "HeroViewComp"
SkillCon->>SkillCon : check_target()
SkillCon->>ECS : ecs.query(allOf(MonModelComp))
ECS-->>SkillCon : 目标实体列表
alt 有可用目标
SkillCon->>SkillCon : get_front(entities)
SkillCon->>SkillCon : selectTargets(t_num)
SkillCon->>SkillCon : 返回目标坐标数组
else 无可用目标
SkillCon->>SkillCon : 返回默认位置
end
Note over SkillCon,BattleMove : 目标选择完成,准备施放技能
图表来源
详细组件分析
selectTargets 方法详解
selectTargets方法是目标选择算法的核心实现,采用了精心设计的策略来确保战斗的公平性和策略性。
算法流程
flowchart TD
Start([开始目标选择]) --> CheckEntities["检查可用实体"]
CheckEntities --> HasEntities{"是否有可用实体?"}
HasEntities --> |否| DefaultPos["返回默认位置"]
DefaultPos --> CloneDefault["克隆默认位置"]
CloneDefault --> ReturnDefault["返回t_num个默认位置"]
HasEntities --> |是| GetFront["获取最前排目标"]
GetFront --> PushFront["添加最前排坐标到targets"]
PushFront --> LoopTargets["循环添加后续目标"]
LoopTargets --> RandomSelect["随机选择实体"]
RandomSelect --> AddRandom["添加随机坐标到targets"]
AddRandom --> MoreTargets{"还有更多目标?"}
MoreTargets --> |是| LoopTargets
MoreTargets --> |否| ReturnTargets["返回目标坐标数组"]
ReturnDefault --> End([结束])
ReturnTargets --> End
图表来源
第一个目标:最前排单位
算法的第一个目标总是最前排的单位,这是基于以下设计原则:
- 战略优先级:最前排单位通常是敌方的主力或关键目标
- 视觉反馈:玩家能够直观看到技能的主要目标
- 战斗节奏:确保技能施放具有明确的战略意义
实现细节:
- 使用
get_front方法确定最前排位置 - 根据阵营方向选择最小或最大X坐标
- 确保第一个目标始终是最接近施法者的前排单位
后续目标:随机选择机制
从第二个目标开始,算法采用随机选择机制:
- 随机性保证:每个目标都有平等的选择机会
- 重复允许:同一个实体可能被多次选中
- 性能优化:避免复杂的排序和去重操作
实现原理:
- 使用
Math.random()生成随机索引 - 直接从实体列表中选择对应实体
- 确保算法复杂度为O(n),其中n为目标数量
默认位置处理
当没有可用目标时,算法返回预定义的默认位置:
const defaultPos = this.HeroView.fac === FacSet.HERO ? v3(400, 0, 0) : v3(-400, 0, 0);
设计意图:
- 战场定位:默认位置位于战场边缘,不影响战斗区域
- 视觉平衡:确保技能效果在视觉上仍然有意义
- 战术灵活性:为玩家提供策略选择的空间
章节来源
check_target 方法分析
check_target方法负责根据阵营确定目标实体集合:
classDiagram
class SkillConComp {
+HeroView : any
+check_target() Entity[]
+get_front(entities) Vec3
+selectTargets(t_num) Vec3[]
}
class FacSet {
<<enumeration>>
HERO = 0
MON = 1
}
class ECS {
+query(filter) Entity[]
+allOf(component) Filter
}
SkillConComp --> FacSet : "使用"
SkillConComp --> ECS : "查询"
note for SkillConComp "根据阵营决定查询英雄还是怪物"
note for FacSet "定义阵营常量"
note for ECS "提供实体查询功能"
图表来源
实现逻辑:
- 英雄阵营(FacSet.HERO)查询怪物实体(MonModelComp)
- 怪物阵营(FacSet.MON)查询英雄实体(HeroModelComp)
这种设计确保了目标选择的阵营对立性,符合游戏的战斗机制。
章节来源
get_front 方法详解
get_front方法实现了前排单位的智能识别:
X坐标比较逻辑
flowchart LR
subgraph "英雄阵营 (FacSet.HERO)"
MinX["Math.min(...entities.map(e => e.get(HeroViewComp).node.position.x))"]
MinX --> Leftmost["找到最左侧的X坐标"]
end
subgraph "怪物阵营 (FacSet.MON)"
MaxX["Math.max(...entities.map(e => e.get(HeroViewComp).node.position.x))"]
MaxX --> Rightmost["找到最右侧的X坐标"]
end
Leftmost --> KeyEntity["查找对应实体"]
Rightmost --> KeyEntity
KeyEntity --> FrontPos["返回前排坐标"]
图表来源
方向性判断
算法根据阵营自动判断左右方向:
- 英雄阵营:寻找最小X坐标,即最左侧的单位
- 怪物阵营:寻找最大X坐标,即最右侧的单位
这种设计符合战场布局的直觉认知,英雄在左侧,怪物在右侧。
实体匹配过程
let keyPos = this.HeroView.fac == FacSet.HERO ?
Math.min(...entities.map(e => e.get(HeroViewComp).node.position.x)) :
Math.max(...entities.map(e => e.get(HeroViewComp).node.position.x));
let keyEntity = entities.find(e => e.get(HeroViewComp).node.position.x === keyPos);
return keyEntity.get(HeroViewComp).node.position;
这个过程确保了:
- 正确的前排单位被识别
- 坐标信息准确返回
- 异常情况下的稳定性
章节来源
ECS查询与坐标比较整合
目标选择算法与ECS系统的深度集成体现在以下几个方面:
查询优化
graph TD
A[ECS查询] --> B[allOf过滤]
B --> C[实体列表]
C --> D[坐标提取]
D --> E[X坐标数组]
E --> F[Math.min/max计算]
F --> G[前排坐标]
G --> H[实体匹配]
H --> I[最终目标]
style A fill:#e1f5fe
style F fill:#f3e5f5
style I fill:#e8f5e8
图表来源
性能考量
- 批量查询:一次性获取所有符合条件的实体
- 延迟计算:只在需要时进行坐标比较
- 内存优化:避免不必要的对象创建
异常处理
算法包含了多层异常保护:
- 空实体检查:防止空列表访问
- 坐标有效性验证:确保坐标数据正确
- 实体状态检查:排除无效或死亡的实体
章节来源
依赖关系分析
graph LR
subgraph "外部依赖"
ECS["ECS框架"]
Vec3["Vec3类"]
FacSet["FacSet枚举"]
end
subgraph "内部组件"
SkillConComp["SkillConComp"]
HeroViewComp["HeroViewComp"]
BattleMoveSystem["BattleMoveSystem"]
end
subgraph "配置文件"
BoxSet["BoxSet.ts"]
heroSet["heroSet.ts"]
end
SkillConComp --> ECS
SkillConComp --> Vec3
SkillConComp --> FacSet
SkillConComp --> HeroViewComp
BattleMoveSystem --> ECS
BattleMoveSystem --> HeroViewComp
BoxSet --> FacSet
heroSet --> HeroViewComp
style SkillConComp fill:#ffeb3b
style ECS fill:#2196f3
style FacSet fill:#4caf50
图表来源
核心依赖关系
- ECS框架依赖:
ecs.query()用于实体查询 - 数学运算依赖:
Math.min()和Math.max()用于坐标比较 - 类型系统依赖:
Vec3和Entity类型定义 - 配置系统依赖:
FacSet提供阵营常量
松耦合设计
算法采用松耦合设计,便于维护和扩展:
- 接口隔离:通过ECS查询接口与具体实体解耦
- 配置驱动:阵营逻辑通过配置文件管理
- 模块化组织:各功能模块职责单一
章节来源
性能考虑
时间复杂度分析
目标选择算法的时间复杂度为O(n),其中n为目标数量:
- ECS查询:O(m),m为实体总数
- 坐标提取:O(m)
- 前排计算:O(m)
- 目标生成:O(n)
空间复杂度分析
空间复杂度为O(m+n),主要用于存储实体列表和目标坐标:
- 实体缓存:存储查询结果
- 目标数组:存储最终目标坐标
- 临时变量:坐标计算过程中的中间值
性能优化策略
- 查询复用:一次查询结果供多个目标选择使用
- 延迟计算:只在需要时进行坐标比较
- 内存池化:重用Vec3对象减少GC压力
故障排除指南
常见异常情况
空实体列表
问题描述:当没有可用目标时,算法返回默认位置。
解决方案:
- 检查ECS查询条件是否正确
- 验证实体是否正确添加到场景中
- 确认实体组件是否完整
坐标计算错误
问题描述:前排单位识别不准确。
解决方案:
- 验证
HeroViewComp组件是否正确挂载 - 检查实体位置是否正确初始化
- 确认坐标系统的一致性
随机选择偏差
问题描述:随机选择结果不均匀。
解决方案:
- 检查
Math.random()的分布特性 - 验证实体列表的完整性
- 确认索引计算的正确性
调试技巧
- 日志记录:在关键步骤添加调试信息
- 可视化:绘制目标选择路径
- 单元测试:为每个方法编写测试用例
章节来源
结论
目标选择算法展现了优秀的工程设计原则:
设计优势
- 清晰的职责分离:每个方法专注于特定功能
- 灵活的扩展性:易于适应新的需求变化
- 稳定的性能表现:O(n)时间复杂度确保高效运行
- 健壮的异常处理:多层次保护机制
战术价值
算法的设计充分考虑了战斗策略的需求:
- 优先级策略:确保关键目标优先被选中
- 随机性机制:增加战斗的不确定性和趣味性
- 边界处理:优雅处理异常情况
- 阵营感知:符合游戏世界的逻辑设定
未来改进方向
- 智能预测:基于历史数据预测目标偏好
- 动态权重:根据战斗状态调整目标选择权重
- 机器学习:利用AI技术优化目标选择策略
- 实时反馈:提供更直观的目标选择反馈
该算法为游戏战斗系统提供了坚实的基础,其设计理念和实现方式值得在类似项目中借鉴和应用。