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

作者:Yumir
哈嘍大家好我是Yumir。
不出意外這是本系列的最后一篇文章啦,感興趣的同學可以從頭開始看哦。傳送門如下:
本次使用了狀態(tài)模式實現(xiàn)了簡單的AI,代碼也比較多說的比較細,有條件的同學可以試著自己實現(xiàn)一發(fā)。
傳送門:你的生命是人類的貨幣——用Unity實現(xiàn)《Seed》(一)
? ? ? ? ? ? ? 你的生命是人類的貨幣——用Unity實現(xiàn)《Seed》(二)
我們今天的目標是完成人類部分的功能,因為人類這邊邏輯上都是一樣的(只有貼圖差別),所以只實現(xiàn)一個類型的人類。

我們需要實現(xiàn)的功能有:
1、人類出生之后會尋找樹并砍樹。
2、將樹砍倒后,要有一個人把樹送回人類的房子,其他人繼續(xù)砍樹。
3、將木材送到人類的房子,增加貨幣購買新的人類。
4、人會變老(順便實現(xiàn)被植物攻擊的方法)。
----------------------------開始準備預制體----------------------------------
首先我們來實現(xiàn)人類的預制體,每個類型的人類都是四個動畫,動畫的制作方法之前已經(jīng)講過:用Unity做一個萌萌噠游戲(附資源)

動畫的狀態(tài)機如圖所示,再在人類身上添加一個控制人類行為的腳本,一個人類的預制體就制作完成啦,比樹的預制體簡單很多很多。

----------------------------預制體準備完畢----------------------------------
接下來是人的AI行為,對需求進行分析,首先我們需要實現(xiàn)一個讓人類移向目標的方法,熟悉unity的同學此時就很開心了,這多簡單啊,求出一個指向目標點的向量,讓物體向著這個方向移動就可以了。確實如此,再復雜的建筑也是用建材和粘合劑組合起來的。
人物的移動方法很簡單,但是還沒到分享代碼的時候,還記得文章開頭我說了會用到狀態(tài)模式么?
在看看我們的需求,人類有:找樹、砍樹、回家交貨、死亡,這四個狀態(tài),實際上還有一個狀態(tài)是原游戲的“原始人”特有的(原游戲的原始人沒有工具可以砍樹),也可以用于場景內(nèi)沒有樹的時候,人類會隨機走動。
當人類找樹的時候,人類需要以樹為目標尋路;
當人類在砍樹的時候,人類不需要尋路;
當人類回家交貨的時候,人類需要以“家”為目標尋路;
當人類死亡的時候,人類不會移動;
也就是說我們可以根據(jù)當前人類的狀態(tài)設置目標點。
switch?(humanState){
????case?HumanState.GoNone:
??????target.position?=?new?Vector3(Random.Range(0,?100),?Random.Range(0,?100),?Random.Range(0,?100));
??????break;
????case?HumanState.GoHome:
???????target?=?GameManager.instance.humanHome.transform;
???????break;
????case?HumanState.GoTree:
????????target?=?GameManager.instance.GetHumanTargetTree(transform);
????????break;
????case?HumanState.Attack:
????????break;
????case?HumanState.Die:
?????????target?=?transform;
?????????break;}
?
那么場景中的樹那么多,人類怎么知道自己要跑向哪顆樹呢?

如果你剛剛認真看了上面的代碼就會發(fā)現(xiàn)我寫了一個方法,在“GoTree”狀態(tài)下調(diào)用。
我在GameManager中聲明了一個樹的list,當樹長出來的時候就會把自己加到這個list里面,當樹的生命值小于等于零時將自己從該list刪除并播放樹死亡的動畫,這個list就是下面的“TreeGOList”。這個方法返回了一顆離當前人類最近的樹作為人類的目標。
public Transform GetHumanTargetTree(Transform humanTransform)
{
????????Transform targetTrans = humanTransform;
????????float direction = 2000;
????????foreach (GameObject item in TreeGOList)
????????{
????????????if (Vector3.Distance(item.transform.position, humanTransform.position) < direction)
????????????{
????????????????direction = Vector3.Distance(item.transform.position, humanTransform.position);
????????????????targetTrans = item.transform;
????????????}
????????}
????????return targetTrans;
}
?
現(xiàn)在人類有了自己的目標,我們來想象一下:
游戲開始,人類出現(xiàn)開始找樹,他跑到樹的面前了,我們看到了,可是“人類”并不知道,他還在不停的尋路。
所以我們讓他在距離目標點還有一段距離的時候停下來,進入砍樹狀態(tài),但是目標點也不一定就是樹啊,所以我們還要判斷嘛!當人類往回走的時候需要轉(zhuǎn)身,總不能退著回家嘛!所以我們需要通過比較目標點和人類自身的x軸大小改變?nèi)祟惖摹癴lipX”的值。
人類可真是麻煩,但是沒關系,這個麻煩我?guī)湍銈兏愣?,來看代碼。
curenPosition = this.transform.position;
moveDirection = target.position - curenPosition;
moveDirection.Normalize();
if (curenPosition.x > target.position.x)
{
????GetComponent<SpriteRenderer>().flipX = true;
}
else if (curenPosition.x < target.position.x)
{
????GetComponent<SpriteRenderer>().flipX = false;
}
if (Mathf.Abs(curenPosition.x - target.position.x) < 35f&& Mathf.Abs(curenPosition.y - target.position.y) < 15)
{
?????transform.position = new Vector3(transform.position.x, transform.position.y, target.position.z);
?????if (target.tag == "Tree" && humanState == HumanState.GoTree)
?????{
?????????humanState = HumanState.Attack;
?????????animator.SetBool("attack", true);
?????????target.GetComponent<Tree>().AddAttacker(gameObject);
??????}
??????else if (target.tag == "Home" && humanState == HumanState.GoHome)
??????{
??????????//把身上攜帶的木材交給管理器,然后把自身攜帶的木材清零
??????????GameManager.instance.AddWood(wood);
??????????wood = 0;
??????????animator.SetBool("gohome", false);
??????????humanState = HumanState.GoTree;
???????}
}
else
{
??????transform.Translate(moveDirection * speed * Time.deltaTime);
}
?
人類找到了目標之后還有什么操作呢,一個是砍樹,另一個就是交貨啦。
先說砍樹,在上一篇文章里我們在樹的腳本中寫了被攻擊方法,那么我們要怎么獲取到他呢?
欸!你已經(jīng)在上面的代碼看到了!對的,我們已經(jīng)知道目標是哪個樹了,直接Get他!但是不是上面那句代碼哦,是這段:
private void Attack()
{
???target.GetComponent<Tree>().BeAttack(attack);
???//音效
}
?
這句代碼在哪里調(diào)用呢?答案是在幀事件調(diào)用噠!在人類的刀砍中樹的那一幀加入幀事件,調(diào)用這個方法就可以啦~
那么之前提到的代碼,為什么要在尋路碰到樹的時候調(diào)用呢,這就要說到我們的第二個目標:將樹砍倒后,要有一個人把樹送回人類的房子,其他人繼續(xù)砍樹。

