你的生命是人類的貨幣——用Unity實(shí)現(xiàn)《Seed》(二)

作者:Yumir
哈嘍大家好我是Yumir。
上次畫了一個(gè)世界樹動(dòng)畫【封面圖】,這次我畫了一個(gè)歌唱家被攻擊時(shí)對(duì)攻擊者進(jìn)行迷惑的動(dòng)畫,非常的鬼畜,調(diào)動(dòng)畫的時(shí)候正好在聽Doubt & Trust,興致上來順手做了個(gè)視頻。
這么鬼畜的場景我一定要和大家一起分享。
【魔性歌唱家】做游戲的時(shí)候一定要聽歌 ‐ Made with Clipchamp
咳,安靜安靜,我們是正經(jīng)的游戲復(fù)刻分享。
今天我們繼續(xù)Seed的制作,在上一篇文章中我們對(duì)該游戲做了簡單的分析,以及部分素材的制作,現(xiàn)在我們繼續(xù)實(shí)現(xiàn)更多的功能。
上一篇的傳送門:你的生命是人類的貨幣——用Unity實(shí)現(xiàn)《Seed》(一)
--------------------------------肅靜分割線-------------------------------------------
首先我們今天的目標(biāo)有:
1、種樹,并且樹之間要有層級(jí)關(guān)系。


3、植物預(yù)制體的動(dòng)畫狀態(tài)機(jī)以及各種邏輯等等...
總之我們今天要把植物相關(guān)的功能都做完就是了- -
------------------------------------開始制作!-----------------------------------------
游戲的開始我們需要種下第一顆樹,那么我們需要實(shí)現(xiàn)種下第一個(gè)樹的邏輯。在上一篇文章中已經(jīng)制作了初始樹的預(yù)制體,初始樹可以直接在沙地(反正就是不能種樹的地)上種植,之后的樹則只能在綠地上種植,所以首先我們要添加三個(gè)tag用于區(qū)分:沙地、綠地和樹蔭。

種樹的邏輯基本上就是發(fā)射射線,然后在碰撞點(diǎn)生成一個(gè)對(duì)應(yīng)的預(yù)制體(這部分游戲邏輯慣例是在游戲管理器(GameManager)上實(shí)現(xiàn))。
//射線碰撞檢測 hit = Physics2D.Raycast(Camera.main.ScreenToWorldPoint(Input.mousePosition), Vector2.zero);//生成預(yù)制體Instantiate(treePrefab, new Vector3(hit.point.x, hit.point.y, hit.point.y), Quaternion.identity);
是我們在場景中種下了我們的第一棵樹,此時(shí)我們需要將UI中世界樹種子的按鈕隱藏,顯示普通樹的種子,這當(dāng)然很簡單,GameObject.SetActive嘛,但是我們除了世界樹之外還有很多其他樹哦,所以我用了一個(gè)數(shù)組將所有的種子按鈕按順序存儲(chǔ)了起來,方便管理。

