如何简化2D动画状态机过渡并实现完整攻击动画播放
解决Unity动画过渡繁杂+攻击动画被覆盖问题
一、先搞定攻击动画被打断的问题
核心思路是加一个攻击状态锁,让FixedUpdate里的idle/walk逻辑只在未攻击时生效:
- 定义布尔变量控制攻击状态:
private bool isAttacking;
- 修改FixedUpdate,先判断是否在攻击,不在才处理移动/待机动画:
private void FixedUpdate() { if (isAttacking) return; // 攻击时直接跳过,不播放idle/walk bool isMoving = moveInput.x != 0 || moveInput.y != 0; if (isMoving) { if (moveInput.y == 0) { if (moveInput.x < 0) { animator.SetInteger(Direction, (int)MovingDirection.Left); animator.Play("player_walk_right"); spriteRenderer.flipX = true; } else if (moveInput.x > 0) { animator.SetInteger(Direction, (int)MovingDirection.Right); animator.Play("player_walk_right"); spriteRenderer.flipX = false; } } else { if (moveInput.y < 0) { animator.SetInteger(Direction, (int)MovingDirection.Down); animator.Play("player_walk_front"); } else if (moveInput.y > 0) { animator.SetInteger(Direction, (int)MovingDirection.Up); animator.Play("player_walk_back"); } } } else { // 根据当前方向播放对应待机动画 switch(animator.GetInteger(Direction)) { case (int)MovingDirection.Up: animator.Play("player_idle_back"); break; case (int)MovingDirection.Down: animator.Play("player_idle_front"); break; case (int)MovingDirection.Left: animator.Play("player_idle_right"); spriteRenderer.flipX = true; break; case (int)MovingDirection.Right: animator.Play("player_idle_right"); spriteRenderer.flipX = false; break; } } }
- 修改OnFire方法,启动攻击时锁定状态:
void OnFire() { if (isAttacking) return; // 防止重复触发攻击 isAttacking = true; switch (animator.GetInteger(Direction)) { case (int)MovingDirection.Up: animator.Play("player_attack_back"); break; case (int)MovingDirection.Down: animator.Play("player_attack_front"); break; case (int)MovingDirection.Left: animator.Play("player_attack_right"); spriteRenderer.flipX = true; break; case (int)MovingDirection.Right: animator.Play("player_attack_right"); spriteRenderer.flipX = false; break; } }
- 添加攻击结束回调,通过动画事件触发:
void OnAttackEnd() { isAttacking = false; // 攻击结束后自动回到对应方向的idle或walk(若此时有移动输入) }
在Unity编辑器中,选中攻击动画的最后一帧,添加动画事件并调用OnAttackEnd()方法。
二、简化Animator过渡的方法
用**子状态机(Sub-State Machine)**打包同类型状态,大幅减少过渡连线:
- 在Animator窗口右键创建3个子状态机:
IdleStates、WalkStates、AttackStates - 将所有idle方向状态拖入
IdleStates,walk拖入WalkStates,attack拖入AttackStates - 给Animator添加Integer参数
ActionType,对应:- 0 = 待机
- 1 = 行走
- 2 = 攻击
- 仅在三个子状态机间设置过渡:
IdleStates↔WalkStates:条件为ActionType等于对应值,可结合Direction参数判断方向IdleStates/WalkStates→AttackStates:条件为ActionType等于2(或用Trigger参数AttackTrigger)AttackStates→IdleStates/WalkStates:勾选过渡的Has Exit Time,动画播放完成后,根据当前移动输入设置ActionType为0或1
这样不用每个单独状态互相连线,仅需子状态机间的几条过渡,逻辑瞬间清晰。
内容的提问来源于stack exchange,提问作者hbprotoss
相关产品推荐
相关产品推荐

