【Unity俯視角射擊】我們來做一個《元?dú)怛T士》的完整Demo(四)

作者:Yumir
哈嘍大家好,我是yumir。
慣例先上成果鏈接:https://connect.unity.com/mg/other/untitled-9864
本來本篇應(yīng)該是完結(jié)篇,主要是補(bǔ)充一下被攻擊方法和房間管理系統(tǒng)這兩個在游戲中占比不低的功能塊,稍微有點復(fù)雜。
但是寫的時候發(fā)現(xiàn)要寫的內(nèi)容篇幅實在是太長了,所以還是把兩個內(nèi)容分開寫了,不過我的游戲資源已經(jīng)打包好了就先放出來吧,你們應(yīng)該不會因為這樣就不追更了吧不會吧不會吧。
一、被攻擊方法
為什么我要單獨(dú)拎這一個方法出來講呢?
因為被攻擊既有玩家攻擊怪物又有怪物攻擊玩家,未來場景中有許多可以被攻擊的物體也有許多不能被攻擊的物體。
所以假設(shè)子彈碰撞到物體的時候,物體可能具有某個特殊的性質(zhì),也有可能沒有,而我需要一個方便快捷的方法。
我編寫了一個BeAttack接口:
interface BeAttack
{
??? void BeAttack(float data);
}
?
接著我讓所有可以“被攻擊”的游戲物體都繼承這個接口。
當(dāng)子彈的碰撞體碰撞到了物體,通過GetComponent的結(jié)果是否為空,可以判斷碰到的物體能不能被攻擊,如果能則運(yùn)行相應(yīng)的方法,反之則說明這個物體不是可以被攻擊的物體:
if (collision.GetComponent<BeAttack>() != null){
??? collision.GetComponent<BeAttack>().BeAttack(attack);
??? Die();
}
else if (collision.transform.tag == "Wall"){
??? Die();
}
?
二、玩家的被攻擊
繼承了BeAttack的游戲物體需要編寫相應(yīng)的方法邏輯,數(shù)據(jù)處理和畫面表現(xiàn)加起來量還是很大的。
設(shè)定中玩家是具有生命值和防御值的,所以在玩家被攻擊時需要分別判斷數(shù)值減去對應(yīng)的數(shù)據(jù),并且在玩家最后一次被攻擊之后一段時間開始恢復(fù)玩家的防御值,也就是脫離戰(zhàn)場之后恢復(fù)。
//脫離戰(zhàn)場&&防御值需要恢復(fù)&&恢復(fù)CD完成
if (Time.time - dpTiming1 > dpCD1 && dpRestore && Time.time - dpTiming2 > dpCD2){
?? dpTiming2 = Time.time;
?? playerDP.realValue++;
?? if (playerDP.realValue >= playerDP.maxValue){
?????? playerDP.realValue = playerDP.maxValue;
?????? dpRestore = false;
?? }
}
?
這里我用了一個布爾值dpRestore作為標(biāo)記,兩個浮點數(shù)作為計時器,一個是脫離戰(zhàn)場計時,一個是恢復(fù)防御值CD。
public void BeAttack(float data){
??? dpRestore = true;
??? dpTiming1 = Time.time;
??? if (playerDP.realValue == 0){
??????? playerHP.realValue -= data;
??? }
??? else if (playerDP.realValue < data){
??????? playerHP.realValue -= data - playerMP.realValue;
??????? playerDP.realValue = 0;
??? }
??? else{
???????? playerDP.realValue -= data;
??? }
??? if (playerHP.realValue <= 0){
??????? playerAnima.SetBool("die", true);
??????? isDie = true;
??????? GameManager.instance.GameOver();
??????? GetComponent<Collider2D>().enabled = false;
??? }
??? else{
??????? playerAnima.Play("BeAttack");
??????? GetComponent<CinemachineImpulseSource>().GenerateImpulse();
??????? GameManager.instance.ShowAttack(data, Camera.main.WorldToScreenPoint(transform.position + new Vector3(0, 1, 0)));
??? }
}
?
Over方法,并且將玩家的碰撞體取消激活。
除此之外,當(dāng)玩家被攻擊后血量大于零,則需要播放被攻擊的動畫,并且讓鏡頭抖動。
在添加Cinemachine的游戲物體“CM vcam1”上點擊AddExtension右邊的下拉菜單,選擇Cinemachine Impulse Listener,會出現(xiàn)下圖的組件,注意要勾選Use 2D Distance。

