最后一步是设置角色被击飞动画。
这里只编写角色被击飞,也就是当角色碰到尖刺后角色向运动方向的反方向运动,同时角色沿y轴蹦起。
声明三个变量,跟之前一样,一个是击飞计时器,一个是击飞长度,还有一个是击飞力度,当然击飞计时器是私有变量,因为它是会变化的。
public float knockBackLength, knockBackForce; // 声明玩家被击打后的长度和力度
private float knockBackCounter; // 声明玩家被击打计时器
这里需要注意的是,如果不设置角色的移动为0,那么角色在碰到尖刺后持续的往前走,因为当计时器大于0后,角色会因为惯性往前走,所以必须要设定玩家放弃按钮后,角色的x轴速度为0,y轴往上被击飞。
编写击飞函数,当碰到尖刺时,赋予击飞计时器击飞长度,角色的移动的y轴为向上移动,x轴不动,同时给角色击飞动画。
public void KnockBack(){
knockBackCounter = knockBackLength;
theRB.velocity = new Vector2(0f, knockBackForce);
anim.SetTrigger("hurt");
}
之后设置角色往x轴反方向移动,计时器是会变换的,当计时器等于0时是不受击飞限制,当计时器大于0时,它会慢慢变小。
knockBackCounter -= Time.deltaTime;
// 如果玩家没有翻转,向左移动,反之向右
if(!theSR.flipX){
theRB.velocity = new Vector2(-knockBackForce, theRB.velocity.y);
}
else{
theRB.velocity = new Vector2(knockBackForce, theRB.velocity.y);
}
这里要设置if函数,如果计时器小于0时,角色由玩家控制,大于0时会被击飞。
if(knockBackCounter < 0)
{
// 控制玩家移动
theRB.velocity = new Vector2(moveSpeed * Input.GetAxisRaw("Horizontal"), theRB.velocity.y);
// 绘制半圆,检测圆心是否接触地面
isGrounded = Physics2D.OverlapCircle(groundCheckPoint.position, .2f, whatIsGround);
// 声明在地面时可以两段跳,不在地面时不能两段跳
if(isGrounded){
canDoubleJump = true;
}
// 设置跳跃
if(Input.GetButtonDown("Jump")){
if(isGrounded){
theRB.velocity = new Vector2(theRB.velocity.x, jumpForce);
}
else{
if(canDoubleJump){
theRB.velocity = new Vector2(theRB.velocity.x, jumpForce);
canDoubleJump = false;
}
}
}
// 控制人物左右方向翻转
if(theRB.velocity.x < 0){
theSR.flipX = true; // flip按钮时unity界面上的按钮
}
else if(theRB.velocity.x > 0){
theSR.flipX = false;
}
}
else
{
knockBackCounter -= Time.deltaTime;
// 如果玩家没有翻转,向左移动,反之向右
if(!theSR.flipX){
theRB.velocity = new Vector2(-knockBackForce, theRB.velocity.y);
}
else{
theRB.velocity = new Vector2(knockBackForce, theRB.velocity.y);
}
}
// 获取动画组组件的MoveSpeed,将主角的移动速度赋予给它
anim.SetFloat("MoveSpeed", Mathf.Abs(theRB.velocity.x)); // math.Abs是取绝对值
anim.SetBool("isGrounded", isGrounded);
}
在PlayerHealthController中调用KnockBack函数
PlayerCont.instance.KnockBack();
同时调用ui界面函数
// 调用静态变量函数
UIController.instance.UpdateHealthDisplay();
完整函数代码如下
public void DealDamage(){
if(invincibleCounter <= 0)
{
currentHealth --;
// 当生命值为0时,玩家消失
if(currentHealth <= 0){
gameObject.SetActive(false);
}
else{
invincibleCounter = invincibleLenght;
theSR.color = new Color(theSR.color.r, theSR.color.g, theSR.color.b, 0.5f);
PlayerCont.instance.KnockBack();
}
// 调用静态变量函数
UIController.instance.UpdateHealthDisplay();
}
}
基于此,角色的ui系统和生命系统已经全部显示完毕,以下展示四个脚本的代码:
DamgePlayer脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DamgePlayer : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
void OnTriggerEnter2D(Collider2D other)
{
if(other.tag == "Player"){
/* Debug.Log("Hit"); // 测试阶段,检测角色是否触碰到陷阱
// 调用PlayerHealthController中的DealDamage函数
FindObjectOfType<PlayerHealthController>().DealDamage(); */
PlayerHealthController.instance.DealDamage(); // 调用PlayerHealthController中在DealDamage的静态变量
}
}
}
PlayerCont脚本代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerCont : MonoBehaviour
{
public static PlayerCont instance;
public float moveSpeed; // 声明移动数值
public Rigidbody2D theRB; // 声明刚体部件
public float jumpForce; // 声明跳跃强度
private bool isGrounded; // 声明bool值:是否在地面?
public Transform groundCheckPoint; // 获取玩家触碰地面的点位置
public LayerMask whatIsGround; // 声明地面图层
private bool canDoubleJump; // 声明bool值:是否两段跳?
private Animator anim; // 声明动画交互
private SpriteRenderer theSR; // 声明精灵操作对象
public float knockBackLength, knockBackForce; // 声明玩家被击打后的长度和力度
private float knockBackCounter; // 声明玩家被击打计时器
private void Awake()
{
instance = this;
}
void Start()
{
anim = GetComponent<Animator>(); // 获取动画组件
theSR = GetComponent<SpriteRenderer>(); // 获取玩家精灵对象组件
}
void Update()
{
if(knockBackCounter < 0)
{
// 控制玩家移动
theRB.velocity = new Vector2(moveSpeed * Input.GetAxisRaw("Horizontal"), theRB.velocity.y);
// 绘制半圆,检测圆心是否接触地面
isGrounded = Physics2D.OverlapCircle(groundCheckPoint.position, .2f, whatIsGround);
// 声明在地面时可以两段跳,不在地面时不能两段跳
if(isGrounded){
canDoubleJump = true;
}
// 设置跳跃
if(Input.GetButtonDown("Jump")){
if(isGrounded){
theRB.velocity = new Vector2(theRB.velocity.x, jumpForce);
}
else{
if(canDoubleJump){
theRB.velocity = new Vector2(theRB.velocity.x, jumpForce);
canDoubleJump = false;
}
}
}
// 控制人物左右方向翻转
if(theRB.velocity.x < 0){
theSR.flipX = true; // flip按钮时unity界面上的按钮
}
else if(theRB.velocity.x > 0){
theSR.flipX = false;
}
}
else
{
knockBackCounter -= Time.deltaTime;
// 如果玩家没有翻转,向左移动,反之向右
if(!theSR.flipX){
theRB.velocity = new Vector2(-knockBackForce, theRB.velocity.y);
}
else{
theRB.velocity = new Vector2(knockBackForce, theRB.velocity.y);
}
}
// 获取动画组组件的MoveSpeed,将主角的移动速度赋予给它
anim.SetFloat("MoveSpeed", Mathf.Abs(theRB.velocity.x)); // math.Abs是取绝对值
anim.SetBool("isGrounded", isGrounded);
}
public void KnockBack(){
knockBackCounter = knockBackLength;
// theRB.velocity = new Vector2(0f, theRB.velocity.y); // 玩家被击退时停止运动
theRB.velocity = new Vector2(0f, knockBackForce); // 给y值一个变量,玩家会出现向上蹦一下的效果
anim.SetTrigger("hurt");
}
}
PlayerHealthController脚本代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerHealthController : MonoBehaviour
{
public static PlayerHealthController instance; // 声明静态变量
public int currentHealth, maxHealth; // 声明当前生命和最大生命
public float invincibleLenght; // 声明玩家无敌时间变量
private float invincibleCounter; // 声明玩家无敌计数器
private SpriteRenderer theSR; // 声明玩家渲染变量
private void Awake()
{
instance = this;
}
void Start()
{
currentHealth = maxHealth;
theSR = GetComponent<SpriteRenderer>();
}
// Update is called once per frame
void Update()
{
if(invincibleCounter > 0){
invincibleCounter -= Time.deltaTime;
if(invincibleCounter <= 0){
theSR.color = new Color(theSR.color.r, theSR.color.g, theSR.color.b, 1f);
}
}
}
public void DealDamage(){
if(invincibleCounter <= 0)
{
currentHealth --;
// 当生命值为0时,玩家消失
if(currentHealth <= 0){
gameObject.SetActive(false);
}
else{
invincibleCounter = invincibleLenght;
theSR.color = new Color(theSR.color.r, theSR.color.g, theSR.color.b, 0.5f);
PlayerCont.instance.KnockBack();
}
// 调用静态变量函数
UIController.instance.UpdateHealthDisplay();
}
}
}
UIController脚本代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class UIController : MonoBehaviour
{
// 创建静态变量以访问其他脚本
public static UIController instance ;
public Image heart1, heart2, heart3; // 声明图片变量
public Sprite heartFull, heartHalf, heartEmpty; // 声明精灵变量
private void Awake(){
instance = this;
}
void Start()
{
}
void Update()
{
}
// 声明角色收到伤害后数据更新
public void UpdateHealthDisplay(){
switch(PlayerHealthController.instance.currentHealth){
case 6:
heart1.sprite = heartFull;
heart2.sprite = heartFull;
heart3.sprite = heartFull;
break;
case 5:
heart1.sprite = heartFull;
heart2.sprite = heartFull;
heart3.sprite = heartHalf;
break;
case 4:
heart1.sprite = heartFull;
heart2.sprite = heartFull;
heart3.sprite = heartEmpty;
break;
case 3:
heart1.sprite = heartFull;
heart2.sprite = heartHalf;
heart3.sprite = heartEmpty;
break;
case 2:
heart1.sprite = heartFull;
heart2.sprite = heartEmpty;
heart3.sprite = heartEmpty;
break;
case 1:
heart1.sprite = heartHalf;
heart2.sprite = heartEmpty;
heart3.sprite = heartEmpty;
break;
case 0:
heart1.sprite = heartEmpty;
heart2.sprite = heartEmpty;
heart3.sprite = heartEmpty;
break;
default:
heart1.sprite = heartEmpty;
heart2.sprite = heartEmpty;
heart3.sprite = heartEmpty;
break;
}
}
}
以下为unity界面的操作:
全部演示完毕,这一章内容较多,花费三天时间写完,写笔记对理解游戏运行逻辑有很大帮助。