想要快速實(shí)現(xiàn)像我這樣全場動(dòng)作整齊劃一的乖乖Button布局的同學(xué)可以用“Horizontal Layout Group”組件快速布局,再將組件去掉哦。
UI是做好了,怎么控制不同按鈕種不同的樹呢,而且還要實(shí)現(xiàn)變異!面對(duì)問題我們要學(xué)會(huì)簡化,按鈕的目的是種樹,那么我們首先要讓按鈕”有樹可依“!
為了使目前測試的UI按鈕“有樹可依”,我們先定一個(gè)小目標(biāo):制作四個(gè)預(yù)制體。
-----------------------------------樹的預(yù)制體----------------------------------------
考慮到有同學(xué)可能會(huì)對(duì)“狀態(tài)機(jī)”充滿疑惑,這里先放一個(gè)傳送門,便于同學(xué)們學(xué)習(xí):
【Unity教程】和十四一起做游戲——斜45度仿黑魂作品(2)人物移動(dòng)控制
這里因?yàn)槲也幌朊款w樹都做一個(gè)動(dòng)畫狀態(tài)機(jī),所以把網(wǎng)格部分的動(dòng)畫用協(xié)程實(shí)現(xiàn)了,這時(shí)候又會(huì)發(fā)現(xiàn)樹只剩下一個(gè)圖片切換的動(dòng)畫,而且不方便調(diào)整樹發(fā)芽的時(shí)間,所以我們其實(shí)可以干脆把這個(gè)狀態(tài)機(jī)取消使用,用協(xié)程來實(shí)現(xiàn)。
實(shí)現(xiàn)效果不如動(dòng)畫來得流暢,但問題不大,同學(xué)們也可以試試通過延時(shí)觸發(fā)實(shí)現(xiàn)。
public IEnumerator TreeAnima()
????{
????????treeGO.SetActive(false);
????????budGO.SetActive(true);
????????yield return new WaitForSeconds(sproutTime);
????????treeGO.SetActive(true);
????????budGO.SetActive(false);
????????float v = maskMaxScale / (maskTime / 0.1f);
????????while (greenMaskGO.transform.localScale.x < maskMaxScale && greenMaskGO.transform.localScale.y < maskMaxScale)
????????{
????????????yield return new WaitForSeconds(0.1f);
????????????greenMaskGO.transform.localScale = new Vector3(greenMaskGO.transform.localScale.x + v, greenMaskGO.transform.localScale.y + v);
????????}
????}
?
由于“歌唱家”異于常樹,需要多一個(gè)攻擊攻擊者的動(dòng)畫,所以在制作歌唱家預(yù)制體的時(shí)候需要新建一個(gè)歌唱家獨(dú)享動(dòng)畫狀態(tài)機(jī)、寫一個(gè)新的腳本,該腳本繼承原本的樹通用的腳本,并重寫被攻擊方法。
//歌唱家的被攻擊方法,其實(shí)差別只有多了一個(gè)概率攻擊
public override void BeAttack(float attackData)
????{
????????if (treeLife > attackData)
????????{
????????????if (Random.Range(0, 2) == 0)
????????????{
????????????????animator.SetTrigger("attack");
????????????????//調(diào)用攻擊者的被攻擊
????????????}
????????????else
????????????{
????????????????animator.SetTrigger("beAttack");
????????????}
????????????treeLife -= attackData;
????????}
????????else
????????{
????????????animator.SetBool("die", true);
????????????Destroy(greenMaskGO);
????????????Destroy(shadowGO);
????????}
????}
?
不要忘了樹被砍掉之后的小樹樁哦~

--------------------------叮,您的按鈕有樹可依啦---------------------------------
有了樹的預(yù)制體之后我們就可以開始我們的造樹大業(yè)啦,首先我們要知道接下來要種的是什么樹,這里設(shè)計(jì)了一個(gè)樹的枚舉類型,因?yàn)闃溥€有變異機(jī)制,所以我們的做法需要稍微復(fù)雜一點(diǎn)點(diǎn),我的做法是這樣的:
1、聲明一個(gè)用于標(biāo)識(shí)按鈕是否顯示的數(shù)組和一個(gè)存儲(chǔ)預(yù)制體名稱的數(shù)組,以及一個(gè)樹的枚舉類型:

2、按鈕腳本中持有一個(gè)樹的枚舉類型,當(dāng)按鈕按下(并且CD結(jié)束)的時(shí)候調(diào)用GameManager對(duì)應(yīng)的方法,對(duì)GameManager持有的當(dāng)前輸入的樹枚舉值進(jìn)行賦值。
3、當(dāng)GameManager中的樹枚舉值不為None的時(shí)候(GameManager中的樹枚舉值為None時(shí)代表沒有持有樹種),玩家點(diǎn)擊地面可以種樹,但是長出來的樹不一定是此時(shí)持有的樹的類型,這里就需要進(jìn)行“變異”
樹的變異:
樹的變異設(shè)計(jì)在上一篇文章中提及過,原版游戲的樹的設(shè)定如下:

