前言
??本篇文章的內(nèi)容是實(shí)現(xiàn)實(shí)現(xiàn)角色的血量控制功能涨岁,在開(kāi)始實(shí)現(xiàn)之前漠酿,我們需要知道角色血量控制功能
的需求是什么冯凹。
角色血量控制功能的需求
- 角色頭上需要顯示一個(gè)跟隨角色移動(dòng)的
血量條
,實(shí)時(shí)顯示角色當(dāng)前的血量- 角色的
最大血量
可以任意修改- 角色
接觸怪物
時(shí)會(huì)受傷炒嘲,并播放受傷音效- 角色受傷時(shí)宇姚,除了
減少相應(yīng)的血量
,還需要有一個(gè)向后擊退
的效果- 為了避免角色被怪物卡住時(shí)摸吠,出現(xiàn)不斷受傷的問(wèn)題空凸,角色在受傷后,將在
短暫時(shí)間內(nèi)
獲得免傷效果- 當(dāng)角色
血量為0
時(shí)寸痢,角色死亡呀洲,播放死亡動(dòng)畫(huà),游戲結(jié)束
在弄清楚并整理好需求之后啼止,我們開(kāi)始一一實(shí)現(xiàn)這些功能道逗。
制作血量條
??首先,我們來(lái)制作血量條献烦。因?yàn)?code>血量條要一直跟隨移動(dòng)滓窍,所以我們不妨將血量條
作為Player
的子物體。在Player
下新建一個(gè)名為HealthBarDisplay
的空物體巩那,然后將Assets\Sprites\UI
下的Health
以及Health-bg
拖拽到HealthBarDisplay
下面吏夯。
它們的具體屬性如下:
HealthBarDisplay
:
Position
: (0, 0, 0)Health
:
Position
: (-0.8, 1.5, 0)Color
: (0, 255, 0, 255)Sorting Layer
: Character,Order In Layer
: 4Health-bg
:
Position
: (0, 1.5, 0)Sorting Layer
: Character,Order In Layer
: 4
??此時(shí),將Health
的Scale
屬性的X分量
緩慢從1減少至0
即横,我們可以看到血量條逐漸變短噪生。但為了避免角色在轉(zhuǎn)向時(shí),血量條
跟著翻轉(zhuǎn)东囚,我們還需要在PlayerController.cs
中加入以下代碼:
public class PlayerController : MonoBehaviour {
...
[Tooltip("顯示血量條的物體")]
public Transform HealthBarDisplay;
...
private void Flip() {
...
if(HealthBarDisplay != null) {
// 在角色轉(zhuǎn)向時(shí)翻轉(zhuǎn)HealthBarDisplay跺嗽,確保HealthBarDisplay不隨角色轉(zhuǎn)向而翻轉(zhuǎn)
HealthBarDisplay.localScale = Vector3.Scale(
new Vector3(-1, 1, 1),
HealthBarDisplay.localScale
);
} else {
Debug.LogWarning("請(qǐng)?jiān)O(shè)置HealthBarDisplay");
}
}
}
??接著,我們將HealthBarDisplay
拖拽到PlayerController.cs
下HealthBarDisplay
屬性的賦值框,然后Health
的Scale
設(shè)置為(0.5, 1, 1)
桨嫁,運(yùn)行游戲植兰,讓角色左右翻轉(zhuǎn),可以看到血量條
不隨著角色轉(zhuǎn)向而翻轉(zhuǎn)璃吧。停止運(yùn)行游戲楣导,將Health
的Scale
設(shè)置為(1, 1, 1)
,然后保存游戲畜挨,將我們所做的修改應(yīng)用至Player
對(duì)于的Prefab爷辙。
創(chuàng)建血量控制腳本
??我們?cè)?code>Assets\Scripts\Player下創(chuàng)建一個(gè)名為PlayerHealth.cs
的腳本。因?yàn)榻巧?code>最大血量需要能被修改朦促,角色受傷時(shí)不僅要播放受傷音效
,還要有向后擊退
的效果栓始,因此我們需要在PlayerHealth.cs
腳本中添加以下代碼:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerHealth : MonoBehaviour {
[Tooltip("角色的最大生命值")]
public float MaxHP = 100f;
[Tooltip("角色被怪物傷害時(shí)受到的擊退力大小")]
public float HurtForce = 100f;
[Tooltip("角色受傷后的免傷時(shí)間")]
public float FreeDamagePeriod = 0.35f;
[Tooltip("角色的受傷音效")]
public AudioClip[] OuchClips;
}
??添加完畢之后务冕,將PlayerHealth.cs
添加到物體Player
上,并將Assets\Audio\Player\Ouch
下的四個(gè)音頻文件拖動(dòng)到OuchClips
的賦值框幻赚,然后保存修改禀忆。
實(shí)現(xiàn)接觸怪物時(shí)受傷
??在Unity中,當(dāng)一個(gè)帶Collider2D
的物體和其他帶有Collider2D
的物體發(fā)生了碰撞時(shí)落恼,將會(huì)觸發(fā)OnCollisionEnter2D
箩退,我們可以通過(guò)OnCollisionEnter2D
這個(gè)函數(shù)來(lái)獲取物體的碰撞信息。那我們?nèi)绾闻袛嗯鲎驳奈矬w是怪物呢佳谦?答案是利用Unity提供的Tag
戴涝,通過(guò)設(shè)置Tag
這一屬性,我們可以方便地對(duì)物體進(jìn)行標(biāo)識(shí)钻蔑。選中AlienSlug
啥刻,點(diǎn)擊Tag
下拉框,然后點(diǎn)擊Add Tag
咪笑,創(chuàng)建一個(gè)名為Enemy
的Tag并將AlienSlug
和AlienShip
的Tag都設(shè)置為Enemy
可帽。最后,將AlienSlug
和AlienShip
的修改應(yīng)用至Prefab窗怒。
??添加完成之后映跟,我們?cè)?code>PlayerHealth.cs腳本中添加以下代碼:
private void OnCollisionEnter2D(Collision2D collision) {
//假如撞到怪物
if(collision.gameObject.tag == "Enemy") {
Debug.Log("Enemy");
}
}
??運(yùn)行游戲,控制人物移動(dòng)去接觸怪物扬虚,可以看到Console
輸出Enemy
字符串努隙,說(shuō)明已經(jīng)檢測(cè)到了角色和怪物發(fā)生碰撞。接下來(lái)孔轴,我們?cè)?code>PlayerHealth.cs加入角色受傷的代碼:
public class PlayerHealth : MonoBehaviour {
...
[Tooltip("角色受傷時(shí)減少的血量")]
public float DamageAmount = 10f;
[Tooltip("角色受傷后的免傷時(shí)間")]
public float FreeDamagePeriod = 0.35f;
// 角色當(dāng)前的血量
private float m_CurrentHP;
// 上一次受到傷害的時(shí)間
private float m_LastFreeDamageTime;
private void Start() {
// 初始化變量
m_CurrentHP = MaxHP;
m_LastFreeDamageTime = 0f;
}
private void OnCollisionEnter2D(Collision2D collision) {
// 判斷此時(shí)是否處于免傷狀態(tài)
if(Time.time > m_LastFreeDamageTime + FreeDamagePeriod) {
// 假如撞到怪物
if(collision.gameObject.tag == "Enemy") {
// 檢測(cè)當(dāng)前血量
if(m_CurrentHP > 0f) {
// 調(diào)用受傷函數(shù)
TakeDamage(collision.transform);
// 更新上次受傷害的時(shí)間
m_LastFreeDamageTime = Time.time;
} else {
// 角色死亡
}
}
}
}
// 受傷函數(shù)
public void TakeDamage(Transform enemy) {
// 給角色加上后退的力剃法,制造擊退效果
Vector3 hurtVector = transform.position - enemy.position + Vector3.up * 5f;
GetComponent<Rigidbody2D>().AddForce(hurtVector * HurtForce);
// 更新角色的生命值
m_CurrentHP -= DamageAmount;
// 更新生命條
Debug.Log(m_CurrentHP);
// 隨機(jī)播放音頻
int i = Random.Range(0, OuchClips.Length);
AudioSource.PlayClipAtPoint(OuchClips[i], transform.position);
}
}
??運(yùn)行游戲,控制人物移動(dòng)去接觸怪物,可以看到角色在觸碰怪物時(shí)贷洲,角色會(huì)受到一個(gè)擊退力的作用收厨,同時(shí)Console
窗口輸出當(dāng)前的生命值。
更新血量條的顯示
??接下來(lái)优构,我們要根據(jù)角色當(dāng)前的生命值來(lái)實(shí)時(shí)更新血量條的顯示诵叁,也就是我們需要根據(jù)角色當(dāng)前的生命值,來(lái)更新HealthBarDisplay
的子物體Health
的Scale
和Color
钦椭。我們?cè)?code>PlayerHealth.cs中加入以下代碼:
public class PlayerHealth : MonoBehaviour {
...
[Tooltip("血量條")]
public SpriteRenderer HealthSprite;
...
// 血量條的初始長(zhǎng)度
private Vector3 m_InitHealthScale;
private void Start() {
// 初始化變量
...
m_InitHealthScale = HealthSprite.transform.localScale;
}
//受傷函數(shù)
public void TakeDamage(Transform enemy) {
...
// 更新生命條
UpdateHealthBar();
...
}
private void UpdateHealthBar() {
if(HealthSprite != null) {
// 更新血量條顏色
HealthSprite.color = Color.Lerp(Color.green, Color.red, 1 - m_CurrentHP * 0.01f);
// 更新血量條長(zhǎng)度
HealthSprite.transform.localScale = Vector3.Scale(m_InitHealthScale, new Vector3(m_CurrentHP * 0.01f, 1, 1));
} else {
Debug.LogError("請(qǐng)?jiān)O(shè)置HealthSprite");
}
}
}
??將HealthBarDisplay
的子物體Health
拖動(dòng)到HealthSprite
的賦值框拧额,運(yùn)行游戲,控制人物移動(dòng)去接觸怪物彪腔,可以看到當(dāng)角色的生命值變化時(shí)侥锦,血量條
也隨之更新。
控制角色的死亡
??最后德挣,我們還需要控制角色的死亡恭垦。我們知道,當(dāng)角色死亡時(shí)格嗅,不能再和場(chǎng)景中的任何物體發(fā)生交互
番挺,玩家也不能再控制角色
。因此屯掖,我們?cè)?code>PlayerHealth.cs中加入以下代碼:
public class PlayerHealth : MonoBehaviour {
...
//受傷函數(shù)
public void TakeDamage(Transform enemy) {
...
// 檢測(cè)當(dāng)前血量
if(m_CurrentHP > 0f) {
...
} else {
// 角色死亡
Death();
}
...
}
private void Death() {
// 禁用碰撞體
Collider2D[] cols = GetComponents<Collider2D>();
foreach(Collider2D c in cols) {
c.enabled = false;
}
// 禁用腳本
GetComponent<PlayerController>().enabled = false;
// 播放死亡動(dòng)畫(huà)
GetComponent<Animator>().SetTrigger("Death");
}
}
??運(yùn)行游戲玄柏,控制人物移動(dòng)去接觸怪物,可以看到當(dāng)角色的生命值減少至0
時(shí)贴铜,角色播放死亡動(dòng)畫(huà)粪摘,且不與場(chǎng)景中的其他物體發(fā)生交互,玩家也不能再控制角色绍坝。將Player
的修改應(yīng)用至Prefab赶熟,并保存場(chǎng)景產(chǎn)生的修改。
PlayerHealth.cs的完整代碼
??PlayerHealth.cs
的完整代碼如下所示:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerHealth : MonoBehaviour {
[Tooltip("角色的最大生命值")]
public float MaxHP = 100f;
[Tooltip("角色被怪物傷害時(shí)受到的擊退力大小")]
public float HurtForce = 100f;
[Tooltip("角色的受傷音效")]
public AudioClip[] OuchClips;
[Tooltip("角色受傷時(shí)減少的血量")]
public float DamageAmount = 10f;
[Tooltip("角色受傷后的免傷時(shí)間")]
public float FreeDamagePeriod = 0.35f;
[Tooltip("血量條")]
public SpriteRenderer HealthSprite;
// 角色當(dāng)前的血量
private float m_CurrentHP;
// 上一次受到傷害的時(shí)間
private float m_LastFreeDamageTime;
// 血量條的初始長(zhǎng)度
private Vector3 m_InitHealthScale;
private void Start() {
// 初始化變量
m_CurrentHP = MaxHP;
m_LastFreeDamageTime = 0f;
m_InitHealthScale = HealthSprite.transform.localScale;
}
private void OnCollisionEnter2D(Collision2D collision) {
// 判斷此時(shí)是否處于免傷狀態(tài)
if(Time.time > m_LastFreeDamageTime + FreeDamagePeriod) {
// 假如撞到怪物
if(collision.gameObject.tag == "Enemy") {
// 檢測(cè)當(dāng)前血量
if(m_CurrentHP > 0f) {
// 調(diào)用受傷函數(shù)
TakeDamage(collision.transform);
// 更新上次受傷害的時(shí)間
m_LastFreeDamageTime = Time.time;
} else {
// 角色死亡
Death();
}
}
}
}
// 受傷函數(shù)
public void TakeDamage(Transform enemy) {
// 給角色加上后退的力陷嘴,制造擊退效果
Vector3 hurtVector = transform.position - enemy.position + Vector3.up * 5f;
GetComponent<Rigidbody2D>().AddForce(hurtVector * HurtForce);
// 更新角色的生命值
m_CurrentHP -= DamageAmount;
// 更新生命條
UpdateHealthBar();
// 隨機(jī)播放音頻
int i = Random.Range(0, OuchClips.Length);
AudioSource.PlayClipAtPoint(OuchClips[i], transform.position);
}
private void UpdateHealthBar() {
if(HealthSprite != null) {
// 更新血量條顏色
HealthSprite.color = Color.Lerp(Color.green, Color.red, 1 - m_CurrentHP * 0.01f);
// 更新血量條長(zhǎng)度
HealthSprite.transform.localScale = Vector3.Scale(m_InitHealthScale, new Vector3(m_CurrentHP * 0.01f, 1, 1));
} else {
Debug.LogError("請(qǐng)?jiān)O(shè)置HealthSprite");
}
}
private void Death() {
// 禁用碰撞體
Collider2D[] cols = GetComponents<Collider2D>();
foreach(Collider2D c in cols) {
c.enabled = false;
}
// 禁用腳本
GetComponent<PlayerController>().enabled = false;
// 播放死亡動(dòng)畫(huà)
GetComponent<Animator>().SetTrigger("Death");
}
}
后言
??至此映砖,我們已經(jīng)完成了角色的血量控制功能,本篇文章提到的數(shù)值參數(shù)都可以根據(jù)自己的喜好進(jìn)行調(diào)整灾挨。最后邑退,本篇文章所做的修改,可以在PotatoGloryTutorial這個(gè)倉(cāng)庫(kù)的essay7
分支下看到劳澄,讀者可以clone這個(gè)倉(cāng)庫(kù)到本地進(jìn)行查看地技。