再在Player游戲物體上添加Cinemachine Impulse Source組件,設(shè)置方面因為我也不懂抖動的藝術(shù)所以直接選擇了一個默認(rèn)的Raw Signal,你可以試著自己調(diào)一個。

最后,除了被攻擊的動畫和鏡頭抖動這兩個效果,還需要一個UI上面的數(shù)值顯示,為此我制作了一個用于顯示被攻擊的UI預(yù)制體。
①新建一個Text,調(diào)整成合適的大小和顏色,并新建一個腳本用于控制該UI。
②編寫腳本,該預(yù)制體的腳本邏輯非常簡單,用DoTween做了一段動畫,結(jié)束時刪除自己即可。
public class BeAttackText : MonoBehaviour
{
??? void Start()
??? {
??????? transform.localScale = new Vector3(0, 0, 1);
??????? transform.DOScale(new Vector3(1, 1, 0), 0.4f);
??????? transform.DOMoveY(transform.position.y + 20, 0.5f);
??????? Destroy(gameObject,0.6f);
??? }
}
?
③在GameManager中控制在對應(yīng)的位置生成該預(yù)制體。
public void ShowAttack(float data, Vector3 pos){
??? GameObject textgo = Instantiate(beAttackText, pos, Quaternion.identity);
??? textgo.transform.SetParent(GameObject.Find("Canvas").transform);
??? textgo.GetComponent<Text>().text = "-" + data;
}
?
在BeAttack方法中調(diào)用這個ShowAttack方法,傳入屏幕映射值作為生成該預(yù)制體的位置。
三、怪物的被攻擊
雖然游戲中有四種怪,但是被攻擊時目前來講沒有什么差別,主要操作都是在父類里面寫的。
public virtual void BeAttack(float data){
??? hp -= data;
??? if (hp <= 0){
??????? monsterState = MonsterState.Die;
??????? GetComponent<Animator>().SetBool("die", true);
??????? GetComponent<Collider2D>().enabled = false;
??????? room.MonsterDie(this);
??????? for (int i = 0; i < coin; i++){
??????????? Instantiate(GameManager.instance.coinPre, transform.position, Quaternion.identity);
??????? }
??????? for (int i = 0; i < magic; i++){
??????????? Instantiate(GameManager.instance.mpPre, transform.position, Quaternion.identity);
??????? }
??? }
??? else{
??????? GetComponent<Animator>().Play("BeAttack");
??????? GameManager.instance.ShowAttack(data, Camera.main.WorldToScreenPoint(transform.position + new Vector3(0, 1, 0)));
??? }
}
?
基本結(jié)構(gòu)和玩家被攻擊方法也差不多,只是沒有防御值判斷。
其中不同的地方一個是怪物死亡之后需要通知自己所在的房間自己已經(jīng)被打敗了,在房間持有怪物L(fēng)ist中刪除自己。
另一個就是需要生成金幣和魔法石,而這些金幣和魔法石在玩家靠近時會自動飛向玩家。

private void Update(){
??? if (!isDrop){
??????? if (Vector3.Distance(transform.position, target) > 0.01)
??????????? transform.position = Vector3.Lerp(transform.position, target, Time.deltaTime * speed);
??????? else
??????????? isDrop = true;
??? }
??? else{
??????? if (Vector3.Distance(transform.position, player.position) <= getDistance)
??????????? transform.position = Vector3.Lerp(transform.position, player.position, Time.deltaTime * speed);
??????? if (Vector3.Distance(transform.position, player.position) <= 0.5){
??????????? player.GetComponent<PlayerControl>().AddCoin();
??????????? Destroy(gameObject);
??????? }
??? }
}
?

除此之外還可以制作一些木箱子花瓶之類的可以被打碎的道具,或者是打碎之后有物體溢出的箱子等等。
你可以設(shè)想一下如果不使用接口應(yīng)該如何實現(xiàn)這些邏輯判斷,或許你會想到更好的辦法?
游戲Demo源碼+素材網(wǎng)盤鏈接:https://pan.baidu.com/share/init?surl=FUGcRoh3Zw9SwKXe67DZiQ
提取碼:8bwo

歡迎加入游戲開發(fā)群歡樂攪基:1082025059
對游戲開發(fā)感興趣的童鞋可戳這里進(jìn)一步了解:http://www.levelpp.com/