這里我將樹的類型和變異都進(jìn)行了簡化,去掉了最后一級(jí)變異,我的變異邏輯是:
1、當(dāng)樹的變異目標(biāo)已經(jīng)全部存在,則直接不變異,否則進(jìn)入變異方法;
2、利用隨機(jī)數(shù)變異,如果隨機(jī)到的物種已經(jīng)存在,也返回不變異,反之返回變異結(jié)果;
這里我對(duì)所有樹的變異抽象成了一個(gè)方法,根據(jù)每個(gè)會(huì)變異的樹聲明一個(gè)枚舉類型數(shù)組,數(shù)組的第一位是該樹自己的枚舉值,其他位置是樹可以變異成的類型。
private SeedType Variation(SeedType[] vars)
{
????int num = Random.Range(0, vars.Length);
????if (!seedIsGet[(int)(vars[num])])
????{
????????return vars[num];
????}
????return vars[0];
}
?
也有更加適合新手閱讀的分別變異的方法,但是在文章中展示的話篇幅就太長了,這段看不懂的同學(xué)可以下載我Github上的源代碼配合理解。
現(xiàn)在你拿到了樹的種子的枚舉類型,只需要通過“Resources.Load<GameObject>("Prefabs/" + treePrefabName[(int)seedType])”就可以得到樹的預(yù)制體啦。
那么我們要把樹種在哪里呢?如果直接種在射線碰撞的位置就會(huì)出現(xiàn)種在后面的樹卻擋住了前面的樹的情況,這就是我們的目標(biāo)之一啦。
最后的解決方法很簡單,就是改變樹的z軸,這里我直接使用了樹的y值,另一個(gè)辦法是修改樹的layer,但是這樣要涉及到鏈表操作,非常的費(fèi)力不討好,當(dāng)然我的源代碼也是有鏈表實(shí)現(xiàn)方法的,下面是種樹的方法。
因?yàn)檫壿嫶笾乱恢?,所以我將種世界樹和其他樹的方法都集合到了同一個(gè)方法里,在調(diào)用的時(shí)候根據(jù)是否種下過世界樹輸入tag標(biāo)簽的名字,為了知道是否已經(jīng)種下世界樹,我又添加了一個(gè)布爾值。
?
public void TreePlanting(string tag)
{
???hit = Physics2D.Raycast(Camera.main.ScreenToWorldPoint(Input.mousePosition), Vector2.zero);
???if (hit.transform != null && hit.transform.tag == tag)
???{
???????if (seedType == SeedType.FristSeed)
???????{
???????????isStart = true;
???????????fristTreeItem.SetActive(false);
???????????seedIsGet[0] = true;
???????????buttonGO[0].SetActive(true);
???????????buttonGO[0].GetComponent<SeedItemBtn>().StartCD();
???????}
???????else
???????{
???????????buttonGO[(int)seedType].GetComponent<SeedItemBtn>().StartCD();
???????????if (IsVariation(ref seedType))
???????????{
???????????????//變異音效//變異UI提示//顯示對(duì)應(yīng)Btn,開始CD
???????????????buttonGO[(int)seedType].SetActive(true);
???????????????seedIsGet[(int)seedType] = true;
???????????}
???????????buttonGO[(int)seedType].GetComponent<SeedItemBtn>().StartCD();
???????}
???????index.SetActive(false);
???????GameObject go = Instantiate(Resources.Load<GameObject>("Prefabs/" + treePrefabName[(int)seedType]), new Vector3(hit.point.x, hit.point.y, hit.point.y), Quaternion.identity);
???????seedType = SeedType.None;
???}
}
?
這樣操作的結(jié)果種樹的3D結(jié)構(gòu)就是下圖這個(gè)樣子的:

那么我們今天的大目標(biāo)小目標(biāo)就都實(shí)現(xiàn)啦~
什么?你說我還沒說歌唱家的動(dòng)畫是怎么做的?
就是Sprite動(dòng)畫啊,上上篇文章教過了,快去補(bǔ)哇!

考慮到同學(xué)們會(huì)跟著做所以決定還是把目前使用的素材上傳到百度網(wǎng)盤,只能作為學(xué)習(xí)用途哦。

https://pan.baidu.com/s/1epj7TiJam46LA5FOHn3h4w#list/path=%2F
------最后的分割線------
歡迎加入游戲開發(fā)群歡樂攪基:869551769
有意向參與線下游戲開發(fā)學(xué)習(xí)的讀者可戳這里進(jìn)一步了解:http://levelpp.com/