Unity3D開發(fā)教程:憤怒的小鳥
一、前言
“憤怒的小鳥”在2009年12月發(fā)布,由于它的高度上癮的游戲,它很快成為有史以來最成功的移動游戲。
在本教程中,我們將在“Unity”中實現(xiàn)“憤怒的小鳥”翻版。游戲中最復(fù)雜的部分是物理系統(tǒng),但是多虧了Unity,我們就不用擔(dān)心太多了。
像往常一樣,一切都會盡可能簡單地解釋,這樣每個人都能理解它。
以下是項目的預(yù)覽:
二、源碼
UI資源:
http://xiazai.jb51.net/202106/yuanma/picturezy_jb51.rar
源代碼:
http://xiazai.jb51.net/202106/yuanma/Game_AngryBirds_jb51.rar
三、正文 項目版本
Unity5.0.0f4
1.設(shè)置相機
點擊Main Cameras,在Hierarchy面板設(shè)置背景色以友好的藍色色調(diào)(紅色=187, 綠色=238, 藍色=255)并調(diào)整大小而位置如下圖所示:
2.地面設(shè)置
地面貼圖設(shè)置
為了防止版權(quán)問題,我們不能在本教程中使用原“憤怒的小鳥”圖形。相反,我們將畫我們自己的Sprite,使他們看起來像原來的游戲。
讓我們從用我們選擇的繪圖工具開始:
將其保存到我們的項目中后,我們可以在項目區(qū)可以看到:
然后在Inspector修改導(dǎo)入設(shè)置:
注:Pixels Per Unit像素轉(zhuǎn)到單位價值16這意味著16x16像素將適合在游戲世界的一個單位。我們將使用這個值作為我們所有的紋理。我們選擇16,因為鳥的大小將有一個16x16像素后,我們希望在游戲世界它有一個單位的大小。
好了,現(xiàn)在我們可以將圖片從項目區(qū)拖入到場景中:
讓我們看看Inspector把地面定位在(0, -2),所以作為不為y=0的都不是地面的一部分:
地面物體設(shè)置
現(xiàn)在地面只是一幅圖像,僅此而已。它不是物理世界的一部分,事物不會與它相撞,也不會站在它上面。我們需要添加一個Collider讓它成為物理世界的一部分,這意味著事物將能夠站在它的頂端,而不是掉進它的正中。
添加BoxCollider2D組件:
3.邊界設(shè)置
創(chuàng)建空對象,命名為borders
位置歸零:
現(xiàn)在,我們將在我們的水平的左邊、右邊和頂部添加某種不可見的區(qū)域。每當(dāng)有東西進入那個區(qū)域,它就應(yīng)該被摧毀。此類行為可以通過Trigger,這幾乎只是一個Trigger它接收到碰撞信息,但不會與任何東西發(fā)生沖突。
添加碰撞器:
勾選
Is Trigger
之后,我們可以為級別的右側(cè)和頂部再添加兩個triggers :
如果我們看看場景然后,我們可以看到觸發(fā)器是如何與我們的背景很好地對齊的:
現(xiàn)在我們?nèi)匀槐仨毚_保任何進入邊界的東西都會立即被銷毀。此類行為可以通過腳本Borders:
創(chuàng)建腳本Borders.cs:
將其添加到邊界對象物體上面:
讓我們也將腳本移動到一個新的Scripts文件夾,只是為了保持清潔:
編輯Borders.cs腳本:
using UnityEngine; using System.Collections; public class Borders : MonoBehaviour { // Use this for initialization void Start () { } // Update is called once per frame void Update () { } }
我們不需要啟動或者更新函數(shù),所以讓我們移除它們。相反,我們將使用OnTriggerEnter2D函數(shù),每當(dāng)有東西進入其中一個邊界觸發(fā)器時,統(tǒng)一將自動調(diào)用該函數(shù):
using UnityEngine; using System.Collections; public class Borders : MonoBehaviour { void OnTriggerEnter2D(Collider2D co) { } }
在這個函數(shù)中,無論什么東西進入Triggers,我們都將Destroy這個物體:
using UnityEngine; using System.Collections; public class Borders : MonoBehaviour { void OnTriggerEnter2D(Collider2D co) { Destroy(co.gameObject); } }
保存腳本后,我們的邊界就完成了。我們稍后會看到,如果我們試圖將一只鳥射出水平之外,它就會消失。
4.云彩設(shè)置
我們將花幾分鐘額外添加云到背景,以使水平看起來更好。像往常一樣,我們首先畫一個:
讓我們在項目區(qū),然后在Inspector修改云的導(dǎo)入設(shè)置:
現(xiàn)在我們要做的就是把它從項目區(qū)進入場景幾次,將每一片云放置在我們想要的位置:
注意:只要使用一些重復(fù)的模式和一些非常顏色,我們可以使水平看起來相當(dāng)好,無需付出很大的努力。
5.彈弓設(shè)計
彈弓圖片
一個飛彈將產(chǎn)生新的鳥類,并允許用戶發(fā)射到水平。和往常一樣,我們將從畫Sprites開始:
這里是導(dǎo)入設(shè)置:
稍后,我們將創(chuàng)建一個腳本,在彈弓的位置生成一只新的鳥,或者確切地說是在彈弓的Pivot位置生成一只鳥。
我們想要在彈弓頂部而不是中間處出現(xiàn),這就是為什么我們要在“導(dǎo)入設(shè)置”中設(shè)置Pivot在頂部。
下面的圖像顯示了中心和頂:
*注意:如果我們將數(shù)據(jù)透視設(shè)置為中心然后變換位置是彈弓中心的點。如果我們把Pivot 設(shè)為頂,然后變換位置是彈弓頂端的點。
好了,現(xiàn)在我們可以將彈弓拖到場景中去了(-22, 3):
生成鳥腳本
如前所述,我們的彈弓應(yīng)該是生成鳥。確切地說,它應(yīng)該在一開始就生成一個,然后等待用戶啟動它,然后在所有的物理計算完成之后再生成另一個。(當(dāng)什么都不動的時候).
我們可以通過腳本來實現(xiàn)這樣的行為。
添加腳本Spawn.cs:
我們可以雙擊腳本來打開它:
using UnityEngine; using System.Collections; public class Spawn : MonoBehaviour { // Use this for initialization void Start () { } // Update is called once per frame void Update () { } }
這個啟動函數(shù)在開始游戲時由Unity自動調(diào)用。這個更新函數(shù)被一次又一次地自動調(diào)用,大約每秒60次。我們不需要它們中的任何一個,這樣我們就可以從腳本中刪除它們。
還有另一種類型的更新函數(shù),它被稱為FixedUpdate…它也被一次又一次的調(diào)用,但是是在單位物理完全相同的時間間隔內(nèi)計算的,所以在做物理工作的時候使用FixedUpdate是一個好主意(我們很快就會這么做).
下面是修改后的腳本FixedUpdate腳本:
using UnityEngine; using System.Collections; public class Spawn : MonoBehaviour { void FixedUpdate() { } }
好的,讓我們添加一個變量,允許我們稍后指定BirdPrefab(我們想生的鳥):
using UnityEngine; using System.Collections; public class Spawn : MonoBehaviour { // Bird Prefab that will be spawned public GameObject birdPrefab; void FixedUpdate() { } }
以下是我們?nèi)绾紊伤姆椒ǎ?/pre>void spawnNext() { // Spawn a Bird at current position with default rotation Instantiate(birdPrefab,transform.position,Quaternion.identity); }生成的觸發(fā)區(qū)域現(xiàn)在我們不能只生一只又一只鳥。相反,我們將不得不生成一個,然后等待它被發(fā)射。有幾種方法可以實現(xiàn)這一點,但最簡單的方法是使用Triggers.
Trigger是一個簡單的Collider ,接收碰撞信息,但實際上并不是物理世界的一部分。所以如果我們添加一個Trigger然后,每當(dāng)有東西進入Trigger、留在Trigger中或離開Trigger時,我們都會收到通知。然而,由于它只是一個Trigger,事情不會像普通Collider 那樣與它相撞(這很快就更有意義了).
我們可以將Trigger添加到彈弓中,方法是在Hierarchy面板中,然后點擊添加組件Circle Collider 2D,給它一個合適的半徑和中心然后啟用觸發(fā):
Is Trigger
我們還可以在場景中查看:
在添加觸發(fā)器之后,每當(dāng)有東西進入時,我們都會收到通知。(OnTriggerEnter2D),停留(OnTriggerStay2D)或離開(OnTriggerExit2D)上面那個綠色的圓圈。
現(xiàn)在,我們可以通過創(chuàng)建一個使用中變量,然后將其設(shè)置為bool值,當(dāng)生下一只鳥的時候為false,當(dāng)它離開觸發(fā)器時為true:
using UnityEngine; using System.Collections; public class Spawn : MonoBehaviour { // Bird Prefab that will be spawned public GameObject birdPrefab; // Is there a Bird in the Trigger Area? bool occupied = false; void FixedUpdate() { } void spawnNext() { // Spawn a Bird at current position with default rotation Instantiate(birdPrefab, transform.position, Quaternion.identity); occupied = true; } void OnTriggerExit2D(Collider2D co) { // Bird left the Spawn occupied = false; } }之后我們可以修改我們的FixedUpdate函數(shù),因此每當(dāng)觸發(fā)區(qū)域不再被占用時,它總是生成一只鳥:void FixedUpdate() { // Bird not in Trigger Area anymore? if (!occupied) spawnNext(); }注:!occupied意思是還沒有被占用…我們也可以用if(occupied==false).我們的生成腳本現(xiàn)在可以正常工作了,但是讓我們再添加一個特性。在射殺一只鳥之后,會有很多東西相互碰撞,墜落,滾來滾去,甚至爆炸。在最初的“憤怒的小鳥”游戲中,只有在水平上的所有東西停止移動之后,才會產(chǎn)生一只新的鳥。
我們可以很容易地創(chuàng)建一個sceneMoving函數(shù),該函數(shù)查找場景中是否有任何對象仍在移動,而不僅僅是一點點:
bool sceneMoving() { // Find all Rigidbodies, see if any is still moving a lot Rigidbody2D[] bodies = FindObjectsOfType(typeof(Rigidbody2D)) as Rigidbody2D[]; foreach (Rigidbody2D rb in bodies) if (rb.velocity.sqrMagnitude > 5) return true; return false; }注意:我們使用了FindObjectsOfType找到所有帶有剛體的物體,之后我們會檢查每個物體的velocity,如果這個剛體的sqrMagnitude大于5,說明這個剛體還在移動就返回True,沒有就返回false使用這個整潔的小腳本,我們可以輕松地修改FixedUpdate功能,因此只有在沒有任何移動的情況下才會產(chǎn)生新的鳥:
void FixedUpdate() { // Bird not in Trigger Area anymore? And nothing is moving? if (!occupied && !sceneMoving()) spawnNext(); }現(xiàn)在我們已經(jīng)完成了生成鳥的腳本,我們可以在Inspector面板看到彈弓上面掛載的腳本:
注意:我們還不能在沒有鳥的情況下測試生成鳥腳本,但是它確實工作得很好,我們將在創(chuàng)建鳥之后看到這一點。
6.鳥的設(shè)置
鳥的圖片
讓我們開始更有趣的事情:鳥。我們首先畫一個16 x 16一只大圓身軀和一些小小的翅膀和眼睛的鳥的像素圖像:
我們將使用以下方法導(dǎo)入設(shè)置為此:
讓我們從項目區(qū)進入場景若要從其中創(chuàng)建游戲?qū)ο?,請?zhí)行以下操作:
鳥的物理
讓我們?yōu)轼B添加碰撞器Circe Collider 2D:
現(xiàn)在有一個Physics Material 2D對撞機的縫隙,讓我們可以給鳥一些特殊的物理特性。在“Unity憤怒的小鳥”教程中,物理材料將是非常有用的,因為現(xiàn)在,如果這只鳥掉在地上,它看起來會是這樣的:
看起來有點不自然。相反,我們想讓這只鳥從下面的東西中跳出來:
要在第二張圖片中創(chuàng)建彈跳效果,我們所要做的就是在項目區(qū)并選擇Create>Physics2D Material,說出來鳥類材料把它變成一個新的物理材料文件夾:
一旦被選中,我們就可以修改Inspector:
注:Bounciness值越大,鳥就越會反彈。
最后,我們可以再次選擇鳥,然后拖動鳥類材料從項目區(qū)進入Collider Material插槽:
這只鳥也應(yīng)該四處走動。剛體負責(zé)物體的重力、速度和其他使物體運動的力。根據(jù)經(jīng)驗法則,在物理世界里,所有應(yīng)該移動的東西都需要一個剛體.:
注意:我們設(shè)置了Gravity Scale到4因為它能讓鳥飛得更快。
如果我們按下Play現(xiàn)在我們可以看到鳥從地上掉下來并彈跳起來:
我們的鳥類物理已經(jīng)完成了,但是有一個小的調(diào)整是我們必須在這里進行的?,F(xiàn)在,如果我們在彈弓中生成的話,由于它的剛體引力,它會立即墜落到地面。我們只希望用戶一開火,鳥就會受到重力的影響,所以讓我們現(xiàn)在啟用Is Kinematic,然后在腳本中禁用它:
現(xiàn)在,剛體是運動學(xué)的,這意味著它不受重力或速度的影響,因此不會立即墜落。
注意:為了更清楚地說明這一點,任何像英雄、汽車或鳥之類的東西都應(yīng)該有一個剛體,它是運動學(xué)的。我們只使能只要鳥還在彈弓里就能運動。
鳥預(yù)制體
如前所述,這只鳥從一開始就不應(yīng)該出現(xiàn)在場景中。相反,彈弓應(yīng)該在需要的時候生成出一只新的鳥。為了使彈弓能夠生成鳥,我們必須創(chuàng)建一個預(yù)制件 (換句話說,我們必須在我們的項目區(qū)有鳥的資源).要創(chuàng)建預(yù)制件,我們所要做的就是在hierarchy面板,將物體拖入到項目區(qū)的Prefabs文件夾中:
現(xiàn)在,我們可以在任何時候?qū)ⅧB加載到場景中,這意味著我們現(xiàn)在也可以從Hierarchy中刪除這個對象:
生成鳥
讓我們將預(yù)制體bird拖到到我們的Spawn.cs的腳本中的BirdPrefab插槽中:
如果我們按下Play現(xiàn)在我們可以看到彈弓是如何生出一只鳥的:
拉and釋放腳本
用戶應(yīng)該能夠把鳥在彈弓周圍,然后釋放它,以便把它射向所希望的方向。我們將創(chuàng)造一個新的C#腳本給它起個名字PullAndRelease :
using UnityEngine; using System.Collections; public class PullAndRelease : MonoBehaviour { // Use this for initialization void Start () { } // Update is called once per frame void Update () { } }用戶將能夠拖動鳥繞一個圓圈。每個圓都需要一個中心,在我們的例子中,它是鳥的生成位置,所以讓我們確保將它保存在一個變量中:using UnityEngine; using System.Collections; public class PullAndRelease : MonoBehaviour { // The default Position Vector2 startPos; // Use this for initialization void Start () { startPos = transform.position; } }*注意:我們還刪除了更新函數(shù)因為我們不需要它。好的,為了讓用戶把鳥拖曳成一個圓圈,我們必須找出這只鳥是否被點擊了。(確切地說:拖著)…我們還需要知道用戶是否釋放了鼠標,在這種情況下,我們必須在我們目標方向發(fā)射鳥。
當(dāng)然,如果沒有這方面的功能,那就不是Unity了。Unity自動調(diào)用Onmouseup和OnmouseDrag函數(shù),當(dāng)我們用鼠標拖動游戲?qū)ο蠡螂S后釋放鼠標時:
*注:鼠標拖動,意思是用戶在GameObject上按住鼠標按鈕,然后移動鼠標。移動鳥真的很容易。我們所要做的就是將當(dāng)前的鼠標位置轉(zhuǎn)換到游戲世界的某個點,然后將鳥移到那里。當(dāng)然,只有在一定半徑內(nèi):
void OnMouseDrag() { // Convert mouse position to world position Vector2 p= Camera.main.ScreenToWorldPoint(Input.mousePosition); // Keep it in a certain radius float radius = 1.8f; Vector2 dir = p - startPos; if (dir.sqrMagnitude > radius) dir = dir.normalized * radius; // Set the Position transform.position = startPos + dir;}
*注意:我們可以使用ScreenToWorldPoint獲取到手指點擊的位置,但是這個位置是不固定的,在找到手指點擊的位置p之后,我們只需要計算從startPos到p的距離,如果這個距離太長dir.sqrMagnitude > radius,就讓這個位置等于一個最大值dir = dir.normalized * radius
如果我們按下Play然后我們可以把鳥繞個圈:
把鳥射向一個方向也同樣容易。我們可以用我們的Onmouseup函數(shù)來知道鼠標何時釋放。然后,我們將計算出從鳥到startPos然后使用rigidbody的AddForce在那里啟動它的功能:
// The Force added upon releasepublic float force = 1300;void OnMouseUp() { // Disable isKinematic GetComponent<Rigidbody2D>().isKinematic = false; // Add the Force Vector2 dir = startPos - (Vector2)transform.position; GetComponent<Rigidbody2D>().AddForce(dir * force); // Remove the Script (not the gameObject) Destroy(this);}
*注意:如前所述,我們也將禁用運動學(xué)等使剛體再次受到重力和速度的影響。我們只需將當(dāng)前位置減去startPos…最后,我們刪除這個對象,這樣它就不能再被發(fā)射了。
如果我們按下Play然后我們就可以拉著這只鳥開火了:
Feather Particle Effect羽毛的粒子效果
讓我們通過增加鳥的碰撞效果來使游戲更加流暢。一旦它第一次落地,它就應(yīng)該像這樣在自己周圍隨意地長出羽毛:
當(dāng)我們需要隨機粒子產(chǎn)生、旋轉(zhuǎn)和向某個方向移動時,就會使用粒子系統(tǒng)。粒子系統(tǒng)的一個簡單的例子是煙霧,它產(chǎn)生灰色紋理,然后以錐狀向上移動。
我們將修改我們的粒子系統(tǒng),使其不是使粒子向上飛,而是使它們飛向四面八方。我們還將修改一些更具體的東西,如大小,速度和旋轉(zhuǎn)。我們的羽毛沒有正確或錯誤的粒子系統(tǒng),所以你可以隨意使用它,直到它看起來像你想讓它看起來那樣。以下是我們得出的結(jié)論:
這是我們用來做這件事的圖像:
*注意:右擊圖像,選擇另存為.。并將其保存在項目的Assets/Sprites文件夾。
導(dǎo)入設(shè)置:
之后,我們可以從項目區(qū)進入我們粒子系統(tǒng)所以它使用圖像對所有的粒子。
現(xiàn)在我們可以將場景中的羽毛對象拖入到我們項目區(qū)的Prefabs文件夾,做成一個預(yù)制體:
然后我們可以在Hierarchy中刪除羽毛游戲?qū)ο?/p>
最后一件事是給我們的鳥添加一個腳本,這樣羽毛粒子系統(tǒng)就會在發(fā)生碰撞時產(chǎn)生。讓我們在項目區(qū)然后創(chuàng)建新腳本…我們給它起個名字CollisionSpawnOnce。我們也會把它移到我們的Sprits文件夾,然后雙擊它以打開它:
using UnityEngine; using System.Collections; public class CollisionSpawnOnce : MonoBehaviour { // Use this for initialization void Start () { } // Update is called once per frame void Update () { } }我們不需要啟動或更新函數(shù)。相反,我們將使用OnCollisionEnter2D函數(shù)和effect公共游戲?qū)ο髴?yīng)生成的預(yù)制件的變量:using UnityEngine; using System.Collections; public class CollisionSpawnOnce : MonoBehaviour { // Whatever should be spawned (Particles etc.) public GameObject effect; void OnCollisionEnter2D(Collision2D coll) { // Spawn Effect, then remove Script Instantiate(effect,transform.position,Quaternion.identity); Destroy(this); } }*注意:為了確保只產(chǎn)生一次效果,我們將從Destroy(this)(這只會關(guān)閉腳本,而不是整只鳥)。保存腳本后,我們可以看到Effect插槽…現(xiàn)在我們可以拖著羽毛粒子系統(tǒng)預(yù)制件項目區(qū)進入Effect插槽:
如果我們按下Play然后把這只鳥發(fā)射到地上,然后我們就可以看到它周圍的羽毛在生成:
路徑
我們還會給我們的鳥添加另一個效果,讓它看起來更流暢:一條白點的軌跡,顯示鳥的軌跡:
首先,我們需要一些大小不同的跟蹤圖像:
我們會用同樣的導(dǎo)入設(shè)置對于每一幅圖像:
我們希望能夠在我們想要的任何時候產(chǎn)生軌跡部分,這意味著我們將需要三個預(yù)制件。因此,讓我們選擇三個圖像并將其拖到場景中,然后拖回到Prefabs文件夾。直到我們有三個預(yù)制體:
現(xiàn)在我們只需要一個腳本來生成一個又一個的TRAIL元素,大約每秒鐘一次。讓我們創(chuàng)建一個新的C#腳本給它起個名字Trail :
using UnityEngine; using System.Collections; public class Trail : MonoBehaviour { // Use this for initialization void Start () { } // Update is called once per frame void Update () { } }我們可以移除更新因為我們不需要它。讓我們添加一個public GameObject[]保存所有跟蹤元素的變量。我們將使用Array,這意味著它不僅僅是一個GameObject:using UnityEngine; using System.Collections; public class Trail : MonoBehaviour { // Trail Prefabs public GameObject[] trails; // Use this for initialization void Start () { } }我們還需要一個函數(shù)來生成下一條線索。例如,它應(yīng)該產(chǎn)生第一個TRAIL元素,然后下一次應(yīng)該生成第二個,然后是第三個,然后是第一個。這可以通過使用實例化有一個額外的計數(shù)器變量:using UnityEngine; using System.Collections; public class Trail : MonoBehaviour { // Trail Prefabs public GameObject[] trails; int next = 0; // Use this for initialization void Start () { } void spawnTrail() { Instantiate(trails[next], transform.position, Quaternion.identity); next = (next+1) % trails.Length; } }我們將其設(shè)置為0,這意味著trails spawnTrail中的第一個元素被調(diào)用。然后使用next+1來增加next。為了保持它在trails數(shù)組的范圍內(nèi),我們還將使用% trails.Length,它使用模(%)運算。對于那些不了解模的人,這里有一個更明顯的版本:void spawnTrail() { Instantiate(trails[next], transform.position, Quaternion.identity); next = next + 1; if (next == trails.Length) next = 0; }現(xiàn)在我們有了一個生成軌跡函數(shù),我們可以使用它生成一個新的trail 元素通過使用InvokeRepeting函數(shù)100 ms生成一個:using UnityEngine; using System.Collections; public class Trail : MonoBehaviour { // Trail Prefabs public GameObject[] trails; int next = 0; // Use this for initialization void Start () { // Spawn a new Trail every 100 ms InvokeRepeating("spawnTrail", 0.1f, 0.1f); } void spawnTrail() { Instantiate(trails[next], transform.position, Quaternion.identity); next = (next+1) % trails.Length; } }現(xiàn)在,小徑元素會一直生成,甚至當(dāng)鳥不飛的時候也是如此。讓我們添加一個小小的修改,只在鳥飛得足夠快的情況下才會產(chǎn)生軌跡:void spawnTrail() { // Spawn Trail if moving fast enough if (GetComponent<Rigidbody2D>().velocity.sqrMagnitude > 25) { Instantiate(trails[next], transform.position, Quaternion.identity); next = (next+1) % trails.Length; } }好的,讓我們保存腳本。在這里,我們將從我們的三條小徑預(yù)制體中拖到插槽中:
如果我們按下Play然后我們就可以看到這只鳥射擊后的蹤跡:
7.木片
讓我們添加一些結(jié)構(gòu),如石頭,冰和木材,我們的Unity2D憤怒的小鳥游戲更加豐富。
我們先畫一塊木片:
注意:右擊圖像,選擇另存為。并將其保存在項目的Assetes/Sprits文件夾。
這里是導(dǎo)入設(shè)置:
現(xiàn)在我們可以把它拖到場景把它放在地上的某個地方:
木片應(yīng)該是物理世界的一部分,所以我們將一如既往地在添加Box Collider 2D組件:
木片也應(yīng)該能夠四處移動?,F(xiàn)在它不會自己移動,但是如果鳥飛進它,它就會移動。它也應(yīng)該受到重力的影響,所以我們需要的是剛體…我們可以通過選擇添加組件->物理二維->Rigidbody 2D…我們也會增加Mass到4所以它更重了一點:
現(xiàn)在我們有一塊木頭,它是物理世界的一部分!
對于這個略有不同的木片,我們將重復(fù)相同的工作流程:
這是我們的游戲如何看待添加第二塊木材和旋轉(zhuǎn)第一個90°:
8.石頭
為了在我們的游戲中有幾種不同的結(jié)構(gòu),我們還將添加兩種不同類型的石頭:
操作流程和以前一樣,這次我們將Mass設(shè)置為10:
下面是我們的游戲中有一些石頭的樣子:
注:我們再次實現(xiàn)了一些體面的外觀與基本的形狀,只有少數(shù)顏色和抖動。
9.冰
冰的圖片
我們將為我們的游戲增加一個結(jié)構(gòu):冰。不過,這一次會更有趣一些。像往常一樣,我們首先畫一塊冰:
這個導(dǎo)入設(shè)置與以往相同:
冰物理
添加 Boxcollider2D組件 剛體組件:
冰應(yīng)該很滑,所以讓我們右擊項目區(qū)并選擇創(chuàng)造->物理二維材料給它起個名字冰IceMaterial:
設(shè)置參數(shù):
之后,我們可以在Hierarchy面板中選擇冰然后將IceMaterial從項目區(qū)拖入到BoxCollider2D>Material插槽:
撞擊時摧毀冰
如果被足夠的力量撞擊,我們也希望冰層被摧毀,因為這就是冰的自然作用。我們添加腳本BreakOnImpact.cs腳本:using UnityEngine; using System.Collections; public class BreakOnImpact : MonoBehaviour { // Use this for initialization void Start () { } // Update is called once per frame void Update () { } }我們不需要啟動或者更新函數(shù),所以讓我們移除這兩個函數(shù)。我們需要一種方法來估計碰撞的力量。我們將保持簡單,并將速度與質(zhì)量相乘:float collisionForce(Collision2D coll) { // Estimate a collision's force (speed * mass) float speed = coll.relativeVelocity.sqrMagnitude; if (coll.collider.GetComponent<Rigidbody2D>()) return speed * coll.collider.GetComponent<Rigidbody2D>().mass; return speed; }*注:Collision2D繼承OnCollisionEnter2D功能,我們將獲取一個估計碰撞的力量,它包含方向與速度相乘。如果我們只關(guān)心速度,那么我們可以使用coll.relativeVelocity.sqrMagnitude…現(xiàn)在,如果造成碰撞的對象有剛體然后我們把速度乘以剛體的質(zhì)量…否則我們只返回速度。好了,現(xiàn)在我們可以用OnCollisionEnter2D函數(shù)以獲得有關(guān)碰撞的通知。然后,我們將比較碰撞的力量和一個可配置的變量。如果它比力大,那么冰就會破裂:
using UnityEngine; using System.Collections; public class BreakOnImpact : MonoBehaviour { public float forceNeeded = 1000; float collisionForce(Collision2D coll) { // Estimate a collision's force (speed * mass) float speed = coll.relativeVelocity.sqrMagnitude; if (coll.collider.GetComponent<Rigidbody2D>()) return speed * coll.collider.GetComponent<Rigidbody2D>().mass; return speed; } void OnCollisionEnter2D(Collision2D coll) { if (collisionForce(coll) >= forceNeeded) Destroy(gameObject); } }如果我們保存腳本,請按Play把鳥碰撞冰層,然后它就會破裂:
現(xiàn)在,我們可以花幾分鐘來復(fù)制這些結(jié)構(gòu),并將它們放在一起,這樣我們就可以在它們之間添加豬了:
10.綠豬
鳥兒想要消滅所有的豬,所以讓我們把一些豬加入到我們的游戲中,這樣鳥兒就不會覺得無聊了。
我們首先畫一個:
導(dǎo)入設(shè)置:
添加碰撞器和剛體:
如果有足夠大的力量襲擊豬,豬就會死。幸運的是,我們已經(jīng)有了一個腳本。給我們的pig物體添加腳本BreakOnImpact.cs,并且設(shè)置Force Needed的值:
*注意:能夠重用這樣的腳本是基于組件的開發(fā)。
現(xiàn)在我們可以復(fù)制這頭豬并把它移到一些結(jié)構(gòu)之間:
如果我們按下Play然后我們就可以試著消滅豬了:
11.橡膠
我們將為我們的游戲添加最后一個功能:彈弓橡膠,所以拖拽和釋放鳥看起來要好得多:
我們首先畫一半的橡膠,這幾乎只是一條粗線:
這里是導(dǎo)入設(shè)置:
*注意:這次我們設(shè)置了Pivot到右(邊),正確的讓一些旋轉(zhuǎn)變得更容易。
現(xiàn)在我們可以從項目區(qū)進入場景兩次,說出其中一個左橡膠,另一個右橡膠把它們放在彈弓的上部:
我們要確保左邊的那個總是畫出來的。后面彈弓和右彈弓總是被拉進去的。前面其中的一部分。我們可以再加兩個分類層對于我們的游戲,但我們將保持簡單,只需更改層序到-1左邊的橡膠和1右邊的橡膠:
現(xiàn)在橡膠看起來就像在外彈弓:
在開始編寫腳本之前,讓我們在Hierarchy然后將這兩個對象設(shè)置為slingshot的子對象:
*注意:每當(dāng)我們移動彈弓時,橡膠部件就會隨之移動。
讓我們創(chuàng)建一個新的C#腳本給它起個名字Rubber:
using UnityEngine; using System.Collections; public class Rubber : MonoBehaviour { // Use this for initialization void Start () { } // Update is called once per frame void Update () { } }這個腳本的目的是讓兩個橡膠部分跟隨鳥,直到它離開生成鳥的觸發(fā)圈。我們需要兩個變量leftRubber 和rightRubber,讓我們在以后指定橡膠。我們不需要啟動或者更新函數(shù),讓我們刪除掉它們:
using UnityEngine; using System.Collections; public class Rubber : MonoBehaviour { // The Rubber objects public Transform leftRubber; public Transform rightRubber; }現(xiàn)在,有一些稍微復(fù)雜一些的功能。我們將需要一個功能,定位橡膠在彈弓和鳥的位置,我們必須先將橡膠旋轉(zhuǎn)到鳥的方向,然后根據(jù)鳥的距離使橡膠變成或變短:void adjustRubber(Transform bird, Transform rubber) { // Rotation Vector2 dir = rubber.position - bird.position; float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg; rubber.rotation = Quaternion.AngleAxis(angle, Vector3.forward); // Length float dist = Vector3.Distance(bird.position, rubber.position); dist += bird.GetComponent<Collider2D>().bounds.extents.x; rubber.localScale = new Vector2(dist, 1); }注意:首先我們計算從鳥到橡膠的距離,然后計算這個方向的角度。然后我們,通過Quaternion.AngleAxis(angle, Vector3.forward)將橡膠旋轉(zhuǎn)到這個角度。最后,我們計算從鳥到橡膠的距離,之后,我們將橡膠的縮放設(shè)置為這個距離加上碰撞器的x的長度既dist += bird.GetComponent().bounds.extents.x這個OnTriggerStay2D功能將通知我們,每當(dāng)鳥改變它的位置時,它還在彈弓。我們可以使用這個函數(shù)來調(diào)整左右橡皮筋:
using UnityEngine; using System.Collections; public class Rubber : MonoBehaviour { // The Rubber objects public Transform leftRubber; public Transform rightRubber; void adjustRubber(Transform bird, Transform rubber) { // Rotation Vector2 dir = rubber.position - bird.position; float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg; rubber.rotation = Quaternion.AngleAxis(angle, Vector3.forward); // Length float dist = Vector3.Distance(bird.position, rubber.position); dist += bird.GetComponent<Collider2D>().bounds.extents.x; rubber.localScale = new Vector2(dist, 1); } void OnTriggerStay2D(Collider2D coll) { // Stretch the Rubber between bird and slingshot adjustRubber(coll.transform, leftRubber); adjustRubber(coll.transform, rightRubber); } }快好了。我們將再增加一個離開時候觸發(fā)的事件,使橡皮筋在發(fā)射后變短:void OnTriggerExit2D(Collider2D coll) { // Make the Rubber shorter leftRubber.localScale = new Vector2(0, 1); rightRubber.localScale = new Vector2(0, 1); }現(xiàn)在,我們可以通過首先選擇彈弓對象中的游戲?qū)ο?。層次性然后點擊添加組件->Scitps->Rubber…我們亦會把這兩個橡膠拖到相應(yīng)的槽內(nèi):
如果我們按下Play現(xiàn)在我們可以看到小鳥和彈弓之間的橡皮筋:
當(dāng)然,現(xiàn)在我們也可以玩一輪憤怒的小鳥了:
總結(jié)
到這里本篇文章就結(jié)束了,我們剛剛創(chuàng)建了一個小游戲憤怒的小鳥翻版,使用簡單的形狀與顏色來達到一個良好的效果,廣泛的使用了Unity的2D物理引擎,添加了許多的效果
相關(guān)文章
深入解析C#中的交錯數(shù)組與隱式類型的數(shù)組
這篇文章主要介紹了深入解析C#中的交錯數(shù)組與隱式類型的數(shù)組,隱式類型的數(shù)組通常與匿名類型以及對象初始值設(shè)定項和集合初始值設(shè)定項一起使用,需要的朋友可以參考下2016-01-01C# 并發(fā)控制框架之單線程環(huán)境下實現(xiàn)每秒百萬級調(diào)度
本文介紹了一款專為工業(yè)自動化及機器視覺開發(fā)的C#并發(fā)流程控制框架,通過模仿Go語言并發(fā)模式設(shè)計,支持高頻調(diào)度及復(fù)雜任務(wù)處理,已在多個項目中驗證其穩(wěn)定性和可靠性2024-10-10