也就是我們需要讓人知道自己的目標樹已經(jīng)去世這個消息,而且只有一個人可以獲得這棵樹的遺產(chǎn),我們要怎么做到呢。
其實砍樹的人群就是若干的“觀察者”,而被砍的樹就是“通知者”,這棵樹在完全去世之前還要先向每個人類都說一句“嘿,我被你打敗了啦,快去砍別的樹吧”并且把自己的遺產(chǎn)交給其中一個人類,讓他回家。
說得花里胡哨的,其實就是在每棵樹的身上也有一個list,這個list是用來存儲當前砍伐自己的人類的,在人類的腳本上寫了人類的狀態(tài)轉(zhuǎn)換方法,當樹死亡時通過foreach遍歷調(diào)用就行了。人類開始砍樹時加入該list,死亡時將自己從該list上刪除。
if (attacker.Count != 0)
{
????foreach (GameObject item in attacker)
????{
????????item.GetComponent<Human>().GoTree();
????}
????????attacker[0].GetComponent<Human>().GoHome(wood);
}
?
再來是回家交貨,樹通知了砍樹的人并且將自己的遺產(chǎn)交給了其中一個人類,這個樹的“wood”就賦值到了人類的“wood”,當人類到達根據(jù)地的時候就會將這個“wood”傳遞給管理器,這段代碼在前面已經(jīng)出現(xiàn)過了,那么問題是管理器中發(fā)生了什么。

游戲中的木材是“人類世界”的貨幣,可以用來購買新的人類,新的人類又會去砍樹,反復循環(huán)。但是如果是交貨的同時購買新的人類的話游戲會仿佛沒有獲得過貨幣,所以這里我用了一個延時將購買新的人類的操作延時觸發(fā)。
? ? public void AddWood(int num)
????{
????????wood += num;
????????woodText.text = wood.ToString();
????????Invoke("BuyHuman", 0.5f);
????}
?
每次購買的人類數(shù)量是所有貨幣的數(shù)量除以當前人類的價格的商(不同時代的人類價格可以改變),為了人類生成的時候不是堆在一起的,給他的坐標加上一個隨機數(shù)。
public void BuyHuman()
????{
????????if (wood / humanPrice != 0)
????????{
????????????int num = wood / humanPrice;
????????????for (int i = 0; i < num; i++)
????????????{
????????????????Instantiate(humanPrefab, new Vector3(humanHome.transform.position.x + Random.Range(20, 60), humanHome.transform.position.y + Random.Range(20, 60), humanHome.transform.position.z), Quaternion.identity);
????????????}
????????????wood = wood % humanPrice;
????????????woodText.text = wood.ToString();
????????}
????}
?
這樣一來我們的前三個目標都已經(jīng)完成了,剩下最后一個目標:人類會死亡。

這是一個很簡單的操作,回憶一下上一篇文章,有一種植物會讓人類加速死亡,我們可以一起做了,我是這樣實現(xiàn)的——增加一個老化速度,每次被大王花攻擊的時候增加這個速度,這個速度的初始值為1。
private void HumanOld()
????{
????????if (life > 0)
????????{
????????????life -= Time.deltaTime*oldVelocity;
????????}
????????else if(humanState != HumanState.Die)
????????{
????????????animator.SetTrigger("die");
????????????humanState = HumanState.Die;
????????????if(humanState == HumanState.Attack)
????????????{
????????????????target.GetComponent<Tree>().RemoveAttacker(gameObject);
????????????}
????????}
????}
?
最后一個功能是對應了我上一篇做的植物預制體中的歌唱家,他的能力是概率使人類迷惑,也就是暫停人類的行動。這個功能可以通過“animator.speed”來實現(xiàn),我用協(xié)程來控制這個迷惑的時間,并且改變了貼圖的顏色,使效果更明顯。

以上就是該游戲所有的功能實現(xiàn)啦,有了這些功能已經(jīng)足夠同學自主補充完整個游戲了,當然我們也不一定要完全把原來的游戲完完整整的做出來,學習嘛,學習的事情,也是要勞逸結(jié)合的。

——分割線——
歡迎加入游戲開發(fā)群歡樂攪基:869551769
有意向參與線下游戲開發(fā)學習的讀者可戳這里進一步了解:http://levelpp.com/