上一節的最後,我們把Healthy與Score物件都掛上了Tag標籤,並且已經用程式將這兩個物件連結起來了。
1.在場景中,我們需要新增:
1.在場景中,我們需要新增: 2.修改掉多段跳的設定 3.利用程式使分數、血條增減能正常運作
這節,我們會去修改掛在Player物件上的「MainCS.cs」腳本,把我們先前說的所有功能都寫進去。說明完程式、確認沒有問題之後,這次的教學就會告一個段落了。
我們先稍微確定一下我們至今為止做的事情:
a.分數
b.血條
c.扣血的物件
d.補血的物件
e.加分的物件
2.修改掉多段跳的設定
3.利用程式使分數、血條增減能正常運作
我們已經在場景中建立了我們所需要的所有物件,剩下的第二點與第三點,都需要用程式來解決。
不過,在正式進入程式之前,還有一些之前還沒做完的前置作業必須先做一下。
那麼,我們開始吧!
========================================================================
我們之前說,想要讓玩家碰觸到物件時,觸發相關的事件。比如說接觸到加分的物件時,左上角的分數就會增加之類的。
但是,我們該如何判斷「玩家與物件碰觸」這件事情呢?
在【新手入門】三、新增物件、場景&元件介紹中,我們有在介紹Box Collider 2D時提到,在這個元件底下有一個「Is Trigger」的選項。觸發器,可以用這個來觸發某一段程式。
在這次我們所創建的那三個物件中,都會用到這個選項。為此,我們必須在這三個物件底下,都各別新增一個Box Collider 2D元件。但是,個別新增之後又要個別調數值太麻煩了。不知道有沒有人有發現,其實這三個物件的「大小」與「Player」是一樣的?
那這下就簡單多了,我們直接把Player的Box Collider 2D元件直接複製到那三個物件上就可以了。
選取Player,按一下在Box Collider 2D元件右上角的選項圖示。
然後選擇「Copy Component」
回到Hierarchy中,將「Damage」、「Complement」、「Star」物件選起來。由於都是新增同樣的東西,所以我們打算一起處理。
※按住Ctrl可以複選。
然後點選Transform或Sprite Renderer的選項圖示,選擇「Paste Component As New」
貼上之後會長這樣:
這時,我們直接把「Is Trigger」勾選起來。
如果我們在Inspector中修改時,Hierarchy的選擇狀態是長這樣的話:
那就表示,被選起來的那三個物件是一起做同樣的修改的。
現在個別點選「Damage」、「Complement」、「Star」三個物件,應該可以發現他們都被掛上了Box Collider 2D元件,而且「Is Trigger」都已經勾選了。
這三個物件都已經設定完畢,還有一件事情我們需要調整。
在前幾節,我們把血條新增出來之後,就這麼放置不管了,但是不曉得有沒有人有發現,我們的血條的數值竟然從一開始就是「0」
這在遊戲中相當不正常,怎麼有主角剛生出來血條是零的呢?所以,在Hierarchy選擇「Healthy」
在「Slider」元件下面有一個「Value」的滑條。將他往右滑到最底。
雖然滑到最底我們的數值也才「1」而已,但這項設定我們先暫時不理他。先這樣就好。
到目前為止,就是我在一開始說的前置作業。
前置作業已經完成,可以開始進入程式的部分了。
選擇「Player」物件之後,將「MainCS.cs」腳本雙擊開啟。
※由於程式碼變得越來越長,我不會再加上//a、//b等註解,我會直接用行數來表示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class MainCS : MonoBehaviour { public float moveSpeed = 5f; Transform trans; Rigidbody2D rigid; Text score; Slider healthy; bool isJump = true; int scoreCount = 0; // Use this for initialization void Start () { trans = GetComponent<Transform>(); rigid = GetComponent<Rigidbody2D>(); score = GameObject.FindGameObjectWithTag ("Score").GetComponent<Text> (); healthy = GameObject.FindGameObjectWithTag ("Healthy").GetComponent<Slider> (); } // Update is called once per frame void Update () { if (Input.GetKey(KeyCode.LeftArrow)) { trans.Translate(Vector2.left * Time.deltaTime * moveSpeed); } else if (Input.GetKey(KeyCode.RightArrow)) { trans.Translate(Vector2.right * Time.deltaTime * moveSpeed); } if (Input.GetKeyDown(KeyCode.UpArrow) && !isJump) { rigid.AddForce(Vector2.up * 200); } } void OnTriggerEnter2D(Collider2D other) { if (other.gameObject.name == "Damage") { healthy.value -= 0.5f; } else if (other.gameObject.name == "Complement") { healthy.value += 0.3f; } else if (other.gameObject.name == "Star") { scoreCount += 50; score.text = "分數:" + scoreCount.ToString (); } } void OnCollisionEnter2D(Collision2D other) { if (other.gameObject.name == "Floor") { isJump = false; } } void OnCollisionExit2D(Collision2D other) { if (other.gameObject.name == "Floor") { isJump = true; } } } |
在第三節時我有增加幾行程式,這次我又增加了更多行程式XDD,我們一起來看看:
8.public float moveSpeed = 5f; 在原本的設定中,主角移動的速度實在太過緩慢,於是我增加了一個可以在Inspector中隨時變更的公開變數,將這個變數套用在主角移動的公式中,藉此來增減我們的主角移動的速度。 我直接將主角移動速度的預設值設定為「5」 在宣告變數時將它宣告成公開變數(public),會讓這個變數在Inspector裡能夠被看見。 這在【碰撞器應用+UI應用】三、讓程式以Tag方式與物件連結中有提過。 ※這個變數在Inspector裡要顯示甚麼名字,取決於你取的變數名稱。 ※公開變數這種做法不只可以用在指定物件,只要是任何想要讓他在Inspector中能夠被自由定義的變數,都可以使用這種作法。 |
15.bool isJump = true; 這次我們設定的遊戲架構中的第二點,就是想讓主角不能進行多段跳。於是我宣告了一個布林變數,打算用它來判斷現在主角是不是處於跳躍的狀態,如果是,就禁止再次跳躍。 由於在原本設計的遊戲場景中,主角最一開始生在半空中,所以在這裡,我設定isJump的預設值為「true」 |
16.int scoreCount = 0; 雖然我們已經新增了一個可以顯示分數的物件,但是那個Text物件的功用是「顯示字串」,字串是無法計算的,所以我們必須在程式中增加一個數值型態的變數,用這個變數來進行計算,計算完之後再轉為字串,最後交由Text物件──也就是我們新增的Score物件來顯示。 |
32.trans.Translate(Vector2.left * Time.deltaTime * moveSpeed); 這一行就是將第八行宣告的公開變數給放進來。因為預設值為「5」,所以直接乘上去之後,會讓主角的移動速度變為原來的五倍。 第34行也是同樣的概念,只是方向不同而已。 |
37.if (Input.GetKeyDown(KeyCode.UpArrow) && !isJump) { 在向上跳躍的判斷式中,我新增了一小段敘述「&& !isJump」 「&&」是「And」邏輯閘,表示前後的布林值皆為「true」的時候,才會輸出「true」,除此之外的可能性全部都會輸出「false」 「isJump」就是我們剛剛在第15行時宣告,用來判斷是否為跳躍狀態的布林變數。 前面的「!」並不是多打的,「!」為反向邏輯閘,布林值的前面接上這個之後,輸出的布林值就會是相反的。如果「isJump」為「true」,那麼輸出就為「false」,反之,亦然。 所以這段判斷式的意思就是說:如果玩家按下了向上鍵,「而且」不是處於跳躍狀態」的時候,則執行下面的程式。 所以,在「!isJump」這一敘述中,要讓他為「true」,必須要「isJump」為「false」才行。也就是說,如果要讓玩家能夠跳躍,必須要是「非跳躍狀態」下才可以。 |
42.void OnTriggerEnter2D(Collider2D other) { 還記得剛剛我們有將Box Collider 2D元件下的「Is Trigger」選項打勾嗎?將這個選項勾選起來之後,這一行程式才有用處。 這一行程式的意思就是:「當碰到『Is Trigger』選項被勾選的物件的時候,則回傳那個物件的Collider2D資料。」 「OnTriggerEnter2D」是已經被Unity定義好的方法,就是用來判斷具有「Is Trigger」選項的物件被碰觸的事件。這點在Unity API上能夠查得到。 根據剛剛我所描述的這個方法的意思來看,如果被碰觸到的那個物件的「Collider2D」系列元件中,「Is Trigger」沒有被勾選起來的話,那麼這個物件即使被碰觸,也無法被這一段程式偵測到。 另外,OnTriggerEnter2D方法其實還有另外兩個兄弟,分別是「OnTriggerStay2D」、「OnTriggerExit2D」 他們的差別只是在於「Enter」、「Stay」、「Exit」。這三個方法,分別針對三種不同狀態進行偵測: a.Enter:在物件相互碰觸的那一瞬間。 b.Stay:在物件重疊的那一段時間。 c.Exit:在物件離開彼此的那一瞬間。 假設今天有一個A物件穿越了一個B物件。 其中,Enter與Exit,只會在A物件進入與離開B物件時分別執行一次。 Stay,則是會在A物件進入B物件之後,每一幀都執行一次。 這就是這三個方法偵測的事件的差別。 後面(Collider2D other)的意思則是,被碰觸的那個物件的Collider2D資料,將會被回傳在other這個變數中,所以可以用other這個變數來針對被碰觸的那個物件進行修改。 |
43.if (other.gameObject.name == "Damage") { 剛剛有說,第42行後面括弧內的敘述,是被碰觸的物件回傳的Collider2D資料,被存放在other變數裡。 這一行判斷式,就是用來確認主角所碰觸的這個物件,究竟是哪一個物件。 我們可以用主角所碰觸的物件「名稱」,來判斷這個物件究竟是甚麼。 所以我們會用「name」這個函數來取得物件的名稱。 不過「name」這個函數取得的名稱,是「物件的名稱」。但是other實際上是一個Collider2D的資料型態,並不是物件,所以無法用「other.name」直接取得物件名稱。 於是,我們先用「other.gameObject」來取得具有Collider2D的這個物件,然後在後面再加上「.name」來取得這個物件的名稱。 取得被碰觸的物件的名稱之後,把它拿來做比對,如果它的名稱為「Damage」,則執行接下來的程式。 |
44.healthy.value -= 0.5f; 在第43行,我們已經判斷完了被碰觸的物件了,假設主角被「Damage」物件碰觸的話,那們就會執行這一行程式。 「healthy」是我們在上一節中就已經宣告過的「血條」的變數。 「healthy.value」可以用來取得、修改血條的數值。 由於在當前的設定中,血條最小值為0,最大值為1,增減的數值不可以太大,所以我設定為0.5f 「-= 0.5f」的意思白話一點就是,把自己減掉0.5。「0.5f」的「f」是資料型態「float」的意思。如果資料型態是「float」的話,請務必在數值後面都加一個「f」。 這一行程式,就是在碰觸的物件確定為「Damage」之後執行的。 |
48.scoreCount += 50; 剛剛已經有宣告了一個用來記錄分數的數值變數,這一行的意思就是「把自己增加50」 |
49.score.text = "分數:" + scoreCount.ToString (); 「score」是我們在上一節中宣告用來顯示分數的Text型態的變數。 要讓Text物件能夠顯示出字串,就只要用「Text型態的變數.text」就可以設定要顯示的字串了。 另外,我們要把我們計算出來的分數代進這個方法之中,讓它顯示在螢幕上。但是根據Unity API所寫的,text方法似乎只能用在「字串」型態的資料上。我們的分數卻是數值型態的資料,無法直接套用。 這時候只要在數值型態的變數後面加上「ToString()」方法,就可以將這個變數轉換成字串型態的資料了。 |
53.void OnCollisionEnter2D(Collision2D other) { 在第42行的時候有提到,如果碰觸到具有「Is Trigger」的物件,可以用第42行的程式來判斷。但是如果將物件的Is Trigger選項打勾,那麼那個物件就會被穿透過去。 今天我希望能夠不要穿透過去,但是又能夠偵測到與該物件碰觸的話,那該怎麼辦? Unity也有提供這種方法,就是「OnCollisionEnter2D」,他與OnTriggerEnter2D一樣還有另外兩個兄弟,不過意義都是一樣的,我就不再說一次了。 後面括弧內的意義,也跟第42行的方法一樣,只是回傳的資料型態不同而已。 之所以會用到這行程式,是因為我想用主角與「地板」的接觸,來判斷主角是不是「跳躍狀態」 但是地板不可以是可穿透的物體,所以才會用到這個方法。 這個方法與OnTriggerEnter2D方法的差別,就是OnTriggerEnter2D是偵測「Is Trigger有被勾選」的物件,這個則是偵測「Is Trigger沒有被勾選」的物件,僅此而已。 |
55.isJump = false; 根據第53、54行的敘述,當主角與地板接觸的那一瞬間,就會執行這一行程式 而這一行就是設定,主角與地板接觸之後,主角就不再是跳躍狀態。 |
59.void OnCollisionExit2D(Collision2D other) { 這一行就是第53行那個方法的兄弟,在「主角離開某個物件的瞬間」就會執行下面的程式。 |
61.isJump = true; 根據59.60行的敘述,主角離開地板後,就會執行這一行程式。 而這一行就是設定,主角離開地板之後,主角就是跳躍狀態。 |
有了53行~63行的相互搭配,「isJump」將會隨著主角離開地面的與否而改變,進而改變主角跳躍的判斷結果。
今天新增了這幾行的程式,想必我們的遊戲又往前邁進了一步。
已經將程式碼套用上去的人,不妨可以試玩看看,是不是在碰到左邊那個黃色的物件之後,左上角的分數就會增加了呢?
碰到綠色的物件血條會上升、紅色的物件血條會下降,主角的移動速度是不是也變得快多了?
再試試看之前提到的那不合理的現象,狂按上方向鍵看看XDD,是不是已經無法多段跳了?
如果試著做這些事情,都和我所說的一樣的話,那表示已經沒有問題了。
※如果程式套上去之後有錯誤,可能是漏掉了甚麼重要的動作,歡迎在下面留言告訴我和我討論。
最後,我們再來做最後的確認:
a.分數
b.血條
c.扣血的物件
d.補血的物件
e.加分的物件
我們之前所想到的新功能、新元素與設定錯誤,全都已經修改完畢。
這一單元結束,我們了解了該怎麼用Tag的方式與物件做連結,並且也稍微接觸了一點UI介面的設定。也知道了OnCollisionEnter2D、OnTriggerEnter2D等碰撞器相關的應用與程式意義。
我們的遊戲變得越來越豐富,雖然還是有許多可以更好的地方。這些就等到日後有機會再來加強。
這一單元的內容開始變得有一點點小複雜,或許不是那麼好理解,也開始有一些繁瑣的設定需要做。如果在製作的途中有遇到甚麼問題的話,歡迎在下方留言告訴我,我們可以一起討論看看XDD。
那麼,我們【碰撞器應用+UI應用】這個單元的教學也告一段落了。希望至今為止的教學能讓剛接觸Unity的人有些許幫助。
那就這樣,謝謝大家!
========================================================================