379 lines
12 KiB
Markdown
379 lines
12 KiB
Markdown
# 技能执行机制
|
||
|
||
<cite>
|
||
**本文档中引用的文件**
|
||
- [HeroSkills.ts](file://assets\script\game\hero\HeroSkills.ts) - *重构技能数据组件*
|
||
- [Skill.ts](file://assets\script\game\skill\Skill.ts) - *新增技能实体与组件结构*
|
||
- [AtkConCom.ts](file://assets\script\game\skill\AtkConCom.ts) - *优化技能碰撞与方向处理*
|
||
- [HeroAtk.ts](file://assets\script\game\hero\HeroAtk.ts) - *重构伤害计算系统*
|
||
- [SkillSet.ts](file://assets\script\game/common/config/SkillSet.ts) - *重构技能配置表*
|
||
- [SkillView.ts](file://assets\script\game\skill\SkillView.ts) - *更新目标选择与范围伤害逻辑*
|
||
</cite>
|
||
|
||
## 更新摘要
|
||
**变更内容**
|
||
- 根据最新重构提交更新技能执行机制文档
|
||
- 新增技能数据组件(SDataCom)和移动组件(SMoveDataComp)说明
|
||
- 重构技能伤害计算流程,增加命中检测与伤害类型处理
|
||
- 更新技能碰撞检测逻辑,支持范围伤害与目标数量限制
|
||
- 优化技能加载与方向处理机制
|
||
- 支持前后摇动画配置
|
||
- 更新核心组件架构图以反映ECS实体结构变化
|
||
|
||
## 目录
|
||
1. [概述](#概述)
|
||
2. [核心组件架构](#核心组件架构)
|
||
3. [技能冷却检测机制](#技能冷却检测机制)
|
||
4. [技能施放流程](#技能施放流程)
|
||
5. [多段连发技能机制](#多段连发技能机制)
|
||
6. [目标选择策略](#目标选择策略)
|
||
7. [异常处理与资源清理](#异常处理与资源清理)
|
||
8. [性能优化考虑](#性能优化考虑)
|
||
9. [最佳实践总结](#最佳实践总结)
|
||
|
||
## 概述
|
||
|
||
SkillConComp类是游戏技能控制系统的核心组件,负责管理英雄的技能冷却、自动施放条件判断以及技能执行流程。该系统采用ECS架构模式,结合定时器机制和节点有效性检查,实现了复杂的技能逻辑控制。近期重构优化了伤害计算与技能释放逻辑,增加了命中检测和伤害类型处理,并支持范围伤害和数量限制。
|
||
|
||
## 核心组件架构
|
||
|
||
```mermaid
|
||
classDiagram
|
||
class Skill {
|
||
+addComponents() void
|
||
+load() void
|
||
+SDataCom : SDataCom
|
||
+SMoveCom : SMoveDataComp
|
||
+SView : SkillView
|
||
}
|
||
class SDataCom {
|
||
+group : number
|
||
+hit_count : number
|
||
+Attrs : any
|
||
+caster : HeroViewComp
|
||
}
|
||
class SMoveDataComp {
|
||
+startPos : Vec3
|
||
+targetPos : Vec3
|
||
+s_uuid : number
|
||
+scale : number
|
||
+rePos() void
|
||
}
|
||
class SkillView {
|
||
+s_uuid : number
|
||
+group : number
|
||
+SConf : SkillConfig
|
||
+sData : SDataCom
|
||
+anim : Animation
|
||
+start() void
|
||
+update() void
|
||
+onBeginContact() void
|
||
+do_linear() void
|
||
+do_bezier() void
|
||
+do_fixedStart() void
|
||
+apply_damage() void
|
||
}
|
||
class HeroSkillsComp {
|
||
+skills : Record<number, SkillSlot>
|
||
+initSkills() void
|
||
+addSkill() void
|
||
+getSkill() void
|
||
+canCast() void
|
||
}
|
||
Skill --> SDataCom : "包含"
|
||
Skill --> SMoveDataComp : "包含"
|
||
Skill --> SkillView : "包含"
|
||
SkillView --> SDataCom : "引用"
|
||
SkillView --> SMoveDataComp : "引用"
|
||
HeroSkillsComp --> SkillSlot : "管理"
|
||
```
|
||
|
||
**图表来源**
|
||
- [Skill.ts](file://assets\script\game\skill\Skill.ts#L1-L39)
|
||
- [SDataCom.ts](file://assets\script\game\skill\SDataCom.ts#L1-L20)
|
||
- [SMoveComp.ts](file://assets\script\game\skill\SMoveComp.ts#L1-L25)
|
||
- [SkillView.ts](file://assets\script\game\skill\SkillView.ts#L1-L200)
|
||
- [HeroSkills.ts](file://assets\script\game\hero\HeroSkills.ts#L1-L91)
|
||
|
||
**章节来源**
|
||
- [Skill.ts](file://assets\script\game\skill\Skill.ts#L1-L74)
|
||
- [HeroSkills.ts](file://assets\script\game\hero\HeroSkills.ts#L1-L91)
|
||
|
||
## 技能冷却检测机制
|
||
|
||
### update循环中的冷却检测
|
||
|
||
SkillConComp的update方法实现了精确的技能冷却管理系统:
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
Start([update循环开始]) --> CheckMission{"检查游戏状态<br/>smc.mission.play && !pause"}
|
||
CheckMission --> |否| End([结束])
|
||
CheckMission --> |是| CheckStatus{"检查状态<br/>!isStun && !isFrost"}
|
||
CheckStatus --> |否| End
|
||
CheckStatus --> |是| LoopSkills["遍历技能列表"]
|
||
LoopSkills --> AddCD["累积冷却时间<br/>skills[i].cd += dt"]
|
||
AddCD --> CheckCD{"冷却完成<br/>cd > cd_max?"}
|
||
CheckCD --> |否| NextSkill["下一技能"]
|
||
CheckCD --> |是| CheckMP{"检查魔法值<br/>mp >= cost?"}
|
||
CheckMP --> |否| NextSkill
|
||
CheckMP --> |是| CheckType{"技能类型<br/>SType.damage?"}
|
||
CheckType --> |否| NextSkill
|
||
CheckType --> |是| CheckAttack{"正在攻击<br/>is_atking?"}
|
||
CheckAttack --> |否| NextSkill
|
||
CheckAttack --> |是| CastSkill["调用castSkill"]
|
||
CastSkill --> ResetCD["重置冷却<br/>skills[i].cd = 0"]
|
||
ResetCD --> ConsumeMP["消耗魔法值<br/>mp -= cost"]
|
||
ConsumeMP --> NextSkill
|
||
NextSkill --> MoreSkills{"还有技能?"}
|
||
MoreSkills --> |是| LoopSkills
|
||
MoreSkills --> |否| End
|
||
```
|
||
|
||
**图表来源**
|
||
- [SkillConComp.ts](file://assets/script/game/hero/SkillConComp.ts#L35-L52)
|
||
|
||
### 冷却检测的关键要素
|
||
|
||
1. **时间累积机制**:每个技能的冷却时间通过`skills[i].cd += dt`逐步累积
|
||
2. **阈值判断**:当冷却时间超过最大冷却时间`cd_max`时触发
|
||
3. **资源检查**:确保魔法值充足`this.HeroView.mp >= skills[i].cost`
|
||
4. **状态验证**:排除眩晕和冰冻状态影响
|
||
5. **攻击状态检查**:只有在攻击状态下才允许施放伤害技能
|
||
|
||
**章节来源**
|
||
- [HeroSkills.ts](file://assets\script\game\hero\HeroSkills.ts#L50-L55)
|
||
|
||
## 技能施放流程
|
||
|
||
### castSkill与doSkill方法调用链
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant Player as "玩家输入/自动触发"
|
||
participant SkillCon as "SkillConComp"
|
||
participant HeroView as "HeroViewComp"
|
||
participant SkillEnt as "Skill"
|
||
participant ECS as "ECS系统"
|
||
Player->>SkillCon : castSkill(config)
|
||
SkillCon->>SkillCon : check_wfuny()
|
||
SkillCon->>SkillCon : doSkill(config, wfuny, dmg)
|
||
Note over SkillCon : 节点有效性检查
|
||
SkillCon->>SkillCon : 检查节点有效性
|
||
alt 目标组为Self
|
||
SkillCon->>SkillCon : targets = [this.node.position]
|
||
else 目标组为Enemy
|
||
SkillCon->>SkillCon : selectTargets(config.t_num)
|
||
end
|
||
SkillCon->>HeroView : playSkillEffect(config.uuid)
|
||
HeroView->>HeroView : 播放技能特效
|
||
SkillCon->>ECS : ecs.getEntity<Skill>(Skill)
|
||
SkillCon->>SkillCon : 创建setTimeout定时器
|
||
Note over SkillCon : 延迟执行300ms
|
||
SkillCon->>SkillCon : 定时器回调函数
|
||
SkillCon->>SkillCon : 再次检查节点有效性
|
||
SkillCon->>SkillEnt : sEnt.load(...)
|
||
SkillCon->>SkillEnt : load方法
|
||
SkillEnt->>SkillEnt : 加载技能预制体
|
||
SkillEnt->>SkillEnt : 设置节点属性
|
||
SkillEnt->>SkillEnt : 添加SkillView组件
|
||
alt wfuny机制启用
|
||
SkillCon->>SkillCon : scheduleOnce(doSkill, 0.1)
|
||
end
|
||
Note over SkillCon : 保存定时器ID
|
||
SkillCon->>SkillCon : this._timers[`skill_${config.uuid}`] = timerId
|
||
```
|
||
|
||
**图表来源**
|
||
- [Skill.ts](file://assets\script\game\skill\Skill.ts#L35-L74)
|
||
- [SkillView.ts](file://assets\script\game\skill\SkillView.ts#L27-L61)
|
||
|
||
### 节点有效性检查机制
|
||
|
||
系统实现了双重节点有效性检查:
|
||
|
||
1. **初始检查**:在`doSkill`方法开头进行基础节点有效性验证
|
||
2. **延迟检查**:在定时器回调函数中再次确认节点状态
|
||
|
||
这种设计确保了即使在技能施放过程中节点被销毁,也不会导致错误操作。
|
||
|
||
**章节来源**
|
||
- [Skill.ts](file://assets\script\game\skill\Skill.ts#L35-L74)
|
||
|
||
## 多段连发技能机制
|
||
|
||
### wfuny机制实现原理
|
||
|
||
wfuny机制是一种基于概率的技能连发系统:
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
Start([doSkill开始]) --> CheckWfuny{"检查wfuny<br/>check_wfuny()"}
|
||
CheckWfuny --> |false| NormalSkill["正常技能执行"]
|
||
CheckWfuny --> |true| ScheduleOnce["scheduleOnce(doSkill, 0.1)"]
|
||
ScheduleOnce --> RecursiveCall["递归调用doSkill<br/>is_wfuny=false"]
|
||
RecursiveCall --> CheckWfuny2{"再次检查wfuny"}
|
||
CheckWfuny2 --> |false| FinalExecution["最终技能执行"]
|
||
CheckWfuny2 --> |true| ContinueRecursion["继续递归"]
|
||
ContinueRecursion --> ScheduleOnce
|
||
NormalSkill --> SaveTimer["保存定时器ID"]
|
||
FinalExecution --> SaveTimer
|
||
SaveTimer --> End([结束])
|
||
```
|
||
|
||
**图表来源**
|
||
- [SkillConComp.ts](file://assets/script/game/hero/SkillConComp.ts#L82-L87)
|
||
|
||
### wfuny概率计算
|
||
|
||
```typescript
|
||
check_wfuny() {
|
||
let random = Math.random() * 100
|
||
if (random < this.HeroView.Attrs[Attrs.WFUNY]) {
|
||
return true
|
||
}
|
||
return false
|
||
}
|
||
```
|
||
|
||
该方法:
|
||
1. 生成0-100之间的随机数
|
||
2. 与英雄的WFUNY属性值比较
|
||
3. 当随机数小于WFUNY值时返回true,触发连发
|
||
|
||
**章节来源**
|
||
- [SkillConComp.ts](file://assets/script/game/hero/SkillConComp.ts#L89-L95)
|
||
|
||
## 目标选择策略
|
||
|
||
### selectTargets方法实现
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
Start([selectTargets开始]) --> GetEntities["获取目标实体<br/>check_target()"]
|
||
GetEntities --> CheckEmpty{"实体列表为空?"}
|
||
CheckEmpty --> |是| DefaultPos["返回默认位置<br/>t_num个相同坐标"]
|
||
DefaultPos --> Return([返回目标坐标数组])
|
||
CheckEmpty --> |否| FindFront["找到最前排目标<br/>get_front(entities)"]
|
||
FindFront --> PushFront["targets.push(frontPos)"]
|
||
PushFront --> LoopOthers["循环处理剩余目标<br/>i=1 to t_num-1"]
|
||
LoopOthers --> SelectRandom["随机选择实体<br/>Math.floor(Math.random() * entities.length)"]
|
||
SelectRandom --> GetRandomPos["获取随机实体位置"]
|
||
GetRandomPos --> PushRandom["targets.push(randomPos)"]
|
||
PushRandom --> MoreTargets{"还有目标?"}
|
||
MoreTargets --> |是| LoopOthers
|
||
MoreTargets --> |否| Return
|
||
```
|
||
|
||
**图表来源**
|
||
- [SkillConComp.ts](file://assets/script/game/hero/SkillConComp.ts#L112-L155)
|
||
|
||
### 目标选择算法详解
|
||
|
||
1. **实体获取**:通过`check_target()`方法获取敌方实体列表
|
||
2. **前排优先**:第一个目标总是最前排的实体
|
||
3. **随机分布**:后续目标从所有可用实体中随机选择
|
||
4. **重复可能**:由于随机选择,可能出现重复目标
|
||
5. **默认处理**:当没有可用目标时,返回预设的默认位置
|
||
|
||
### 默认位置处理逻辑
|
||
|
||
```typescript
|
||
const defaultPos = this.HeroView.fac === FacSet.HERO ? v3(400, 0, 0) : v3(-400, 0, 0)
|
||
```
|
||
|
||
系统根据施法者阵营确定默认位置:
|
||
- 英雄阵营:x=400
|
||
- 敌人阵营:x=-400
|
||
|
||
**章节来源**
|
||
- [SkillConComp.ts](file://assets/script/game/hero/SkillConComp.ts#L112-L155)
|
||
|
||
## 异常处理与资源清理
|
||
|
||
### 定时器管理机制
|
||
|
||
```mermaid
|
||
classDiagram
|
||
class TimerManagement {
|
||
-_timers : object
|
||
+clear_timer() void
|
||
+reset() void
|
||
+onDestroy() void
|
||
}
|
||
class TimerOperations {
|
||
+Object.values(this._timers).forEach(clearTimeout)
|
||
+this._timers = {}
|
||
+this.off(GameEvent.CastHeroSkill)
|
||
}
|
||
TimerManagement --> TimerOperations : "执行"
|
||
```
|
||
|
||
**图表来源**
|
||
- [SkillConComp.ts](file://assets/script/game/hero/SkillConComp.ts#L157-L177)
|
||
|
||
### 异常处理策略
|
||
|
||
1. **节点有效性检查**:在关键操作前后检查节点状态
|
||
2. **定时器清理**:确保技能执行完成后清理相关定时器
|
||
3. **事件监听移除**:在组件销毁时移除事件监听器
|
||
4. **资源释放**:在组件销毁时释放ECS实体资源
|
||
|
||
### 资源清理最佳实践
|
||
|
||
```typescript
|
||
public clear_timer() {
|
||
Object.values(this._timers).forEach(clearTimeout);
|
||
}
|
||
|
||
reset() {
|
||
this.clear_timer();
|
||
}
|
||
|
||
onDestroy() {
|
||
Object.values(this._timers).forEach(clearTimeout);
|
||
this._timers = {};
|
||
this.off(GameEvent.CastHeroSkill);
|
||
}
|
||
```
|
||
|
||
这些方法确保:
|
||
- 防止内存泄漏
|
||
- 避免僵尸定时器
|
||
- 清理事件监听器
|
||
- 正确释放ECS资源
|
||
|
||
**章节来源**
|
||
- [SkillConComp.ts](file://assets/script/game/hero/SkillConComp.ts#L157-L177)
|
||
|
||
## 性能优化考虑
|
||
|
||
### 定时器优化策略
|
||
|
||
1. **延迟执行**:技能实体创建延迟300ms,避免立即创建大量对象
|
||
2. **条件检查**:在每次操作前进行有效性检查,防止无效操作
|
||
3. **批量清理**:使用`Object.values`一次性清理所有定时器
|
||
|
||
### 内存管理优化
|
||
|
||
1. **及时清理**:在组件销毁时立即清理所有资源
|
||
2. **弱引用**:避免循环引用导致的内存泄漏
|
||
3. **对象池**:利用ECS系统提供的实体池机制
|
||
|
||
## 最佳实践总结
|
||
|
||
### 技能系统设计原则
|
||
|
||
1. **模块化设计**:SkillConComp专注于控制逻辑,Skill负责实体管理
|
||
2. **事件驱动**:通过ECS系统实现松耦合的组件通信
|
||
3. **容错机制**:多重节点有效性检查确保系统稳定性
|
||
4. **资源管理**:完善的定时器和事件监听器清理机制
|
||
|
||
### 开发建议
|
||
|
||
1. **扩展性考虑**:为新的技能类型预留接口
|
||
2. **性能监控**:定期检查定时器数量和内存使用情况
|
||
3. **调试支持**:保留必要的日志输出便于问题排查
|
||
4. **测试覆盖**:确保各种边界情况都有相应的测试用例
|
||
|
||
这个技能执行机制展现了现代游戏开发中复杂系统的设计精髓,通过合理的架构设计和完善的异常处理,实现了稳定可靠的技能控制系统。 |