补上之前缺的,都放在这一章
冲刺冷却时间的设置:
[SerializeField] private float dashCooldown;
private float dashCooldownTimer;
dashCooldownTimer -= Time.deltaTime;
if (Input.GetKeyDown(KeyCode.LeftShift))
{
DashAbility();
}
}
private void DashAbility()
{
if (dashCooldownTimer < 0)
{
dashCooldownTimer = dashCooldown;
dashTime = dashDuration;
}
private void Movement()
// 移动由 x 输入 和 移动速度控制
{
if (dashTime > 0)
{
rb.velocity = new Vector2(xInput * dashSpeed, 0);
}
else
{
rb.velocity = new Vector2(xInput * moveSpeed, rb.velocity.y);
}
}
Attacking animation
人物的攻击动作分为1 2 3,通过素材的拖拽将其设置为合理的值,加入新的动画器参数isattacjing,combocounter 并设置合理的连接状态,但是人物的动作结束将有一个退出时间。
[Header("Attack info")]
private bool isAttacking;
private int comboCounter;
public void AttackingOver()
{
isAttacking = false;
}
private void AnimatorControllers()
// 动画控制器控制动画的播出
{
bool isMoving = rb.velocity.x != 0;
anim.SetFloat("yVelocity", rb.velocity.y);
anim.SetBool("isMoving", isMoving);
// 1.调用 Animator = anim 组件中的 SetBool 方法,将 isMoving 参数的值设置到 Animator 组件中
// 2.后面的 isMoving 这个变量的值将被传递给 SetBool 方法,用于设置 "isMoving" 参数的值
// 要求:player或者其子对象Animitor中必须有一个 Animator 的组件,并且Animator Controller中必须有一个名为 isMoving 的布尔参数
anim.SetBool("isGrounded", isGrounded);
anim.SetBool("isDashing", dashTime > 0);
anim.SetBool("isAttacking", isAttacking);
anim.SetInteger("comnoCounter", comboCounter);
}
创建一个C#脚本,放置于Animitor之中,并在攻击动画的最后一帧之中加入一个事件,这个事件将调用我们刚刚创建的C#脚本之中的函数,以便我们在动作结束之后能够使得 isAttacking 变为flase,停止继续重复播放这个动作。
以下为C#脚本脚本之中的函数:
public class playerAnimEvents : MonoBehaviour
{
private player player;
void Start()
{
player = GetComponentInParent<player>();
}
private void AnimationTrigger()
{
player.AttackingOver();
}
这里我们需要注意:
1.public class player : MonoBehaviour 这行代码表明 player 继承自 MonoBehaviour 类。
2.private player player; 声明一个 后player 变量,为 前player 类型,那么 后player 这个变量能够引用和操作 前Player 类的实例。
3.player = GetComponentInParent<player>();
使用 GetComponentInParent<player>() 时,Unity会从当前GameObject开始,向上遍历其父级,直到找到一个包含 player 类型组件的GameObject。
一旦找到,它就返回这个组件的引用,你可以使用这个引用的返回值来访问 player 类中定义的属性和方法,如下:
player.AttackingOver();
P21 16.Attack combo
添加新的变量:↓
[Header("Attack info")]
[SerializeField] private float comboTime = .3f;
private float comboTimeWindow;
private bool isAttacking;
private int comboCounter;
每秒更新:↓
comboTimeWindow -= Time.deltaTime;
对AttackingOver()函数进行更新:↓
public void AttackingOver()
{
isAttacking = false;
comboCounter++;
if (comboCounter > 2)
{
comboCounter = 0;
}
}
对检查输入进行更新:↓
private void CheckInput()
{
xInput = Input.GetAxisRaw("Horizontal");
// 设置 x 的输入,获得移动的机会
if (Input.GetKeyDown(KeyCode.J))
{
StartAttackEvent();
}
并且创建新的函数:↓
private void StartAttackEvent()
{
if (comboTimeWindow < 0)
{
comboCounter = 0;
}
isAttacking = true;
comboTimeWindow = comboTime;
}
对移动函数进行更新:↓
private void Movement()
// 移动由 x 输入 和 移动速度控制
{
if (isAttacking)
{
rb.velocity = new Vector2(0, 0);
}
else if (dashTime > 0)
{
rb.velocity = new Vector2(facingDirection * dashSpeed, 0);
}
else
{
rb.velocity = new Vector2(xInput * moveSpeed, rb.velocity.y);
}
}
攻击动作的代码进行更新:↓
private void StartAttackEvent()
{
if (!isGrounded)
return;
更新动画控制函数:↓并且需要再attack2 attack3动画的最后一帧添加函数
private void AnimatorControllers()
anim.SetInteger("comboCounter", comboCounter);
这是这一部分总的代码总结,接下来我们将会通过状态机进行代码调试。
public class player : MonoBehaviour
{
private Rigidbody2D rb;
// rb目前处于未被初始化的状态,值为null,不能被调用,不指向任何对象。
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;
[Header("Attack info")]
[SerializeField] private float comboTime = .3f;
private float comboTimeWindow;
private bool isAttacking;
private int comboCounter;
private float xInput;
private int facingDirection = 1;
private bool facingRight = true;
[Header("Collision info")]
[SerializeField] private float groundCheckDistance;
[SerializeField] private LayerMask whatIsground;
private bool isGrounded;
void Start()
{
rb = GetComponent<Rigidbody2D>();
anim = GetComponentInChildren<Animator>();
}
void Update()
{
Movement();
CheckInput();
CollisionCheck();
dashTime -= Time.deltaTime;
dashCooldownTimer -= Time.deltaTime;
comboTimeWindow -= Time.deltaTime;
FlipController();
AnimatorControllers();
}
public void AttackingOver()
{
isAttacking = false;
comboCounter++;
if (comboCounter > 2)
{
comboCounter = 0;
}
}
private void CollisionCheck()
// 如果 groundCheckDistance 碰到地面就是true,没有碰到地面是 flase
{
isGrounded = Physics2D.Raycast(transform.position, Vector2.down, groundCheckDistance, whatIsground);
}
private void CheckInput()
{
xInput = Input.GetAxisRaw("Horizontal");
// 设置 x 的输入,获得移动的机会
if (Input.GetKeyDown(KeyCode.J))
{
StartAttackEvent();
}
if (Input.GetKeyDown(KeyCode.K))
// 如果输入 空格 就会进行跳跃
{
Jump();
}
if (Input.GetKeyDown(KeyCode.LeftShift))
{
DashAbility();
}
}
private void StartAttackEvent()
{
if (!isGrounded)
return;
if (comboTimeWindow < 0)
{
comboCounter = 0;
}
isAttacking = true;
comboTimeWindow = comboTime;
}
private void DashAbility()
{
if (dashCooldownTimer < 0 && !isAttacking)
{
dashCooldownTimer = dashCooldown;
dashTime = dashDuration;
}
}
private void Movement()
// 移动由 x 输入 和 移动速度控制
{
if (isAttacking)
{
rb.velocity = new Vector2(0, 0);
}
else if (dashTime > 0)
{
rb.velocity = new Vector2(facingDirection * dashSpeed, 0);
}
else
{
rb.velocity = new Vector2(xInput * moveSpeed, rb.velocity.y);
}
}
private void Jump()
// 跳跃由跳跃强度控制
{
if (isGrounded)
{
rb.velocity = new Vector2(rb.velocity.x, jumpForce);
}
}
private void AnimatorControllers()
// 动画控制器控制动画的播出
{
bool isMoving = rb.velocity.x != 0;
anim.SetFloat("yVelocity", rb.velocity.y);
anim.SetBool("isMoving", isMoving);
// 1.调用 Animator = anim 组件中的 SetBool 方法,将 isMoving 参数的值设置到 Animator 组件中
// 2.后面的 isMoving 这个变量的值将被传递给 SetBool 方法,用于设置 "isMoving" 参数的值
// 要求:player或者其子对象Animitor中必须有一个 Animator 的组件,并且Animator Controller中必须有一个名为 isMoving 的布尔参数
anim.SetBool("isGrounded", isGrounded);
anim.SetBool("isDashing", dashTime > 0);
anim.SetBool("isAttacking", isAttacking);
anim.SetInteger("comboCounter", comboCounter);
}
private void Flip()
// 进行角色翻转
{
facingDirection = facingDirection * -1;
facingRight = !facingRight;
transform.Rotate(0, 180, 0);
}
private void FlipController()
// 角色翻转控制器
{
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));
}
}
子体脚本如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class playerAnimEvents : MonoBehaviour
{
private player player;
void Start()
{
player = GetComponentInParent<player>();
}
private void AnimationTrigger()
{
player.AttackingOver();
}
}
// public class player : MonoBehaviour 这行代码表明 player 继承自 MonoBehaviour 类。
// private player player; 声明一个 后player 变量,为 前player 类型,那么 后player 这个变量能够引用和操作 前Player 类的实例
// player = GetComponentInParent<player>();
// 使用 GetComponentInParent<player>() 时,Unity会从当前GameObject开始,向上遍历其父级,直到找到一个包含 player 类型组件的GameObject。
// 一旦找到,它就返回这个组件的引用,你可以使用这个引用的返回值来访问 player 类中定义的属性和方法,如下:
// player.AttackingOver();