Player’s Counter attack (完整弹反功能)
接着继续制作玩家弹反部分,包括了动画、输入
动画新加1格挡、2成功格挡
思路:
敌人(Skeleton)已经做了在动画之间开启窗口和关闭窗口。
玩家在按下格挡后检测前方敌人是否处于窗口(CanBeStuned)且
因为public所以player也可访问enemy这个方法。
是就玩家格挡成功转向格挡成功动画(运行完后trgger=true跳回idle)且
同时敌人被调用CanBeStuned跳到Stun状态。
注:
在实现前先去注释掉Enemy_Skeleton的这里(否则按Q就触发Stun)
后面去PlayerState 检测输入
//if (Input.GetKeyDown(KeyCode.U))
//{
// stateMachine.ChangeState(stunState);
//}
Player脚本的代码,主要是声明和new新的状态
using System.Collections;
using System.Collections.Generic;
using System.Linq.Expressions;
using UnityEngine;
public class Player : Entity
{
[Header("Move Info")]
public float moveSpeed = 8f;
public float jumpForce = 10f;
[Header("Dash Info")]
public float dashSpeed =20f;
public float dashDuration;
public float dashCoolDown; //CD
public float dashUsageTime;
[Header("Attack Info")]
public Vector2[] attackMovement; //双值数组
[Header("CounterAttack Info")]
public float counterAttackDuration;
#region States
public PlayerStateMachine stateMachine { get; private set; }
public PlayerIdleState idleState { get; private set; }
public PlayerMoveState moveState { get; private set; }
public PlayerJumpState jumpState { get; private set; }
public PlayerFallState fallState { get; private set; }
public PlayerDashState dashState { get; private set; }
public PlayerWallSlideState wallSlideState { get; private set; }
public PlayerWallJumpState wallJump { get; private set; }
public PlayerPrimaryAttackState primaryAttack { get; private set; }
public PlayerCounterAttack counterAttack { get; private set; }
#endregion
protected override void Awake()
{
stateMachine = new PlayerStateMachine();
idleState = new PlayerIdleState(this, stateMachine,"Idle");
moveState = new PlayerMoveState(this, stateMachine, "Move");
jumpState = new PlayerJumpState(this, stateMachine, "Jump");
fallState = new PlayerFallState(this, stateMachine, "Fall");
dashState = new PlayerDashState(this, stateMachine, "Dash");
wallSlideState = new PlayerWallSlideState(this, stateMachine, "WallSlide");
wallJump = new PlayerWallJumpState(this, stateMachine, "Jump");
primaryAttack = new PlayerPrimaryAttackState(this, stateMachine, "Attack");
counterAttack = new PlayerCounterAttack(this, stateMachine, "CounterAttack");
}
protected override void Start()
{
//Debug.Log("开始");
base.Start();
stateMachine.Initialize(idleState);
}
protected override void Update()
{
base.Update();
stateMachine.currentState.Update();
CheckForDashInput();
//Test();
}
//========================================================================================================================
public void Test()
{
//Debug.Log("Wall" + IsWallSlideDetect());
//Debug.Log("Ground" + IsGroundDetect());
}
public override void Damage()
{
base.Damage();
}
public void AnimationTrigger() => stateMachine.currentState.AnimationFinishTrigger();
//可以让其他函数不调用状态机就调用玩家组建里面的方法,不然不好确认是哪个stateMachine(不同玩家的)
//方法意思:这个状态机的当前状态将执行trigger
public void CheckForDashInput()
{
dashUsageTime -= Time.deltaTime;
if (IsGroundDetect())
{
return;
}
if (Input.GetKeyDown(KeyCode.LeftShift ) && dashUsageTime < 0)
{
dashUsageTime = dashCoolDown; //重置cd
dashDir = Input.GetAxisRaw("Horizontal");
if(dashDir == 0)
{
dashDir = facingDir;
}
stateMachine.ChangeState(dashState);
}
}
}
PlayerCounterAttack 的代码(本节重点),新增主要是检测敌人是否处于弹反窗口且敌人转向Stun(如果是true)
注:
SuccessFul动画最后一帧要加入trigger,使得运行完后CounterAttack脚本转向Idle。通过最下面if
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
public class PlayerCounterAttack : PlayerState
{
public PlayerCounterAttack(Player _player, PlayerStateMachine _stateMachine, string _animBoolName) : base(_player, _stateMachine, _animBoolName)
{
}
public override void Enter()
{
base.Enter();
//格挡时间
stateTimer = player.counterAttackDuration;
//成功格挡动画为false
player.anim.SetBool("SuccessfulCountAttack", false);
}
public override void Update()
{
base.Update();
//一个圆的碰撞体 (数组)
Collider2D[] colliders = Physics2D.OverlapCircleAll(player.attackCheck.position, player.attackCheckRadius);
foreach (var hit in colliders)
{
if (hit.GetComponent<Enemy>() != null)
{
if(hit.GetComponent<Enemy>().CanBeStuned())
{
player.anim.SetBool("SuccessfulCountAttack", true);
stateTimer = 10; //重置时间,让不退出
Debug.Log("弹反!");
}
}
}
Debug.Log("triggerCalled= " + triggerCalled);
Debug.Log("Timer= " + stateTimer);
//时间到,弹反失败
if (stateTimer < 0 || triggerCalled)
{
player.stateMachine.ChangeState(player.idleState);
}
}
public override void Exit()
{
base.Exit();
}
}