Files
pixelheros/.qoder/repowiki/zh/content/英雄系统/技能系统/技能执行机制/目标选择策略.md
panw 4235e3b776 refactor(game): 移除已弃用的事件常量
- 删除 UpdateHero 和 UpdateFightHero 事件
- 移除 MISSION_UPDATE 事件常量
- 优化游戏事件枚举定义
2025-10-28 16:15:47 +08:00

14 KiB
Raw Blame History

目标选择策略

**本文档引用的文件** - [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)

目录

  1. 概述
  2. 项目结构
  3. 核心组件
  4. 架构概览
  5. 详细组件分析
  6. 依赖关系分析
  7. 性能考虑
  8. 故障排除指南
  9. 结论

概述

本文档深入分析了《英雄传说》游戏中目标选择算法的核心实现,重点关注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

图表来源

第一个目标:最前排单位

算法的第一个目标总是最前排的单位,这是基于以下设计原则:

  1. 战略优先级:最前排单位通常是敌方的主力或关键目标
  2. 视觉反馈:玩家能够直观看到技能的主要目标
  3. 战斗节奏:确保技能施放具有明确的战略意义

实现细节

  • 使用get_front方法确定最前排位置
  • 根据阵营方向选择最小或最大X坐标
  • 确保第一个目标始终是最接近施法者的前排单位

后续目标:随机选择机制

从第二个目标开始,算法采用随机选择机制:

  1. 随机性保证:每个目标都有平等的选择机会
  2. 重复允许:同一个实体可能被多次选中
  3. 性能优化:避免复杂的排序和去重操作

实现原理

  • 使用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["返回前排坐标"]

图表来源

方向性判断

算法根据阵营自动判断左右方向:

  1. 英雄阵营寻找最小X坐标即最左侧的单位
  2. 怪物阵营寻找最大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

图表来源

性能考量

  1. 批量查询:一次性获取所有符合条件的实体
  2. 延迟计算:只在需要时进行坐标比较
  3. 内存优化:避免不必要的对象创建

异常处理

算法包含了多层异常保护:

  1. 空实体检查:防止空列表访问
  2. 坐标有效性验证:确保坐标数据正确
  3. 实体状态检查:排除无效或死亡的实体

章节来源

依赖关系分析

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

图表来源

核心依赖关系

  1. ECS框架依赖ecs.query()用于实体查询
  2. 数学运算依赖Math.min()Math.max()用于坐标比较
  3. 类型系统依赖Vec3Entity类型定义
  4. 配置系统依赖FacSet提供阵营常量

松耦合设计

算法采用松耦合设计,便于维护和扩展:

  • 接口隔离通过ECS查询接口与具体实体解耦
  • 配置驱动:阵营逻辑通过配置文件管理
  • 模块化组织:各功能模块职责单一

章节来源

性能考虑

时间复杂度分析

目标选择算法的时间复杂度为O(n)其中n为目标数量

  1. ECS查询O(m)m为实体总数
  2. 坐标提取O(m)
  3. 前排计算O(m)
  4. 目标生成O(n)

空间复杂度分析

空间复杂度为O(m+n),主要用于存储实体列表和目标坐标:

  1. 实体缓存:存储查询结果
  2. 目标数组:存储最终目标坐标
  3. 临时变量:坐标计算过程中的中间值

性能优化策略

  1. 查询复用:一次查询结果供多个目标选择使用
  2. 延迟计算:只在需要时进行坐标比较
  3. 内存池化重用Vec3对象减少GC压力

故障排除指南

常见异常情况

空实体列表

问题描述:当没有可用目标时,算法返回默认位置。

解决方案

  • 检查ECS查询条件是否正确
  • 验证实体是否正确添加到场景中
  • 确认实体组件是否完整

坐标计算错误

问题描述:前排单位识别不准确。

解决方案

  • 验证HeroViewComp组件是否正确挂载
  • 检查实体位置是否正确初始化
  • 确认坐标系统的一致性

随机选择偏差

问题描述:随机选择结果不均匀。

解决方案

  • 检查Math.random()的分布特性
  • 验证实体列表的完整性
  • 确认索引计算的正确性

调试技巧

  1. 日志记录:在关键步骤添加调试信息
  2. 可视化:绘制目标选择路径
  3. 单元测试:为每个方法编写测试用例

章节来源

结论

目标选择算法展现了优秀的工程设计原则:

设计优势

  1. 清晰的职责分离:每个方法专注于特定功能
  2. 灵活的扩展性:易于适应新的需求变化
  3. 稳定的性能表现O(n)时间复杂度确保高效运行
  4. 健壮的异常处理:多层次保护机制

战术价值

算法的设计充分考虑了战斗策略的需求:

  • 优先级策略:确保关键目标优先被选中
  • 随机性机制:增加战斗的不确定性和趣味性
  • 边界处理:优雅处理异常情况
  • 阵营感知:符合游戏世界的逻辑设定

未来改进方向

  1. 智能预测:基于历史数据预测目标偏好
  2. 动态权重:根据战斗状态调整目标选择权重
  3. 机器学习利用AI技术优化目标选择策略
  4. 实时反馈:提供更直观的目标选择反馈

该算法为游戏战斗系统提供了坚实的基础,其设计理念和实现方式值得在类似项目中借鉴和应用。