參考影片來源:Alvin Roe: Platformer Tutorial
目錄:
《Unity Engine》
1.圖片單位5.Inspector鎖定
11.修改反鋸齒等畫面品質輸出設定
15.Input系統
23.【小貼士】平滑數值調整
25.【小貼士】測試模式數值儲存
32.【小貼士】圖層順序
34.【小貼士】UI定位
40.程式讀取順序
《Tilemap》
2.建立「Tilemap」流程3.「Tile Palette」的快捷鍵
4.Tilemap建立前後景的方法
7.Tilemap建立Collider
10.Tilemap破圖修復方法
42.平台碰撞效果器「Platform Effector」
《Rigidbody》
6.Rigidbody 2D的「Body Type」種類8.Rigidbody的語法
9.Rigidibody方向凍結
《Animaion / Animator》
12.Animation建立13.Animator設置
16.利用enum當作Animator變數
20.設定Animation時間單位
21.動畫播放速度
26.【小貼士】Any State的運用
41.動畫調度精簡化
43.【小貼士】Animation逐格整體縮放
《Visual Studio C#》
14.SerializeField變數可視化17.Visual Studio重新命名快捷鍵
18.近乎於0而非0的極小數Mathf.Epsilon
19.偵測是否碰撞Collider與LayerMask類別
22.將選取的程式碼方法化
24.collision與collider觀念釐清
27.繼承的用法
31.自由呼叫音效代碼
33.取得場景名字
38.協程IEnumerator
39.換場景時物件同步
《Cinemachine》
28.建立「Cinemachine」29.「Cinemachine」的Virtual Cam功能
30.攝影機範圍限制
《TextMeshPro》
35.建立「TextMeshPro」36.「TextMeshPro」功能
37.TextMeshPro程式碼的調用
1. 圖片單位:
圖片檔Inspector設定的「Pixels Per Unit」是指圖片以多少像素為一個Sence單位,預設100表示這張圖每100像素會佔Sence的一個格子單位,也就是說數字越少在Sence中顯示越大。如果要放入像素遊戲素材標準需設為16的倍數,假設一個圖像元件是16*16pixel則設16即可。
※若圖片輸出有出現問題,檢查「Texture Type」是否正確,不過基本上Unity會自動分類。
2. 建立「Tilemap」流程:
(1)在Hierarchy視窗點選右鍵>2D Object>Tilemap。
(2)建立完成後,開啟工作視窗,在工具列Window>2D>Tile Palette
(3)在Tile Palette視窗選擇「Create New Palette」按鈕新建,Name欄輸入名字後,點Create選擇檔案建立的位置,這個檔案(.prefab)是負責處理圖片切割的。
(4)將圖片拖曳至Tile Palette視窗,並選擇切割圖片放置的資料夾(切割後會有很多檔案要注意)即可完成Tilemap的準備工作。
※之後想要新增新的圖片元件只要重複第4步驟即可。
3.「Tile Palette」的快捷鍵:
(1)當Tile Palette的工具失焦時(工具變成Sence的),按「B」可以回到Tile Palette的繪製工具。
(2)按住「Shift」會暫時變成橡皮擦工具。
(3)按住「Ctrl」可以選取範圍。
4. Tilemap建立前後景的方法:
為做出前後景的圖層,對新建的Grid物件(Object)使用第2點(建立「Tilemap」流程)提到的第1步驟來新增另一個Tilemap圖層,並對各個圖層的元件更改Tilemap Renderer中的「Sorting Layer」的layer做出顯示的先後順序。
※之前在第2點第1步驟中建立Tilemap圖層時,由於沒有Grid,因此會自動新建一個Grid當作父物件。
5. Inspector鎖定:
點擊Inspector右上方鎖頭圖示即可鎖定,當切換到不同物件時,Inspector的內容保持原樣。
6. Rigidbody 2D的「Body Type」種類:
Dynamic(動態):一般的重力設置。
Kinematic(運動學):除了物理的操作,不會受任何重力影響。
Static(靜態):讓Rigidbody沒有作用,但保持其屬性。
7. Tilemap建立Collider:
為了要讓Tilemap擁有Collider,需要以下幾個步驟:
(1)對Tilemap圖層物件新增一個「Tilemap Collider 2D」元件(不在2D而在Tilemap類別裡),用來讓每一個地圖方塊都可以擁有獨立Collider。
(2)再新增一個「Composite Collider 2D」元件,這是用來將物件所有重疊Collider化為一體,減少問題產生。新增時會自動新增一個「Rigidbody 2D」元件。
(3) 勾選Tilemap Collider 2D的「Used By Composite」選項和Composite Collider 2D元件產生連結,並把Rigidbody 2D的「Body Type」設成「Static(靜態)」讓地圖固定在Sence上。
8. Rigidbody的語法:
Rigidbody2D.velocity = new Vector2(x, y); //給予Rigidbody2D當下的受力狀態 可讀可寫 Rigidbody2D.bodyType = RigidbodyType2D.Dynamic; //將BodyType設為動態 Rigidbody2D.constraints = RigidbodyConstraints2D.FreezePositionX | RigidbodyConstraints2D.FreezeRotation; //凍結X與Z軸
9. Rigidibody方向凍結:
製作2D動作遊戲時,要避免發生角色物件的物理轉動(例如頭朝下),需勾選Rigidbody2D物件「Constraints」中「Freeze Rotation」的Z值。
10. Tilemap破圖修復方法:
Unity的Tilemap在顯示上有個bug,當遊戲畫面移動時,地圖單位和地圖單位之間在某些場合會出現接縫的漏洞,導致鏡頭時不時會有細線閃爍。
解決方法為:
(1)新建一個Material(這裡將其命名為FixGaps),並將其Shader屬性改為「Sprites/Default」,然後勾選「Pixel snap」。
(2)到想修復的Tilemap圖層物件,更改其Tilemap Renderer元件中的「Meterial」為剛剛新建的材質即可修復。
參考資料:How to fix black lines or gaps between tile sprites - Unity 2018.2.7
11. 修改反鋸齒等畫面品質輸出設定:
到工具列Edit>Project Settings>Quality>Anti Aliasing即可修改反鋸齒等設定。
※原本Tilemap的接縫漏洞解決方法是以調整反鋸齒設定來修復,但實測後並沒有太大的作用。
12. Animation建立:
在專案目錄下直接建立一個Animation,並將建立好的檔案拖曳到物件或是其Amimator視窗中。建立好的檔案如果要重複播放,記得到Inspector勾選「Loop Time」。
※建立的Animation可以用在不同物件的Animator裡。
13. Animator設置:
建立Animation動畫間的聯繫時,若只想要立刻跳轉動畫而不做任何延遲或淡出淡入,需做以下兩個動作:
(1)取消勾選「Has Exit Time」。
(Has Exit Time若有勾選,可以到Settings設定「Exit Time」由0-1之間的數,表示這個動畫至少要播到多久才會換下一個動畫,如果是0.25則表示動畫必須播放到1/4的長度才能執行下一個動畫。)
(2)將「Transition Duration」設為0。
(Transition Duration是用來在動畫和動畫間過渡中和的時間長度,例如A動畫移動到a點、B動畫則移動到b點,在這段過度時間內會採用a與b中和的值。上方「Fixed Duration」是用於決定Transition Duration的數字是否以秒為單位,否則以百分比為單位,因此當Transition Duration值為0時勾不勾都無所謂。)
參考資料:【Unity】Mecanim動畫系統Part3 - 動畫轉換的建立和屬性設定(1)
14. SerializeField變數可視化:
讓可填入的變數在Inspector顯示,即使是private也可以,只要在前方加語法即可。
[SerializeField]private ... ; //讓private和public一樣會在Inspector顯示的作用 [SerializeField]public ... ; //public、protected也可以使用
如果想要有相反的結果,讓public不會在Inspector介面顯示,則可以使用HideInInspector。
[HideInInspector]public ... ; //把public隱藏
15. Input系統:
Unity除了在程式碼使用Input.GetKey()外,引擎內也內建一套輸入系統,依按下、放開的時間給予浮點數的正負值,讓輸入和角色動作之間更有彈性。
可以在工具列Edit>Projects Settings>Input找到這項功能。
功能介紹:
Axes Size: 同public的陣列數量,可自定義。
Name: 這套組合的名字,讓程式碼可以呼叫。
Negative Button: 按下後會產生負值(最低到-1)的按鈕。
Positive Button: 按下後會產生正值(最高到1)的按鈕。
Alt Negative Button/ Alt Positive Button: 另一個一樣效果的按鈕。
Gravity: 按下後會花多久才會從正負1回到0的值,以1秒作為單位,1000為1秒的1000倍速度歸位。
Sensitivity: 和Gravity相反,從0按下後要花多久才會到正負1的值。
程式碼:
if (Input.GetAxis("Morizontal") > 0) {...} //接受到Positive Button時的條件 if (Input.GetButtonDown("Jump")) {...} //接受到任何Name為"Jump"的按鈕按下時
16. 利用enum當作Animator變數:
由於enum(枚舉)有int的特性,因此可以宣告枚舉(可以取名為State)來表示角色的狀態,並將值轉換成int來做為Animation播放動畫的條件。
GetComponent<Animator>().SetInteger("state", (int)state); //example
17. Visual Studio重新命名快捷鍵:
對想要全部修改名字的字詞按住「Ctrl」加上兩次「R」,即可更改所有相同名字的字詞。
※其他程式碼檔案的資料也會變動。
18. 近乎於0而非0的極小數Mathf.Epsilon:
有時候我們需要幾乎是0但又不是0的數字,可以使用Mathf.Epsilon,通常用於避免和有整數0會出現的狀態下混淆。
19. 偵測是否碰撞Collider與LayerMask類別:
使用Collider2D.IsTouchingLayers(LayerMask)方法可以偵測該Collider是否有碰觸到擁有該layer的物件;LayerMask是用來在Inspector選擇layer的作用,用法如下:
[SerializeField]LayerMask ground; void Update(){ if(GetComponent().IsTouchingLayers(ground)) { ... } }
20. 設定Animation時間單位:
在Animation視窗點選右上方齒輪圖示>Set Sample Rate可以選擇時間單位,若要自己設定單位,則勾選上方Show Sample Rate,視窗左側會新增Samples格子可自定義單位。
21. 動畫播放速度:
到Animator視窗點選動畫方塊,在Inspector視窗設定「Speed」可調整動畫速度(預設為1)。
22. 將選取的程式碼方法化:
選取想要合併為一個方法的程式碼點選右鍵選擇第一項(或直接按下「Ctril」+「.」), 接著點選Extract Method(擷取方法),輸入方法名稱後即可將選取的程式碼變為一個方法。
23.【小貼士】平滑數值調整:
游標左右拖曳變數的名稱可平滑調整數值,不用手動刪改。
24. collision與collider觀念釐清:
先讓我們看一下以下兩行程式碼:
void OnTriggerEnter2D(Collider2D collider) { ... } void OnCollisionEnter2D(Collision2D collision) { ... }
Collision對物件來說並不是元件,而是一個物件本身專有變數。如果怕混淆,只要記得:當作為「觸發器(Trigger)」使用時,偵測的是「Collider元件」;當作為「碰撞器(Collider)」使用時(雖然方法宣告時使用Collision這個名詞),偵測的是「Collsion數值」。
25.【小貼士】測試模式數值儲存:
在測試模式下如果想要保存調整後的數值,可以到該元件右上方點擊齒輪圖示>Copy Component拷貝數值,接著關閉測試模式回到編輯模式,點擊小齒輪>Paste Component Values將數值貼上。
26.【小貼士】Any State的運用:
在Animator視窗裡的Any State狀態可以做出所有動畫下傳送的觸發條件。要記得Unity有這項功能。
27. 繼承的用法:
如玩家控制的角色接觸到各種怪物時,不想對每一種怪物的程式碼各設一次條件,也不想對各種怪物的程式碼重複輸入,這時就可以運用繼承來保存相通的程式碼,只要調用接觸怪物程式的父類別即可以一概全。
public class Enemy : MonoBehaviour //MonoBehaviour為Unity預設 { protected float life; protected virture void Start() { life = 5; } public LifeDecreaseOne() { life--; } }
public class MonsterA : Enemy //繼承Enemy { protected override void Start() { base.Start() //base為父類別 this為本類別 } }
enemy.GetComponent<Enemy>().LifeDecreaseOne();
28. 建立「Cinemachine」:
Cinemachine是Unity官方提供用來輔助攝影機的外掛,在使用此功能前必須到工具列Window>Package Manager視窗中找到Cinemachine外掛並安裝,即可在工具列中找到Cinemachine>Create 2D Camera來建立一個有Virtual Cam元件的物件,只要此物件存在,攝影機就會受到效益。
29.「Cinemachine」的Virtual Cam功能:
Follow: 首要需設置的功能,用來抓取攝影機跟隨的目標。
Lens > Orthographic Size: 可以調整畫面大小,建議設置與攝影機Size一樣。
Body > Look ahead Time:
根據Follow對象受力的方向依程度移動鏡頭,也就是當數值越大,攝影機越會提前移動到受力方向的位置(黃點)。
Body > X/Y/Z Damping:
Follow的受力點(黃點)從Dead Zone(藍色區塊)回到中心所需花的時間,越高表示受力點超過Dead Zone後會更緩慢的回到Dead Zone外的中心區域。
Body > Screen X/Y: 以Follow對象作為鏡頭中心點的偏移。
Body > Dead Zone Width/Height:
Dead Zone(藍色區塊)的寬度,Follow的受力點(黃點)最終都會離開Dead Zone回到畫面中間的區域。
30. 攝影機範圍限制:
在Cinemachine的Virtual Cam元件最下面找到Extensions>Add Extension,選擇CinemechineConfiner,便會建立一個新的Confiner元件,將其他擁有Polygon Collider 2D元件的物件拖入Bounding Shape 2D後,攝影機拍攝的畫面會限定在該Collider的範圍內移動。
31. 自由呼叫音效代碼:
以下為透過static宣告來讓各個程式碼可以在不需重新宣告下直接使用方法播放音效的程式碼:
public class SEplayer : MonoBehaviour { [SerializeField] string[] name; //設定呼叫時的代號 [SerializeField] AudioClip[] audioClip; //和name一組為被呼叫時播放的音效 AudioSource audioSource; static SEplayer static_SEplayer = new SEplayer(); //建立一個static的自己 void Start() { static_SEplayer.name = name; //給新建立static的自己重新植入Unity介面的值 static_SEplayer.audioClip = audioClip; //以此類推 static_SEplayer.audioSource = GetComponent(); } static public void Play(string input) //供外部呼叫的方法 直接播放一次指定的音效 { int number = GetNameNumber(input); if (number == -1) return; static_SEplayer.audioSource.PlayOneShot(static_SEplayer.audioClip[number]); } static public void Play(string input_1, float input_2) //輸入第二個參數可調節音量 { int number = GetNameNumber(input_1); if (number == -1) return; static_SEplayer.audioSource.PlayOneShot(static_SEplayer.audioClip[number], input_2); } static int GetNameNumber(string input) //分析輸入代號配對的音效順序 若沒找到則回傳-1 { for (int i = 0; i < static_SEplayer.name.Length; i++) { if (input == static_SEplayer.name[i]) return i; } return -1; } }
呼叫方法:
SEplayer.Play("Sound_1");
※更好的static運用請參照第39點。
32.【小貼士】圖層順序:
可以調整Order in Layer來調整同Sorting Layer的圖層顯示順序, 值越小越後面。
33. 取得場景名字:
SceneManager.GetActiveScene().name
※可以用在SceneManager.LoadScene()中重新進入場景。
34.【小貼士】UI定位:
點擊Rect Transform元件左上方的圖示,即可設定UI對齊畫面的條件。
35. 建立「TextMeshPro」:
可以當作是高級版的Text,為新世代Unity引擎的外掛加強功能(UGUI)。
對Hierarchy視窗的Cavas物件點右鍵UI>TextMeshPro - Text,第一次建立時會出現視窗來提醒將會在專案底下新建一個UGUI專用的資料夾「TextMesh Pro」來存放外掛,確認後點選Import TMP Essentials進行安裝。
※對於UGUI的說明可以參照以下的連結。
參考資料:unity 3d中 NGUI和UGUI分别是什么?相对于外部UI插件我们要使用哪个呢?
36.「TextMeshPro」功能:
Color Mode: 用來上漸層色,共有四種模式可選擇。
Spacing Options: 文字間距等設定。
Face > Softness: 柔邊。
Face > Dilate: 文字體積增幅。
Outline > Thickness: 外框線粗度。
Underlay: 陰影設定。
37. TextMeshPro程式碼的調用:
using TMPro; //載入函式庫 public TextMeshProUGUI sometext; //宣告 sometext.text = "test."; //與UI的text用法相同
38. 協程IEnumerator:
協程是Unity在C#一種特殊的方法宣告法,由於所有的程式碼是以一偵作為全部執行的單位,而偵數對人類的時間觀念來說不夠直覺,有時候我們希望程式碼是以某條件才繼續下一個動作,例如一秒過後(通常一秒經過的偵數難以確定)、或是希望程式碼停在某行直到下一偵才繼續運行,這個時候協程就派上用場了。
舉例:
IEnumerator Test() //將void改為IEnumerator { for (int i = 0; i < 10000; i++) { ... //執行內容 yield return null; //yield return是往下執行的條件 值為null表示這一偵到這裡暫停 } yield return null; } //如果刪除for迴圈中的yield return則迴圈將在一偵內執行10000次 同一般方法
IEnumerator Test(float input) //可以加入參數 { for (float i = 0; i <= input; i += Time.deltaTime) { ... //執行內容 yield return 0; //只要return任何數字都等同於null的效果 } } //一個用來在限定執行時間的用法
IEnumerator Test(){ yield return new WaitForSeconds(1.5f) //等待1.5秒後繼續執行 ... //執行內容 yield return new WaitForSecondsRealtime(1.5f) //等待1.5秒後繼續執行(不受Time.timeScale影響) ... //執行內容 } //記得使用WaitForSeconds()前需加上new
呼叫方法:
StartCoroutine(Test());
參考資料一:Unity中协程(IEnumerator)的使用方法介绍
參考資料二:Unity3D製作計數器「StartCoroutine應用」
39. 換場景時物件同步:
在不同場景交換,希望特定物件保持原樣移動到另外一個場景,又不希望和相同的物件重複時(例如每個關卡都有的UI),可以使用以下方法:
public class UI : MonoBehaviour //以記錄數值的UI程式碼為例 { public static UI GetObject; //宣告一個靜態的自己方便其他程式碼呼叫 void Start() { DontDestroyOnLoad(gameObject); if (!GetObject) GetObject = this; //如果GetObject不存在就可以安心把自己拷貝過去 else Destroy(gameObject); //如果已存在靜態的GetObject表示自己是被重複的需要消除 } } public void Function(){ ... }
呼叫方法:
UI.GetObject.Function();
※與第31點直接呼叫方法不同的是:此方法並不需要讓所有方法都宣告static並在Start()將變數全部移植過去。
40. 程式讀取順序:
如果在有使用static程式的情況下出現未知的錯誤,很可能是由於其他程式碼比static的類別宣告前更早要求使用其方法導致,解決方法可以到工具列Edit>Project Settings>Script Execution Order,並在Inspector視窗點選「+」圖示新增程式碼並輸入比Default Time(數值0)更低的數值(最好不要比原本存在的選項更小)表示會在所有程式碼運行前先執行,最後點選Apply更新。
41. 動畫調度精簡化:
為了讓動畫的連結更為簡單,除了讓所有動畫共同擁有一個變數作為條件外,新建一個空白的狀態(舉例取名為StateControl),並將所有動畫連結至此,條件利用Equal及NotEqual來做切換,當變數產生變動時,流程會自動回到中心狀態(StateControl)並轉移到目標動畫,也就是說,只要切換變數就可以切換動畫,不再需要複雜的牽線關係。
42. 平台碰撞效果器「Platform Effector」:
如果不希望有重力的物件(例如角色)因不斷的施力而黏貼於地圖的側邊,可以在地圖圖層新增Platform Effector 2D元件來無視側邊任何施力的摩擦力。
新增後,必須勾選Composite Collider 2D元件的Used By Efferctor才會運作。
以下為Platform Effector 2D的屬性簡易說明:
Use One Way: 變為空心而非實心,以Collider的邊框為實體,會依Suface Arc影響碰撞範圍。
Use One Way Grouping: 碰撞非Suface Arc角度的邊緣則碰撞暫時失效,需先將Use One Way打勾才可使用。
Suface Arc: 碰撞接觸的表面範圍(以角度為單位)。
※以上說明為親自測試的臆測結果。
43.【小貼士】Animation逐格整體縮放
動畫逐格複數選取後左右兩側會有一小格圖示,拖動可調整範圍之間的平均距離。
單字補充:
hierarchy 階層(n.)palette 調色板(n.)
prefab 預制(n.)、預制的(adj.)
composite 合成物(n.)、合成的(adj.)
constraint 限制(n.)
transition 過渡(n.)
duration 期間(n.)
serialize 連載(v.)
sensitivity 敏感、體貼(n.)
epsilon 希臘字母ε(n.)
cine 電影(n.)
lens 鏡頭、透鏡(n.)
orthographic 正交視圖的(adj.)
damping 衰減、潮濕(n.)
polygon 多邊形(n.)
bounding 邊界(n.)
essential 必需品、本質(n.)、必要的、基本的(adj.)
dilate 擴張(v.)
underlay 襯底、地墊(n.)
enumerator 計數器、計數員(n.)
沒有留言:
張貼留言