如何在敌人攻击的时间窗口内检测防御按键并执行对应动作?
问题分析与修复方案
核心问题
- 重复启动协程:Update每帧检测动画播放状态,只要攻击动画在运行就会启动新的
DefendTiming协程,导致同一攻击触发多次判定逻辑,出现异常。 - 状态管理混乱:
defendButtonPressed和receivedDamage没有在攻击结束后重置,后续攻击无法正确判断玩家操作。 - 判定时机偏差:等待整个动画时长再判定,不符合“攻击时机”的设计逻辑,应该在动画的特定命中帧触发判定。
修复代码示例
1. 添加状态标记,避免重复执行
先在类中添加布尔变量,标记当前是否正在处理攻击判定:
private bool isProcessingAttack = false;
2. 修改Update中的检测逻辑
只有当攻击动画播放且未在处理判定时,才启动协程:
void Update() { bool isAttackPlaying = enemy.CheckIfAnimationIsPlaying("Enemy_mainAttack") || enemy.CheckIfAnimationIsPlaying("Enemy_mainAttack2") || enemy.CheckIfAnimationIsPlaying("Enemy_firstAttack"); if (isAttackPlaying && !isProcessingAttack) { isProcessingAttack = true; StartCoroutine(DefendTiming()); } // 防御按键检测(替换为你的实际按键) if (Input.GetKeyDown(KeyCode.LeftShift)) { defendButtonPressed = true; } }
3. 优化判定逻辑(推荐用动画事件)
在攻击动画的命中帧添加Animator事件,直接调用判定方法,精准控制时机:
// 敌人动画事件绑定的方法(挂在敌人对象上) public void TriggerAttackCheck() { if (!defendButtonPressed && !receivedDamage) { ReceiveDamage(enemy.attack); receivedDamage = true; } else { ReduceEndurance(false, 3); } } // 协程仅负责攻击结束后的状态重置 private IEnumerator DefendTiming() { AnimatorStateInfo stateInfo = enemy.animator.GetCurrentAnimatorStateInfo(0); yield return new WaitForSeconds(stateInfo.length); // 重置所有状态,确保下一次攻击正常判定 isProcessingAttack = false; defendButtonPressed = false; receivedDamage = false; }
4. 备选方案:无动画事件时的优化
如果暂时不用动画事件,可调整协程的等待时间,瞄准攻击判定窗口:
private IEnumerator DefendTiming() { AnimatorStateInfo stateInfo = enemy.animator.GetCurrentAnimatorStateInfo(0); // 假设判定时机在动画进行到70%的位置(可根据实际调整) float judgeWindowTime = stateInfo.length * 0.7f; yield return new WaitForSeconds(judgeWindowTime); // 执行判定逻辑 if (!defendButtonPressed && !receivedDamage) { ReceiveDamage(enemy.attack); receivedDamage = true; } else { ReduceEndurance(false, 3); } // 等待动画结束后重置状态 yield return new WaitForSeconds(stateInfo.length - judgeWindowTime); isProcessingAttack = false; defendButtonPressed = false; receivedDamage = false; }
额外优化建议
把攻击动画名称整理成常量数组,避免硬编码:
private readonly string[] _attackAnimations = { "Enemy_mainAttack", "Enemy_mainAttack2", "Enemy_firstAttack" };
后续检测时用遍历简化逻辑:
bool isAttackPlaying = _attackAnimations.Any(anim => enemy.CheckIfAnimationIsPlaying(anim));
内容的提问来源于stack exchange,提问作者pauSei
相关产品推荐
相关产品推荐

