之前已经实现的功能:Player的walk,idle,dash等状态。
目前只使用了动画控制器来管理这些状态,后面会学习用有限状态机来管理这些复杂的状态。
1.回顾之前的脚本
Player脚本:代码如下:
private Rigidbody2D rb;
private Animator anim;//组件的定义
[SerializeField]private float moveSpeed;
[SerializeField]private float jumpForce;//移动速度和跳跃的高度
[Header("Dash info")]
[SerializeField] private float dashSpeed;
[SerializeField] private float dashDuration;
private float dashTime;//冲刺速度,持续时间和计时器
[SerializeField] private float dashCooldown;
private float dashCooldownTimer;//冲刺冷却的时间和其计时器
private float xInput;//玩家x轴,-1和1
private int facingDir=1;
private bool facingRight=true;//玩家翻转的设置
[Header("Collision info")]
[SerializeField] private int groundCheckDistance;//离地面距离用于绘线
[SerializeField] private LayerMask whatisGround;
private bool isGround;//检查是否处于地面图层上
update中的函数
Movement();//移动函数
CheckInput();//检查玩家输入
AnimatorControllers();//动画控制函数
CollisionChecks();//碰撞检测函数
FlipContorller();//翻转控制函数
dashTime -= Time.deltaTime;//冲刺的计时器
dashCooldownTimer -= Time.deltaTime;//冲刺冷却的计时器
具体功能函数
private void CollisionChecks()
{
isGround = Physics2D.Raycast(transform.position, Vector2.down, groundCheckDistance, whatisGround);
}//从玩家的轴心向地面画射线来检测是否在地面上,在则设置isGround为true
private void CheckInput()
{
xInput = Input.GetAxisRaw("Horizontal");
if (Input.GetKeyDown(KeyCode.Space))
Jump();
if (Input.GetKeyDown(KeyCode.LeftShift))
{
dashAbility();
}
}//按空格执行跳跃,按左shift执行冲刺
private void dashAbility()
{
if(dashCooldownTimer<0)
{
dashTime = dashDuration;
dashCooldownTimer = dashCooldown;
}
}//当冲刺计时器<0并且按下冲刺时,将二者计时器重置为持续时间和冷却时间
private void Movement()
{
if (dashTime > 0)
{
rb.velocity = new Vector2(xInput * dashSpeed, 0);
}
else
{
rb.velocity = new Vector2(xInput * moveSpeed, rb.velocity.y);
}
}//冲刺状态下速度要乘以冲刺速度,否则设置为常态速度
private void Jump()
{
if (isGround)
{
rb.velocity = new Vector2(rb.velocity.x, jumpForce);
}
}//检查是否在地面,在则可以跳跃
private void AnimatorControllers()
{
bool isMoving = rb.velocity.x != 0;
anim.SetBool("isMoving", isMoving);
anim.SetBool("isGrounded", isGround);
anim.SetFloat("yVelocity", rb.velocity.y);
anim.SetBool("isDashing", dashTime > 0);
}//动画控制器部分的参数设置
private void Flip()
{
facingDir = -1 * facingDir;
facingRight = !facingRight;
transform.Rotate(0, 180, 0);
}//翻转一次,翻转的参数各自取反,并且玩家组件旋转180°
private void FlipContorller()
{
if (rb.velocity.x > 0 && !facingRight)
Flip();
else if (rb.velocity.x < 0 && facingRight)
Flip();
}//控制何时翻转,保证朝向正确
private void OnDrawGizmos()
{
Gizmos.DrawLine(transform.position, new Vector3(transform.position.x, transform.position.y - groundCheckDistance));
}//绘制线方便调试轴心到地面的距离
玩家控制的角色
player作为父需要挂载碰撞器,刚体,以及player的脚本。
animator作为子则负责挂载动画控制器,包括角色图片也是挂载在它上面的方便调控轴心。
2.今天学习制作攻击以及三段连击
首先制作三段攻击的动画
在动画控制器中添加如下参数:
isAttacking
comboCounter//记录连击数
isAttacking=true并且comboCounter=0过渡进入attack1动画
2,3同理。
当attack动画播完后再进入idle状态。
添加相应的代码:
[Header("Attack info")]
private bool isAttacking;
private int comboCount;//设置对应的参数
anim.SetBool("isAttacking", isAttacking);
anim.SetInteger("comboCounter", comboCount);//在AnimatorControllers()中添加
if(Input.GetKeyDown(KeyCode.Mouse0))
{
isAttacking = true;
}//在CheckInput()中添加,当按下鼠标左键时触发攻击状态。
public void AttackOver()
{
isAttacking = false;
}//攻击结束时的那一帧调用的函数,重置isAttacking为false
在Player子对象Animator上挂载新脚本。
脚本代码如下:
private Player player;
player = GetComponentInParent<Player>();//获取Player脚本,在初始化里调用
private void AnimationTrigle()
{
player.AttackOver();
}//动画触发器函数,用来调用动画帧事件需要的函数
实现连击攻击
首先在player脚本中添加参数
[SerializeField] private float comboTime=.3f;
private float comboTimeWindow;//第一次攻击后,如果一段时间不按下第二次攻击,则重置连击数
在update()中添加
comboTimeWindow -= Time.deltaTime;//所有计时器都需要的步骤
将Check Input()中关于攻击的函数提取出来
if(Input.GetKeyDown(KeyCode.Mouse0))
{
startAttackEvent();
}
private void startAttackEvent()
{
if(comboTimeWindow<0)
{
comboCount = 0;
}//计时器小于0时连击数归零
isAttacking = true;
comboTimeWindow = comboTime;//按下攻击键开始计时
}
完善A他tack Over()函数
public void AttackOver()
{
isAttacking = false;
comboCount++;//动画结束后连击数加1
if(comboCount>2)
{
comboCount = 0;
}//连击数超过2重置为0
}
在attack2和attack3的最后一帧事件绑定该函数。
这样就实现攻击的三段连击
3.修复bug
我们可以移动并攻击
private void Movement()
{
if(isAttacking)
{
rb.velocity = new Vector2(0, 0);
}//如果在攻击状态,则设置玩家速度为0
else if (dashTime > 0)
{
rb.velocity = new Vector2(xInput * dashSpeed, 0);
}
else
{
rb.velocity = new Vector2(xInput * moveSpeed, rb.velocity.y);
}
}
攻击时可以冲刺
private void dashAbility()
{
if(dashCooldownTimer<0&&!isAttacking)//不处于攻击时才可以冲刺
{
dashTime = dashDuration;
dashCooldownTimer = dashCooldown;
}
}
按下冲刺键时不按方向键则不动
else if (dashTime > 0)
{
rb.velocity = new Vector2(facingDir * dashSpeed, 0);//xInput改为facingDir(+1,-1)
}
角色不在地面上时不能攻击
在startAttackEvernt()中添加
if (!isGround)
return;//当角色不在地面上并且攻击时直接返回
总结
仅仅通过动画控制器来实现状态的转变相当的复杂,因为要考虑到各种情况,如果用有限状态机则会更加的方便管理。