# 目标选择策略 **本文档引用的文件** - [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判断左右方向 ## 项目结构 ```mermaid graph TB subgraph "技能系统" SkillConComp["SkillConComp
技能控制器"] SkillEnt["SkillEnt
技能实体"] end subgraph "阵营系统" FacSet["FacSet
阵营枚举"] HeroViewComp["HeroViewComp
英雄视图组件"] end subgraph "ECS系统" ECS["ECS框架"] BattleMoveSystem["BattleMoveSystem
战斗移动系统"] end subgraph "目标检测" check_target["check_target()
目标检测"] get_front["get_front()
前排检测"] selectTargets["selectTargets()
目标选择"] end SkillConComp --> check_target SkillConComp --> get_front SkillConComp --> selectTargets check_target --> ECS get_front --> ECS BattleMoveSystem --> ECS ``` **图表来源** - [SkillConComp.ts](file://assets/script/game/hero/SkillConComp.ts#L109-L122) - [BattleMoveSystem.ts](file://assets/script/game/common/ecs/position/BattleMoveSystem.ts#L165-L200) **章节来源** - [SkillConComp.ts](file://assets/script/game/hero/SkillConComp.ts#L1-L177) - [BoxSet.ts](file://assets/script/game/common/config/BoxSet.ts#L1-L109) ## 核心组件 ### SkillConComp - 技能控制器 `SkillConComp`是目标选择算法的核心控制器,负责协调整个目标选择流程。该组件继承自`CCComp`,实现了ECS注册功能。 主要职责: - **技能施放管理**:控制技能的触发时机和目标选择 - **目标选择协调**:调用`selectTargets`方法获取目标坐标 - **异常处理**:确保节点有效性检查和资源清理 ### FacSet - 阵营系统 `FacSet`定义了游戏中的阵营概念,是目标选择算法的重要输入参数。 阵营常量: - `HERO = 0`:英雄阵营,位于战场左侧 - `MON = 1`:怪物阵营,位于战场右侧 阵营决定了目标选择的方向性逻辑,英雄会选择右侧的怪物作为目标,而怪物会选择左侧的英雄作为目标。 **章节来源** - [SkillConComp.ts](file://assets/script/game/hero/SkillConComp.ts#L1-L50) - [BoxSet.ts](file://assets/script/game/common/config/BoxSet.ts#L48-L50) ## 架构概览 ```mermaid 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 : 目标选择完成,准备施放技能 ``` **图表来源** - [SkillConComp.ts](file://assets/script/game/hero/SkillConComp.ts#L109-L154) - [BattleMoveSystem.ts](file://assets/script/game/common/ecs/position/BattleMoveSystem.ts#L198-L235) ## 详细组件分析 ### selectTargets 方法详解 `selectTargets`方法是目标选择算法的核心实现,采用了精心设计的策略来确保战斗的公平性和策略性。 #### 算法流程 ```mermaid 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 ``` **图表来源** - [SkillConComp.ts](file://assets/script/game/hero/SkillConComp.ts#L128-L154) #### 第一个目标:最前排单位 算法的第一个目标总是最前排的单位,这是基于以下设计原则: 1. **战略优先级**:最前排单位通常是敌方的主力或关键目标 2. **视觉反馈**:玩家能够直观看到技能的主要目标 3. **战斗节奏**:确保技能施放具有明确的战略意义 **实现细节**: - 使用`get_front`方法确定最前排位置 - 根据阵营方向选择最小或最大X坐标 - 确保第一个目标始终是最接近施法者的前排单位 #### 后续目标:随机选择机制 从第二个目标开始,算法采用随机选择机制: 1. **随机性保证**:每个目标都有平等的选择机会 2. **重复允许**:同一个实体可能被多次选中 3. **性能优化**:避免复杂的排序和去重操作 **实现原理**: - 使用`Math.random()`生成随机索引 - 直接从实体列表中选择对应实体 - 确保算法复杂度为O(n),其中n为目标数量 #### 默认位置处理 当没有可用目标时,算法返回预定义的默认位置: ```typescript const defaultPos = this.HeroView.fac === FacSet.HERO ? v3(400, 0, 0) : v3(-400, 0, 0); ``` **设计意图**: - **战场定位**:默认位置位于战场边缘,不影响战斗区域 - **视觉平衡**:确保技能效果在视觉上仍然有意义 - **战术灵活性**:为玩家提供策略选择的空间 **章节来源** - [SkillConComp.ts](file://assets/script/game/hero/SkillConComp.ts#L128-L154) ### check_target 方法分析 `check_target`方法负责根据阵营确定目标实体集合: ```mermaid classDiagram class SkillConComp { +HeroView : any +check_target() Entity[] +get_front(entities) Vec3 +selectTargets(t_num) Vec3[] } class FacSet { <> 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 "提供实体查询功能" ``` **图表来源** - [SkillConComp.ts](file://assets/script/game/hero/SkillConComp.ts#L109-L115) - [BoxSet.ts](file://assets/script/game/common/config/BoxSet.ts#L48-L50) **实现逻辑**: - 英雄阵营(FacSet.HERO)查询怪物实体(MonModelComp) - 怪物阵营(FacSet.MON)查询英雄实体(HeroModelComp) 这种设计确保了目标选择的阵营对立性,符合游戏的战斗机制。 **章节来源** - [SkillConComp.ts](file://assets/script/game/hero/SkillConComp.ts#L109-L115) ### get_front 方法详解 `get_front`方法实现了前排单位的智能识别: #### X坐标比较逻辑 ```mermaid 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["返回前排坐标"] ``` **图表来源** - [SkillConComp.ts](file://assets/script/game/hero/SkillConComp.ts#L116-L122) #### 方向性判断 算法根据阵营自动判断左右方向: 1. **英雄阵营**:寻找最小X坐标,即最左侧的单位 2. **怪物阵营**:寻找最大X坐标,即最右侧的单位 这种设计符合战场布局的直觉认知,英雄在左侧,怪物在右侧。 #### 实体匹配过程 ```typescript 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; ``` 这个过程确保了: - 正确的前排单位被识别 - 坐标信息准确返回 - 异常情况下的稳定性 **章节来源** - [SkillConComp.ts](file://assets/script/game/hero/SkillConComp.ts#L116-L122) ### ECS查询与坐标比较整合 目标选择算法与ECS系统的深度集成体现在以下几个方面: #### 查询优化 ```mermaid 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 ``` **图表来源** - [BattleMoveSystem.ts](file://assets/script/game/common/ecs/position/BattleMoveSystem.ts#L198-L235) #### 性能考量 1. **批量查询**:一次性获取所有符合条件的实体 2. **延迟计算**:只在需要时进行坐标比较 3. **内存优化**:避免不必要的对象创建 #### 异常处理 算法包含了多层异常保护: 1. **空实体检查**:防止空列表访问 2. **坐标有效性验证**:确保坐标数据正确 3. **实体状态检查**:排除无效或死亡的实体 **章节来源** - [BattleMoveSystem.ts](file://assets/script/game/common/ecs/position/BattleMoveSystem.ts#L198-L235) ## 依赖关系分析 ```mermaid 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 ``` **图表来源** - [SkillConComp.ts](file://assets/script/game/hero/SkillConComp.ts#L1-L15) - [BoxSet.ts](file://assets/script/game/common/config/BoxSet.ts#L1-L10) ### 核心依赖关系 1. **ECS框架依赖**:`ecs.query()`用于实体查询 2. **数学运算依赖**:`Math.min()`和`Math.max()`用于坐标比较 3. **类型系统依赖**:`Vec3`和`Entity`类型定义 4. **配置系统依赖**:`FacSet`提供阵营常量 ### 松耦合设计 算法采用松耦合设计,便于维护和扩展: - **接口隔离**:通过ECS查询接口与具体实体解耦 - **配置驱动**:阵营逻辑通过配置文件管理 - **模块化组织**:各功能模块职责单一 **章节来源** - [SkillConComp.ts](file://assets/script/game/hero/SkillConComp.ts#L1-L177) - [BattleMoveSystem.ts](file://assets/script/game/common/ecs/position/BattleMoveSystem.ts#L1-L270) ## 性能考虑 ### 时间复杂度分析 目标选择算法的时间复杂度为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. **单元测试**:为每个方法编写测试用例 **章节来源** - [SkillConComp.ts](file://assets/script/game/hero/SkillConComp.ts#L128-L154) ## 结论 目标选择算法展现了优秀的工程设计原则: ### 设计优势 1. **清晰的职责分离**:每个方法专注于特定功能 2. **灵活的扩展性**:易于适应新的需求变化 3. **稳定的性能表现**:O(n)时间复杂度确保高效运行 4. **健壮的异常处理**:多层次保护机制 ### 战术价值 算法的设计充分考虑了战斗策略的需求: - **优先级策略**:确保关键目标优先被选中 - **随机性机制**:增加战斗的不确定性和趣味性 - **边界处理**:优雅处理异常情况 - **阵营感知**:符合游戏世界的逻辑设定 ### 未来改进方向 1. **智能预测**:基于历史数据预测目标偏好 2. **动态权重**:根据战斗状态调整目标选择权重 3. **机器学习**:利用AI技术优化目标选择策略 4. **实时反馈**:提供更直观的目标选择反馈 该算法为游戏战斗系统提供了坚实的基础,其设计理念和实现方式值得在类似项目中借鉴和应用。