如何做泛解析網(wǎng)站百度知道怎么賺錢(qián)
上期鏈接:Unity2D游戲制作入門(mén) | 11(之人物屬性及傷害計(jì)算)-CSDN博客
上期我們聊到了人物的自身屬性和受傷時(shí)的計(jì)算,我們先給人物和野豬掛上屬性和攻擊屬性的代碼,然后通過(guò)觸發(fā)器觸發(fā)受傷的事件。物體(人物也好敵人也行)受傷時(shí)通常是在被攻擊者的內(nèi)部進(jìn)行計(jì)算的(這是有好處的,比如使用人物更復(fù)雜的血量計(jì)算方式,我們?cè)谌宋飪?nèi)部讓它自己自動(dòng)計(jì)算就好了),我們使用關(guān)鍵詞this來(lái)將Attacker內(nèi)部的變量的訪問(wèn)權(quán)限賦給Character,然后我們又使用了計(jì)時(shí)器,讓被攻擊的物體在受到攻擊后可以有短暫的無(wú)敵時(shí)間,計(jì)時(shí)器的寫(xiě)法你還知道嗎?先創(chuàng)建三個(gè)變量一個(gè)布爾二個(gè)浮點(diǎn),創(chuàng)建新的能賦給人物無(wú)敵的函數(shù),然后判斷人物如果不如那么人物的無(wú)敵狀態(tài)就是True,然后賦給無(wú)敵時(shí)間(一個(gè)浮點(diǎn)變量我們是在Unity窗口手動(dòng)賦給的,另一個(gè)浮點(diǎn)變量是內(nèi)部進(jìn)行使用它,在函數(shù)判斷人物是否處在無(wú)敵時(shí)才用公共無(wú)敵時(shí)間賦給那個(gè)私有的浮點(diǎn)變量,這樣就在內(nèi)部激活了人物的無(wú)敵時(shí)間),我們?cè)趗pdate中計(jì)算人物無(wú)敵的倒計(jì)時(shí),到點(diǎn)了就切換人物無(wú)敵狀態(tài)為flase,然后在人物的Character中的TakeDamage函數(shù)中,我們判斷人物如果不是無(wú)敵則進(jìn)入扣血規(guī)則,然后如果人物的當(dāng)前血量足夠扣除一次血量則正常減血(如果人物血量為2敵人攻擊為5,那么人物直接判斷血量歸零),不然人物死亡,而無(wú)敵狀體的賦給與否全在人物扣血那部分功能中執(zhí)行,如果人物血量歸零那么將無(wú)法觸發(fā)無(wú)敵狀態(tài)??偟膩?lái)說(shuō),上次就是多了兩份通用的代碼文件,分別是物體的屬性和攻擊(僅對(duì)需要這些的物體有用,如果是場(chǎng)景的死物一般不給屬性如血量,也可以給看你設(shè)定,攻擊應(yīng)該是不要加上去的,除了陷阱啥的),雖然多了兩份代碼,我們還是要弄清兩份代碼之間的訪問(wèn)和運(yùn)行的邏輯,以及什么時(shí)候用它們。這期我們看看角色的 受傷和死亡的邏輯和動(dòng)畫(huà) 。代碼先下方:
public class PlayerAnimations : MonoBehaviour
{private Animator anim;//創(chuàng)建好這個(gè)組件變量后,如果不知道如何通關(guān)代碼控制組件,可以去看代碼手冊(cè)private Rigidbody2D rb;//這個(gè)2D不能忽略,不然不報(bào)錯(cuò)但是人物跑不起來(lái)。private Player_control playercon;//人物控制private physicsCheck physicsCheck;private void Awake(){physicsCheck = GetComponent<physicsCheck>();anim = GetComponent<Animator>();rb= GetComponent<Rigidbody2D>();playercon = GetComponent<Player_control>();}private void Update(){SetAnimatons();//每幀時(shí)時(shí)檢測(cè),判斷是否需要切換動(dòng)畫(huà)。}public void SetAnimatons()//需要做很多動(dòng)畫(huà)的切換,我們用這個(gè)函數(shù)來(lái)執(zhí)行所有的動(dòng)畫(huà)切換。{anim.SetBool("isGround", physicsCheck.isGround);anim.SetFloat("velocityX",math.abs(rb.velocity.x));anim.SetFloat("velocityY",rb.velocity.y);anim.SetBool("shift", playercon.isShift);anim.SetBool("isDead", playercon.isDead);}public void PlayerHurt(){anim.SetTrigger("hurt");}}
public class Player_control : MonoBehaviour
{public PlayerInputControl inputControl;public Vector2 inputDirection;//public變量表示一個(gè)公開(kāi)的方法,它代表我在Unity窗口中可以查看得到。private Rigidbody2D rb;private physicsCheck physicsCheck;//創(chuàng)建物理檢測(cè)變量private SpriteRenderer rbSprite;[Header("基本參數(shù)")]public float speed;public float walkSpeed;public float jumpForce;public float hurtForce;[Header("按鍵按下")]public bool isShift=false;[Header("狀態(tài)")]public bool isHurt;public bool isDead;private void Awake(){physicsCheck = GetComponent<physicsCheck>();rb= GetComponent<Rigidbody2D>();inputControl = new PlayerInputControl();inputControl.GamePlayer.Jump.started += Jump;//按下空格那一瞬間執(zhí)行事件Jump//inputControl.GamePlayer.Walk.performed += Walk;rbSprite = GetComponent<SpriteRenderer>();}private void OnEnable(){inputControl.Enable();}private void OnDisable(){inputControl.Disable();}private void Update(){inputDirection = inputControl.GamePlayer.Move.ReadValue<Vector2>();isShift= Keyboard.current.shiftKey.isPressed;//檢測(cè)按鍵}private void FixedUpdate()//固定頻率運(yùn)行,即0.02秒執(zhí)行一次。跟物理有關(guān)的放在這執(zhí)行{if (!isHurt){if (isShift && physicsCheck.isGround)Walk();elseMove();}}public void Move(){rb.velocity = new Vector2(inputDirection.x*speed*Time.deltaTime,rb.velocity.y);//人物翻轉(zhuǎn)if(inputDirection.x>0)rbSprite.flipX = false;if(inputDirection.x<0)rbSprite.flipX = true;}private void Jump(InputAction.CallbackContext obj){if (physicsCheck.isGround){rb.AddForce(jumpForce * transform.up, ForceMode2D.Impulse);//第一個(gè)參數(shù)表示給哪個(gè)方向施加力}}}private void Walk(){rb.velocity = new Vector2(inputDirection.x * walkSpeed * Time.deltaTime, rb.velocity.y);//人物翻轉(zhuǎn)if (inputDirection.x > 0)rbSprite.flipX = false;if (inputDirection.x < 0)rbSprite.flipX = true;}public void GetHurt(Transform attcker){isHurt = true;//受傷了//接下來(lái)執(zhí)行反彈,不過(guò)前提是先把人物的速度停下來(lái)。rb.velocity = Vector2.zero;//表示物體的xy軸速度全設(shè)置為了。//接下來(lái)計(jì)算受傷的方向,然后ta要朝反方向彈射。Vector2 dir=new Vector2((transform.position.x-attcker.position.x),0).normalized;/*這里對(duì)上行代碼進(jìn)行說(shuō)明,dir是direction方向的簡(jiǎn)寫(xiě)。計(jì)算方向我們就用人物坐標(biāo)減去野豬或是其他的attacker的坐標(biāo)對(duì)于x軸來(lái)說(shuō),如果人物在敵人左邊那么它應(yīng)該是負(fù)數(shù),所以我們根據(jù)正負(fù)數(shù)作為它的方向乘上它的反沖力hurtForce(現(xiàn)實(shí)加速的效果)。dir算完后,我們需要不是數(shù)值的大小,而是方向值(我們按下鍵盤(pán)時(shí)dir有-1和1兩種數(shù)值)。如果人物離野豬很遠(yuǎn),那么x的值會(huì)非常大,乘反彈力會(huì)更大反而離得近數(shù)值會(huì)很小,所以我們要把數(shù)值歸一化,即用到normalized的方法。*/rb.AddForce(dir*hurtForce,ForceMode2D.Impulse);//添加瞬時(shí)的力反彈人物}public void PlayerDead(){isDead = true;inputControl.GamePlayer.Disable();}
}
public class Character : MonoBehaviour
{[Header("基本屬性")]public float maxHp;//最大血量public float currentHp;//當(dāng)前血量[Header("受傷無(wú)敵")]public float invincibleTime;//無(wú)敵時(shí)間private float invincibleCounter;//一個(gè)計(jì)數(shù)器,內(nèi)部計(jì)算即可,不需要在窗口可以看得到public bool invincible;//為了能看見(jiàn)無(wú)敵,我們創(chuàng)建一個(gè)布爾值public UnityEvent<Transform> Ontakedamage;public UnityEvent Ondie;private void Start()//開(kāi)始游戲時(shí),要滿血{currentHp = maxHp;}private void Update(){if(invincible){invincibleCounter-=Time.deltaTime;if(invincibleCounter <= 0){invincible = false;}}}//受到傷害public void TakeDamage(Attack attcker){if (invincible)return;//Debug.Log(attcker.damage);if (currentHp - attcker.damage > 0)//血量健康才能減血{currentHp -= attcker.damage;//如果學(xué)過(guò)python,你會(huì)知道這行代碼等于currentHp = currentHp - attcker.damage;TriggerInvincible();//觸發(fā)無(wú)敵//執(zhí)行受傷Ontakedamage?.Invoke(attcker.transform);}else{currentHp=0;//觸發(fā)死亡Ondie?.Invoke();}}//受傷觸發(fā)無(wú)敵private void TriggerInvincible(){if(!invincible){invincible= true;invincibleCounter = invincibleTime;}}
}
public class HurtAninmation : StateMachineBehaviour
{override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex){animator.GetComponent<Player_control>().isHurt = false;}
}
正文:
玩家如果受到傷害,那么應(yīng)該有對(duì)應(yīng)的受傷動(dòng)畫(huà),甚至血量歸零時(shí)人物播放死亡的動(dòng)畫(huà)。找到我們之前的素材,我們可以先看看guid指導(dǎo)的動(dòng)畫(huà)內(nèi)容,找到受傷的內(nèi)容。我們先點(diǎn)擊player,然后創(chuàng)建人物的動(dòng)畫(huà)文件(一定放在人物動(dòng)畫(huà)管理的文件下,這樣方便找),然后創(chuàng)建人物的hurt動(dòng)畫(huà),用右邊的那四張圖片(我們之前切割好的),然后Samples采樣率我們調(diào)整到合適的數(shù)值(14的話感覺(jué)還是有點(diǎn)快了所以我選了9)。
死亡動(dòng)畫(huà)編號(hào)為42-53。
先將Animator中出現(xiàn)的我們剛剛創(chuàng)建的兩個(gè)動(dòng)畫(huà)刪去,我們注意到我們的人物在任何情況下都可能受傷,如我們跑步撞到野豬,在空中碰到敵人,這些都可能使人物受傷。如果人物受傷,應(yīng)該凌駕在所有動(dòng)畫(huà)之上(我們先前制作的所有動(dòng)畫(huà),如跳躍、閑置、跑步等),接下來(lái)我們創(chuàng)建新的方法。先不調(diào)用受傷動(dòng)畫(huà),來(lái)看一個(gè)有意思的方法(UP話)。玩家如果進(jìn)行受傷的狀態(tài), 那么玩家應(yīng)該有閃爍的狀態(tài)來(lái)表示玩家在這個(gè)階段是受傷無(wú)敵的狀態(tài)。我們?cè)谙聢D的這個(gè)地方創(chuàng)建新的動(dòng)畫(huà)的Layer圖層,注意這些圖層的名字下有一條線
我們點(diǎn)擊動(dòng)畫(huà)圖層右邊的齒輪可以發(fā)現(xiàn),有一個(gè)名為Weight權(quán)重的東西,這個(gè)權(quán)重會(huì)影響當(dāng)前的這一層的動(dòng)畫(huà)的播放,你可以理解為:它的優(yōu)先級(jí)是多少。下面的Blending為混合模式,Override是完全覆蓋的意思。我們先將HurtLayer動(dòng)畫(huà)圖層的權(quán)重拉到1,然后混合模式切換成Additive(疊加)模式即當(dāng)前層動(dòng)畫(huà)不會(huì)覆蓋之前層(BaseLayer)的動(dòng)畫(huà),在之前層的基礎(chǔ)上為它進(jìn)行添加。
我們?cè)谶@個(gè)新地方創(chuàng)建新的空的狀態(tài),然后創(chuàng)建新的受傷動(dòng)畫(huà)即bule_hurt2。在這里我們不調(diào)用任何的圖片,現(xiàn)在錄制我們的動(dòng)畫(huà)片段。
這個(gè)地方關(guān)鍵,我們?nèi)サ饺宋锏挠疫叺慕M件Sprite Renderer,看到Color我們修改它RGB的通道,也可以修改它的阿爾法值,怎么說(shuō)呢就是修改物體的透明程度(1的話就顯示,0就隱身了),如果是改RGB中的值人物就會(huì)變色如變成紅色等。然后我們?cè)?strong>動(dòng)畫(huà)管理下的Add Property,找到對(duì)應(yīng)組件的功能,即我們找到Sprite Renderer下的控制color的選項(xiàng)即可。
我們先在BaseLayer中刪除我們創(chuàng)建的bule_hurt2動(dòng)畫(huà),然后添加到HurtLayer中去。對(duì)了,hutr2一個(gè)六幀,采樣率設(shè)置6即可,然后可以可以拖拽右邊的6個(gè)點(diǎn)到1:0的位置,然后再0:2、 0:4 、1:0的位置分別設(shè)置阿爾法值為0.5、1、0.5。弄好后鏈接一根線到bule_hurt2,再創(chuàng)建一個(gè)新的參數(shù)為trigger類(lèi)型的名稱(chēng)取hurt(你會(huì)注意到它右邊是圓形的圖案這是觸發(fā)器)。在觸發(fā)hurt2的條件中添加hurt參數(shù)這個(gè)條件(其他的條件如退出時(shí)間、轉(zhuǎn)換時(shí)間都弄為無(wú)或是0,和之前的那些動(dòng)畫(huà)差不多就行),一旦hurt(左邊紅框的圓圈內(nèi))被勾選了即啟動(dòng)了,那么會(huì)自動(dòng)播放這個(gè)受傷的動(dòng)畫(huà)。
如果要由播放受傷的動(dòng)畫(huà)播放完了,我們需要回到之間的動(dòng)畫(huà)所以設(shè)置受傷播放完后回到初始的狀態(tài),這里返回的條件就設(shè)置為無(wú),即什么都不加。
我們進(jìn)入到代碼把它測(cè)試一下,我們打開(kāi)代碼PlayerAnimations。trigger的方法我們不能在update中執(zhí)行,它是單次執(zhí)行的函數(shù),所以創(chuàng)建一個(gè)新的函數(shù)。
....
public void PlayerHurt(){anim.SetTrigger("hurt");}
那么我們時(shí)候執(zhí)行調(diào)用PlayerHurt()呢?應(yīng)該在Character代碼中去執(zhí)行:受傷我們需要播放人物的動(dòng)畫(huà),以及人物被彈走不能再往前走(當(dāng)然,我們受傷被彈走一般是不允許進(jìn)行鍵盤(pán)操作的),甚至我們需要播放受傷的聲音。這里又很關(guān)鍵了,試想一下,除了剛剛提到要被彈飛等事件,也可能還要調(diào)整一下我們ui的顯示,就在受傷后有很多事情需要去執(zhí)行。想讓一次受傷去觸發(fā)其他各種各樣的代碼或以后做UI都要來(lái)執(zhí)行對(duì)應(yīng)的方法,我們需要使用Unity的事件的方式。
public void TakeDamage(Attack attcker){if (invincible)return;//Debug.Log(attcker.damage);if (currentHp - attcker.damage > 0)//血量健康才能減血{currentHp -= attcker.damage;//如果學(xué)過(guò)python,你會(huì)知道這行代碼等于currentHp = currentHp - attcker.damage;TriggerInvincible();//執(zhí)行受傷}else{currentHp=0;//觸發(fā)死亡}}
接上面的內(nèi)容,我們需要調(diào)用Unity的命名空間,接下來(lái)創(chuàng)建這些事件,我們先創(chuàng)建一個(gè)類(lèi)型為UnityEvent的變量(現(xiàn)在要先弄受傷的事件),然后再public UnityEvent<>
的尖括號(hào)中,我們可以傳入一些參數(shù),例如在受傷時(shí)希望每次受傷都能按受傷的這個(gè)方向的反方向被擊退,無(wú)論敵人還是人物都應(yīng)該是這樣的。傳遞方向進(jìn)去,其實(shí)也就是坐標(biāo),那么我們Transform類(lèi)型的組件傳進(jìn)去。
using UnityEngine.Events;
public UnityEvent<Transform> Ontakedamage;
可能上面代碼看得不是很懂,返回unity窗口就能直觀地看到了(記得先保存寫(xiě)完的代碼):Character組件就多了事情的內(nèi)容了。這個(gè)東西是unity自帶的,它的好處是以后我們學(xué)習(xí)UI時(shí),點(diǎn)擊的時(shí)候也會(huì)有這種事件的處理方式。在這個(gè)事件當(dāng)中,我們可以點(diǎn)擊加號(hào)來(lái)添加各種各樣的函數(shù)方法,各種各樣的功能。那么在一個(gè)事件觸發(fā)時(shí)候,這里可以執(zhí)行所有你添加進(jìn)去的方法。如我們之前提到的人物受傷,如果觸發(fā)了很可能我們要調(diào)用ui、播放音樂(lè)、執(zhí)行方法,播放動(dòng)畫(huà),所有的內(nèi)容都可以放在這里。
應(yīng)用舉例,我們把playerAnimations拖拽下去,我們找到代碼中的playerHurt的函數(shù)方法,然后可以選擇在何時(shí)運(yùn)行它,如在游戲運(yùn)行或是編輯器里時(shí),這里我們選擇只在游戲運(yùn)行時(shí)執(zhí)行。
接下來(lái)我們會(huì)到代碼文件Character中,回看這行代碼public UnityEvent<Transform> Ontakedamage;
,**這個(gè)關(guān)于事件Ontakedamage,剛才的加號(hào)是代表我們把各種各樣的方法注冊(cè)到這個(gè)事件當(dāng)中,接下來(lái)我們需要把這個(gè)事件啟動(dòng)起來(lái),它其實(shí)有一個(gè)固定的寫(xiě)法。**注意新寫(xiě)入的代碼Ontakedamage?.Invoke(attcker.transform);
問(wèn)號(hào)代表判斷一下有沒(méi)有任何方法添加進(jìn)來(lái),很可能之前的列表是空的,那我們就可能報(bào)錯(cuò)了,所以加上問(wèn)號(hào)。**后面接點(diǎn)Invoke表示啟動(dòng)當(dāng)前事件的內(nèi)容(不要忘記打上括號(hào))。**然后因?yàn)閯倓偽覀儎?chuàng)建時(shí)有設(shè)置了參數(shù),所以需要傳一個(gè)transform進(jìn)去,誰(shuí)攻擊我我就朝反方向移動(dòng),那么我們就要獲得attcker它的transform了。這樣,我們就實(shí)現(xiàn)了該函數(shù)(TakeDamage(Attack attcker))受傷數(shù)值減少、觸發(fā)無(wú)敵,然后執(zhí)行所以注冊(cè)過(guò)來(lái)的受傷的函數(shù)方法。(目前我們只注冊(cè)了受傷時(shí)播放閃爍的動(dòng)畫(huà))
public void TakeDamage(Attack attcker){if (invincible)return;//Debug.Log(attcker.damage);if (currentHp - attcker.damage > 0)//血量健康才能減血{currentHp -= attcker.damage;//如果學(xué)過(guò)python,你會(huì)知道這行代碼等于currentHp = currentHp - attcker.damage;//觸發(fā)無(wú)敵TriggerInvincible();//執(zhí)行受傷Ontakedamage?.Invoke(attcker.transform);}
運(yùn)行游戲,如果人物經(jīng)過(guò)野豬會(huì)有一個(gè)人物閃爍的效果,**我經(jīng)過(guò)測(cè)試發(fā)現(xiàn):如果把野豬的Is trigger給關(guān)掉或膠囊觸發(fā)器關(guān)閉不使用,那么人物經(jīng)過(guò)野豬,不管是頂著它走還是穿過(guò)它都不能能觸發(fā)受傷閃爍的效果。**這樣我們就學(xué)習(xí)了如何觸發(fā)事件來(lái)執(zhí)行一些方法。接下來(lái)我們給人物受傷切換回我們之前想要的受傷動(dòng)畫(huà),然后按照我們之前給沒(méi)有素材的動(dòng)畫(huà)添加受傷會(huì)變紅閃爍的效果即可,然后這樣我們就有了紅色受傷的動(dòng)畫(huà),那么我們還需要讓人物反彈回去。
我們受傷被擊退的事件應(yīng)該在人物控制的代碼中被執(zhí)行,因?yàn)榭刂迫宋锏姆较蛞淮驍?#xff0c;如果一直向前跑但是受傷后需要被彈開(kāi)。所以我們?cè)趐layer_control里面創(chuàng)建新的方法,來(lái)執(zhí)行一個(gè)人物反彈的效果。我們需要設(shè)置一下玩家的狀態(tài),受傷就要阻止我們其他的移動(dòng),也要反彈一段距離。所以需要?jiǎng)?chuàng)建是否受傷的布爾值類(lèi)型的變量isHurt。然后反彈的時(shí)候希望有一個(gè)力把它彈開(kāi)一段距離。
public bool isHurt;public float hurtForce;
....private void FixedUpdate()//固定頻率運(yùn)行,即0.02秒執(zhí)行一次。跟物理有關(guān)的放在這執(zhí)行{if (!isHurt)//沒(méi)有進(jìn)入受傷狀態(tài)才能走動(dòng){if (isShift && physicsCheck.isGround)Walk();elseMove();}}
.....public void GetHurt(Transform attcker){isHurt = true;//受傷了//接下來(lái)執(zhí)行反彈,不過(guò)前提是先把人物的速度停下來(lái)。rb.velocity = Vector2.zero;//表示物體的xy軸速度全設(shè)置為了。//接下來(lái)計(jì)算受傷的方向,然后ta要朝反方向彈射。Vector2 dir=new Vector2((transform.position.x-attcker.position.x),0).normalized;/*這里對(duì)上行代碼進(jìn)行說(shuō)明,dir是direction方向的簡(jiǎn)寫(xiě)。計(jì)算方向我們就用人物坐標(biāo)減去野豬或是其他的attacker的坐標(biāo)對(duì)于x軸來(lái)說(shuō),如果人物在敵人左邊那么它應(yīng)該是負(fù)數(shù),所以我們根據(jù)正負(fù)數(shù)作為它的方向乘上它的反沖力hurtForce(現(xiàn)實(shí)加速的效果)。dir算完后,我們需要不是數(shù)值的大小,而是方向值(我們按下鍵盤(pán)時(shí)dir有-1和1兩種數(shù)值)。如果人物離野豬很遠(yuǎn),那么x 的值會(huì)非常大,乘反彈力會(huì)更大,反而離得近數(shù)值會(huì)很小,所以我們要把數(shù)值歸一化,即用到normalized的 方法。 */rb.AddForce(dir*hurtForce,ForceMode2D.Impulse);//添加瞬時(shí)的力反彈人物}
保存上面寫(xiě)好的代碼會(huì)回到unity的窗口,我們需要把我們寫(xiě)好的人物受傷的函數(shù)方法添加到Ontakedamage這個(gè)事件中去,然后我們給人物被攻擊的力改成8,我們發(fā)現(xiàn)人物被彈走后停不下來(lái),原因是isHurt一直沒(méi)用變回flase。
我們留意到animator中,我們看到受傷動(dòng)畫(huà)狀態(tài)的右邊,即我們可以添加一個(gè)代碼,在這個(gè)動(dòng)畫(huà)執(zhí)行的過(guò)程中,做一些代碼的變化,我們先添加一個(gè)叫HurtAninmation的代碼。輸入完名稱(chēng)后,按下回車(chē)鍵,然后它會(huì)為你創(chuàng)建這個(gè)新的代碼,不過(guò)代碼被放在了我們project下的Assets下了,和一些文件目錄同級(jí)了。
雙擊打開(kāi)代碼,我們可以看到這是一個(gè)unity自動(dòng)為我們寫(xiě)好的模板,我們可以直接拿來(lái)用。它繼承了**StateMachineBehaviour,也就是狀態(tài)機(jī)的行為,**在做敵人我們也會(huì)單獨(dú)來(lái)寫(xiě)。取消方法前面的注釋就可以用了。第一方法是,當(dāng)進(jìn)去這個(gè)狀態(tài)時(shí)執(zhí)行,第二方法是當(dāng)在執(zhí)行這個(gè)動(dòng)畫(huà)的過(guò)程中我執(zhí)行函數(shù)方法(如果這個(gè)狀態(tài)也可稱(chēng)為動(dòng)畫(huà)時(shí)間比較久那么函數(shù)一直執(zhí)行),第三個(gè)是當(dāng)狀態(tài)退出后,我們執(zhí)行某某方法。So,當(dāng)我們受傷播完并退出后,我們更改我們的人物isHurt的狀態(tài)為flase即可。
override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex){animator.GetComponent<Player_control>().isHurt = false;}
注意我們用Ontakedamage事件的這個(gè)方法,我們即播放了動(dòng)畫(huà),又可以播放腳本里的函數(shù)的邏輯。最后一個(gè)我們要做的就是人物的死亡,在任何狀態(tài)下我們的人物都會(huì)死亡,所以我們到動(dòng)畫(huà)控制器中添加死亡的動(dòng)畫(huà),然后HurtLayer的動(dòng)畫(huà)圖層我們把混合的模式改為Override覆蓋。我們?cè)赑arameter中添加死亡參數(shù)進(jìn)行判斷isDead(布爾值)。然后給Any State連一根線到死亡動(dòng)畫(huà)中去,條件為isDead為true時(shí),其他要么無(wú)要么為0。
注意一下,我們的人物是否會(huì)一直進(jìn)入死亡狀態(tài)呢?不一定,如果我們重新開(kāi)始游戲,那么人物要刷新?tīng)顟B(tài)并退出死亡的動(dòng)畫(huà)(退出的條件大家應(yīng)該都熟練了,isDead為flse其他要么無(wú)要么為0)。
接下來(lái)把死亡動(dòng)畫(huà)鏈接到函數(shù),我們需要在Character代碼中添加死亡的事件方法。因?yàn)樵谌宋锼劳鰰r(shí),可能也要執(zhí)行很多的事情,比如跳出UI的面板,除了通知你Game Over,還要通知你很多的信息,比如通知你的敵人不要繼續(xù)攻擊,人物不要再移動(dòng)了。但是這次我們不需要傳遞任何參數(shù),就是人物只是死亡。
//#Characte.cspublic UnityEvent Ondie;//新加入,對(duì)應(yīng)代碼第22行
..........
public void TakeDamage(Attack attcker){if (invincible)return;//Debug.Log(attcker.damage);if (currentHp - attcker.damage > 0)//血量健康才能減血{currentHp -= attcker.damage;//如果學(xué)過(guò)python,你會(huì)知道這行代碼等于currentHp = currentHp - attcker.damage;TriggerInvincible();//觸發(fā)無(wú)敵//執(zhí)行受傷Ontakedamage?.Invoke(attcker.transform);}else{currentHp=0;//觸發(fā)死亡Ondie?.Invoke();}}
那么人物的死亡是一個(gè)布爾值,所以它不是單次執(zhí)行的,沒(méi)有必要單獨(dú)去寫(xiě),我們直接把狀態(tài)連接到Animation當(dāng)中即可,并通過(guò)人物控制停止我們所有的人物操作如移動(dòng)。
//#Player_control.cs
public bool isDead;
...
public void PlayerDead(){isDead = true;inputControl.GamePlayer.Disable();//我們之前輸入系統(tǒng)的人物操作相關(guān)的內(nèi)容。我們只關(guān)閉人物的操作,正常的ui操作還要保留的。}
然后在PlaterAnimation中把狀態(tài)連接起來(lái)。
public void SetAnimatons()//需要做很多動(dòng)畫(huà)的切換,我們用這個(gè)函數(shù)來(lái)執(zhí)行所有的動(dòng)畫(huà)切換。{........anim.SetBool("isDead", playercon.isDead);}
然后設(shè)置人物的血量為6進(jìn)行死亡測(cè)試,不過(guò)你會(huì)發(fā)現(xiàn)人物會(huì)反復(fù)死亡,這是因?yàn)槿宋锏乃劳鰟?dòng)畫(huà)沒(méi)有設(shè)置為只播放一次。把下圖人物死亡的動(dòng)畫(huà)的循環(huán)關(guān)去即可,讓它單次執(zhí)行。一般來(lái)說(shuō)很多動(dòng)畫(huà)都需要單次播放的,如果不連接到Any State中,它都不會(huì)有這樣的問(wèn)題,如果你鏈接了Any State且只希望播放一次,一定要取消勾選Loop Time。
總結(jié):
? 添加事件的方式特別的方便,它能幫助我們添加各種函數(shù)去執(zhí)行一些事件,我們雖然只做了兩個(gè)動(dòng)畫(huà),即人物的受傷和死亡卻操作了很久說(shuō)明做游戲也不是很容易的。這節(jié)可能沒(méi)那么好理解,記得多多操作并理解這些步驟是為什么要這么做的,就連我自己都要反復(fù)看才能理出一點(diǎn)頭緒。
? 為了能記下Unity一些的特殊用法,還是一些來(lái)稍微回味一下這期的內(nèi)容吧。在創(chuàng)建人物受傷和死亡的動(dòng)畫(huà)之前,我們先是創(chuàng)建了人物被觸發(fā)后能夠閃爍的動(dòng)畫(huà),這樣的動(dòng)畫(huà)我們是通過(guò)組件Sprite Renderer里面的color功能區(qū)實(shí)現(xiàn)的。我們創(chuàng)建了一個(gè)新的動(dòng)畫(huà)圖層,修改它顯示的權(quán)重并用于疊加到我們之前的動(dòng)畫(huà)上。觸發(fā)受傷的動(dòng)畫(huà)是通過(guò)觸發(fā)器來(lái)實(shí)現(xiàn)的,所以只需要單次執(zhí)行,我們不會(huì)把它放入到Update函數(shù)中去,我們將在PlayerAnimations類(lèi)中的函數(shù)PlayerHurt()放到Character代碼中去執(zhí)行,因?yàn)槿宋餀z測(cè)到血量減少后應(yīng)該就執(zhí)行人物受傷的動(dòng)畫(huà),這很符合直覺(jué)。然后因?yàn)槿绻覀兊娜宋锸軅?#xff0c;我們需要做很多的事情,如播放音樂(lè)、播放受傷動(dòng)畫(huà)等,所以我們使用了命名空間UnityEngine.Events,來(lái)給物體添加一些事件的功能,而且我們?cè)赨nity窗口的物體的組件上也能看到在對(duì)應(yīng)的代碼多出了添加一些注冊(cè)事件的功能,然后我們需要在代碼的對(duì)應(yīng)位置啟動(dòng)某某事件固定格式為:事件名稱(chēng).Invoke(參數(shù))Ontakedamage?.Invoke(attcker.transform);
。
? 運(yùn)行游戲后發(fā)現(xiàn)觸發(fā)人物的受傷動(dòng)畫(huà)是正常的,接下來(lái)我們需要人物在進(jìn)去受傷動(dòng)畫(huà)時(shí),人物不應(yīng)該能執(zhí)行一些操作,如果移動(dòng)跑步等,并且受傷被擊退的事件應(yīng)該在人物控制的代碼中被執(zhí)行,即現(xiàn)在我們需要確定的事件就是,人物受傷不能操作,且要被擊退,而播放動(dòng)畫(huà)是在人物屬性的代碼中執(zhí)行的,因?yàn)橐坏┛垩覀儾挪シ攀軅麆?dòng)畫(huà)。在人物控制的代碼文件中,我們創(chuàng)建了新的函數(shù)方法為GetHurt(Transform attcker),然后我們創(chuàng)建一個(gè)布爾值變量判斷受傷,和一個(gè)受傷應(yīng)該能彈飛人物的力的變量。然后該事件函數(shù)(人物被彈飛,且不能操作)的調(diào)用我們放入到剛剛Character的代碼中去注冊(cè)到事件Ontakedamege中去,這個(gè)地方就是如果在Character代碼中如果事件Ontakedamage被啟動(dòng)了,相應(yīng)的人物動(dòng)畫(huà)代碼中的觸發(fā)的參數(shù)hurt將被激活,然后執(zhí)行人物受傷的動(dòng)畫(huà),再接下來(lái)就是人物控制代碼中的函數(shù)GetHurt(Transform attcker)被調(diào)用了,這是直接更新人物的受傷狀態(tài)isHurt為true,那么,人物進(jìn)入不能操作且被擊飛的狀態(tài),這個(gè)持續(xù)的時(shí)間是是無(wú)限時(shí)間,因?yàn)闆](méi)有設(shè)定何時(shí)把isHurt改為flase,在人物控制的FixedUpdate()函數(shù)中isHurt還是true,達(dá)不到我們能操作的條件,所以我們?cè)谑軅麆?dòng)畫(huà)退出的時(shí)間即受傷動(dòng)畫(huà)播放完后,我們通過(guò)在受傷的State中創(chuàng)建新的代碼來(lái)在退出動(dòng)畫(huà)時(shí)更新我們?nèi)宋锸軅臓顟B(tài),該代碼文件的關(guān)鍵代碼為:animator.GetComponent<Player_control>().isHurt = false;
,即我們獲取了人物控制的代碼,在動(dòng)畫(huà)完全退出后我們把isHurt的狀態(tài)改為flase,這樣在我們?nèi)宋锉粨麸w后我們又獲得了人物操作的控制權(quán)(是的,人物在受傷獲得操控權(quán)后又飄了…)。然后就是注入人物死亡的動(dòng)畫(huà),當(dāng)人物的狀態(tài)isDead為true時(shí),我們讓人物進(jìn)入死亡并且所有操作都要停止,但是也只是限制人物的操作,像其他的如UI的操作我們沒(méi)有停止,因?yàn)槟鞘墙Y(jié)算界面。我們?cè)谌宋锟刂频拇a文件中創(chuàng)建新的函數(shù)為PlayerDead(),人物死亡則isDead為true,而且關(guān)閉人物相關(guān)操作,并在人物動(dòng)畫(huà)中時(shí)時(shí)檢測(cè)人物死亡因?yàn)橹亻_(kāi)游戲人物會(huì)滿血達(dá)不到死亡的條件。
? 大致的流程就是這樣了,不知道我有沒(méi)有說(shuō)清楚?唉這個(gè)部分就是比較混亂的,多看多想吧。
未盡事宜以后可能會(huì)補(bǔ)充。
-------------------------結(jié)束線