image-20250520115822802

轻重攻击逻辑

  • 轻重攻击只能在地面释放
  • 轻攻击有三段,第二段可以衔接重攻击
  • 重攻击可以单独释放

image-20250521102337186

实现

状态机的切换

攻击的实现和跳跃,冲刺的实现类似。都是使用输入事件监听的方法。在PlayerGroundState中添加新的事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
public class PlayerGroundState : PlayerState
{
public PlayerGroundState(Player player, PlayerStateMachine stateMachine, string animBoolName, string animTriggerName) : base(player, stateMachine, animBoolName, animTriggerName)
{
}

public override void Enter()
{
base.Enter();
playerInput.GamePlay.Jump.started += Jump;
playerInput.GamePlay.Dash.started += Dash;
playerInput.GamePlay.BackDash.started += BackDash;
playerInput.GamePlay.LightAttack.started += LightAttack;
playerInput.GamePlay.HeavyAttack.started += HeavyAttack;
}

private void HeavyAttack(InputAction.CallbackContext context)
{
if(physicsCheck.isGrounded) stateMachine.ChangeState(player.heavyAttackState);
}

private void LightAttack(InputAction.CallbackContext context)
{
if (physicsCheck.isGrounded)
stateMachine.ChangeState(player.lightAttackState);
}

private void BackDash(InputAction.CallbackContext context)
{
if (physicsCheck.isGrounded)
stateMachine.ChangeState(player.backDashState);
}

private void Dash(InputAction.CallbackContext context)
{
if (physicsCheck.isGrounded)
stateMachine.ChangeState(player.dashState);
}

private void Jump(InputAction.CallbackContext context)
{
if(physicsCheck.isGrounded)
stateMachine.ChangeState(player.jumpState);
}

public override void Exit()
{
base.Exit();
playerInput.GamePlay.Jump.started -= Jump;
playerInput.GamePlay.Dash.started -= Dash;
playerInput.GamePlay.BackDash.started -= BackDash;
playerInput.GamePlay.LightAttack.started -= LightAttack;
playerInput.GamePlay.HeavyAttack.started -= HeavyAttack;
}

public override void LogicUpdate()
{
base.LogicUpdate();
if (!physicsCheck.isGrounded) stateMachine.ChangeState(player.fallState);
}

public override void PhysicsUpdate()
{
base.PhysicsUpdate();
}
}

当按下轻重攻击时,分别进入到对应状态。

1
2
3
4
5
6
7
8
9
private void HeavyAttack(InputAction.CallbackContext context)
{
if(physicsCheck.isGrounded) stateMachine.ChangeState(player.heavyAttackState);
}

private void LightAttack(InputAction.CallbackContext context)
{
if (physicsCheck.isGrounded) stateMachine.ChangeState(player.lightAttackState);
}

动画状态机设置

动画状态机的对应参数是Trigger和comboCounter

image-20250521095013034

如何实现多段攻击

​ 通过在LightAttack状态中同样添加按键触发事件来实现。当从Ground状态进入到LightAttack状态时,每按一次轻攻击键都会触发Trigger。但是这样就存在一个问题,因为anystate连接到了第一段攻击,当我们进行第二段攻击时,再次按下攻击键,又会返回到第一段攻击,所以我们需要额外添加一个ComboCounter参数,当进入到攻击状态时,把combo数置0,之后每触发一次攻击就加一,至于状态的退出,还是用动画绑定事件的方法。重攻击状态的实现比较简单,这里就不放了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public class PlayerLightAttackState : PlayerState
{
public PlayerLightAttackState(Player player, PlayerStateMachine stateMachine, string animBoolName, string animTriggerName) : base(player, stateMachine, animBoolName, animTriggerName)
{
}

public override void Enter()
{
base.Enter();
player.comboCounter = 0;
player.animator.SetInteger("ComboCounter", player.comboCounter);
playerInput.GamePlay.LightAttack.started += LightAttack;
playerInput.GamePlay.HeavyAttack.started += HeavyAttack;
}

private void HeavyAttack(InputAction.CallbackContext context)
{

stateMachine.ChangeState(player.heavyAttackState);
}

private void LightAttack(InputAction.CallbackContext context)
{
player.comboCounter++;
player.animator.SetInteger("ComboCounter", player.comboCounter);
animator.SetTrigger("LightAttackTrigger");
}

public override void Exit()
{
base.Exit();

playerInput.GamePlay.LightAttack.started -= LightAttack;
playerInput.GamePlay.HeavyAttack.started -= HeavyAttack;
}

public override void LogicUpdate()
{
base.LogicUpdate();
if (animFinTrigger) stateMachine.ChangeState(player.idleState);
}

public override void PhysicsUpdate()
{
player.SetZeroVelocity();
}

}

输入窗口

​ 上面的实现虽然能完成连段的释放,但是存在一个问题,那就是只要输入足够快,每一段攻击都可以瞬间切换到下一段,也就是可以在任意时候取消上一次的攻击,为了限制可以取消的时机,我们为player增加一个Bool变量isAttacking,只有isAttacking为false时,才响应输入事件,同样通过动画绑定事件的方法来控制isAttacking变量。

1
2
3
4
5
6
7
8
9
10
//PlayerAniEvents
public void AnimationAttack()
{
player.isAttacking = true;
}
public void AnimationAttackOver()
{
player.isAttacking = false;

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//PlayerLightAttack
private void HeavyAttack(InputAction.CallbackContext context)
{
if (!player.isAttacking)
stateMachine.ChangeState(player.heavyAttackState);
}

private void LightAttack(InputAction.CallbackContext context)
{
if (!player.isAttacking)
{
player.comboCounter++;
player.animator.SetInteger("ComboCounter", player.comboCounter);
animator.SetTrigger("LightAttackTrigger");
}
}

AnimationAttackOver到AnimationFin之间就是可以衔接连段的窗口

image-20250521101054741