# Unity程式入門
---
# C#簡介
C#是微軟推出的一種基於.NET框架的、物件導向的進階程式語言。
自2017年Unity廢除掉JavaScript後,為用來編寫Unity的主要語言。
---
# 編譯器
## Visual Studio
Microsoft Visual Studio(視覺工作室,簡稱VS或MSVS)是微軟公司的開發工具套件系列產品。VS是一個基本完整的開發工具集,它包括了整個軟體生命週期中所需要的大部分工具,如UML工具、程式碼管控工具、整合開發環境(IDE)等等。所寫的目的碼適用於微軟支援的所有平台,包括Microsoft Windows、Windows Phone、Windows CE、.NET Framework、.NET Compact Framework和Microsoft Silverlight。
---
# Unity下載
[Unity版本 2019.4.9f1 LTS下載 ](https://unity3d.com/unity/qa/lts-releases?_ga=2.37425065.848543940.1599638662-1872926875.1576549933)

---
# C#程式
----
## 宣告變數
`宣告 型別 變數名稱;`
```csharp=
private float a;//宣告一個私有的浮點數(小數)變數 名稱為a
protected bool b;//宣告一個被保護的布林變數 名稱為b
public int c;//宣告一個公開的整數變數 名稱為c
```
如果是宣告 `private float a = 0; `
這邊的=是指往a這個容器塞入0這個數值,跟數學上的定義不太一樣
#
### 宣告部分
#### private
私有的,意指此變數只可被自己所讀取,無法被別的class(類別)存取
#### protected
被保護的,意指此變數只可被自己或繼承自己的子對象存取
#### public
公開的,意指此變數可被任意class(類別)存取,同時在Unity內可以直接從視窗修改其數值
#
### 型別
#### bool
只有true(真)和false(假)這2種值的型別,大小是1 byte
#### int
最常用的整數型別,如果沒有特殊需求,要使用整數時,就會選擇這個型別
大小是4 bytes,範圍是 -2,147,483,648 到 2,147,483,647 之間
#### float
單精度浮點數
如果你使用Unity引擎,那麼這是你最常使用的浮點數型別。
大小是4 bytes,範圍是1.5 × 10-45 到 3.4 × 1038 之間,7 位數精確度
這邊特別提一下在C#,想要賦予的值為小數時,必須在數值後加上f。
範例:
先宣告一個名稱為F的浮點數
```
public float F;
```
錯誤用法:
`F = 0.5;`
小數值後面未加f,
ERROR編譯器報錯
正確為:
`F = 0.5f;`
程式碼正常執行
#### double
雙精度浮點數
大小為8 bytes ,範圍為±5.0 × 10−324 至 ±1.7 × 10308 ,
跟float不同,值為小數時,不用在數值後面加上f。
```
pulbic double x = 0.0;
```
#### string
字串型別,string的大小是內容字元大小的總和
用來代表一串字元。使用雙引號來包裝以表示字串。
例如:”C8763!”
#### var
這比較特別,
是讓Unity根據你後面下的值來自己填型別。
```csharp=
var i; //報錯 var一定要後面有值才可讓Unity判斷
var i = 0.5f;//此時i的型別為float
var j = 5;//此時j的型別為int
var k = false;//此時k的型別為bool
```
#
這邊只介紹比較常用的幾個型別,其他的型別請自行Google~~
----
## if條件式
```
if(條件式)
{
執行程式
}
```
範例:
```csharp=
public int input = 10;
if(input == 10)//在c# "==" 才是兩者相等的意思
{
input += 5;
}
```
當input這個變數為true時,
進入if(條件式結果為真){執行下面程式賦予input一個新的值false}
----
## 迴圈
### for
```
for (起始值; 條件式; 更新值) {
執行程式
}
```
範例:
```csharp=
for(int i=0; i<5; i++)
{
print(i);// 輸出i這個數值
}
```
輸出結果為
```01234```
### while
```
while (條件式) {
執行程式
}
```
範例:
```csharp=
int i = 0;
while(i<5)
{
print(i);
i++;
}
```
輸出結果為
```01234```
While很容易變無窮迴圈,尤其在Untiy執行耗的資源量比一般寫程式要來的大,
很可能會直接把記憶體全部吃爆,讓電腦死當,請小心謹慎
### do while
```
do
{
執行程式
} while (條件式);
```
範例
```csharp=
int i = 0;
do
{
print(i);
i++;
} while (i<0);
```
輸出:
```
0
```
基本上跟一般while一樣,差別在於是先執行,再來考量條件成不成立,
所以最少也會執行一次
### foreach
```
foreach(宣告變數 in Array/List)
{
依序讀取Array/List內的值
讀取所有的值後及跳出迴圈
}
```
範例:
```csharp=
int[] numArray = {11,13,15,17};
foreach(int a in numArray)
{
print(a+" ");
}
```
輸出結果:
```
11 13 15 17
```
據說foreach在Unity使用這個會很浪費效能(?),總之慎用
----
## 陣列
一個連續的記憶體空間,可大量宣告變數,並用index(索引)來管理裡面的資料。
### 靜態陣列
#### 範例:
```csharp=
public int[] i;//宣告一個整數陣列i,然後未指定陣列長度(陣列大小)
public int[] j = new int[5];//宣告一個整數陣列j,長度為5個
for (int k = 0; k < j.Length ;k++) //陣列名稱.Length 為獲取陣列長度
{
j[k] = k;
print(j[k] + " ");
}
```
輸出:
```
0 1 2 3 4
```
同時
```csharp=
j[0]的值為0
j[1]的值為1
j[2]的值為2
j[3]的值為3
j[4]的值為4
```
### 動態陣列
#### 宣告
```
public List<int> L = new List<int>();
```
#### 範例
```csharp=
public List<int> L = new List<int>();
L.Add(5);//此時L增加了一個空間L[0] 並且L[0]的值為5
L.Add(10);//此時L增加了一個空間L[1] 並且L[1]的值為10
print(L.Count);//Count可獲得目前所有元素的數量,現在裡面有2個元素,則輸出結果為2
L.Remove(0);//移除掉L[0]的空間,而後面的L[1]會自動往上遞補移到L[0]的位置
print(L[0]);//輸出值為10
L.Clear();//清空整個陣列 移除掉所有的元素
```
----
## 方法
### 宣告:
```
傳回值 方法名稱(參數)//void為無傳回值
{
程式執行
}
```
### 範例:
```csharp=
public class 主程式() : MonoBehaviour
{
int Test(int a,int b)
{
return a + b;
}
void Start()
{
print(Test(1,3)+"");//隱含轉換
}
}
```
### 輸出結果
`4`
----
## 物件導向
### 封裝
### 繼承
### 多型
---
# Unity相關
----
## 介面介紹
### Unity 操作介面
Unity 編輯器的操作介面是由多個不同功能的視窗組成,使用者可以調整視窗的大小與位置。

### Toolbar 工具列
Unity 編輯器的最上方是工具列,提供移動、旋轉、縮放物件的編輯工具。
Transform Tools 變形工具 移動 ( W )、旋轉 ( E )、縮放 ( R )
Transform Gizmo Toggles 切換物件使用自身座標或世界座標
Play / Pause / Step Buttons 遊戲檢視的執行、暫停、單格前進按鈕

### Project視窗
每個 Unity 遊戲專案的檔案都是儲存在一個專案資料夾,您可以從 Project View 專案視窗瀏覽專案資料夾的內容。
您可以為遊戲專案建立場景 ( Scenes )、預製物件 ( Prefabs )、行為腳本 ( Scripts ) ,或是匯入模型 ( 3D Models )、紋理 ( Textures )、音效 ( Audio ),並使用 Unity 的專案視窗來進行管理。若需要將資源加入目前的遊戲專案,可點選功能表 [ Assets > Import New Asset ] 並選擇檔案,或是使用滑鼠將檔案拖曳到 Project View 專案視窗。

### Scene視窗
主要工作區是的場景視窗,可以編輯地形或是加入各種遊戲物件,例如玩家角色、燈光、攝影機、粒子系統。

### Hierarchy視窗
顯示目前場景的物件與其階層關係,需要在物件附加元件或腳本時,可直接拖曳到階層面板的遊戲物件。
階層視窗中以藍色顯示名稱的遊戲物件是「預製物件」,而以灰色標示的遊戲物件則是暫時關閉的物件。

### Inspector視窗
Unity 畫面右側的屬性編輯器用於設定遊戲物件的屬性(物件名稱、座標位置、旋轉角度、縮放比例等),是否為靜態物件(用於計算燈光貼圖)。
此外,若是物件已附加燈光、攝影機、碰撞器、程式腳本等元件,也可透過 Inspector 屬性編輯器修改各項參數。

### Game視窗
遊戲視窗可用於遊戲的執行測試,點選右上方的 Stats 按鈕即可顯示 Statistics 半透明視窗,檢視 Draw Calls 數量、模型面數、材質貼圖的記憶體用量。

### Console視窗
用來除錯以及Debug。
接受Debug.Log以及print兩種輸出方式。


----
## 基礎方法:
當此class(類)繼承於monobehaviour時,可以調用以下函式
### void Start()
此函式將在物件生成時執行一次。
```
void Start()
{
print("姆咪姆咪蹦蹦跳!");//此物件生成時 會輸出訊息姆咪姆咪蹦蹦跳至控制台中
}
```
### void Awake()
基本同Start(),只是優先於Start()執行。
### void Update()
```
void Update()
{
Move(); //將會不斷調用Move這個函式
}
```
每幀逐幀執行,基於電腦效能不同可能速度會不一致,請千萬不要在這裡面用while(true)...
### void FixedUpdate()
同Update(),但是會固定速度去執行。
----
## GameObject
只要是在場景中被實例化出來的對象都是一個GameObject。
### 生成
```
Instantiate(物件, 位置,旋轉);
```
```
Instantiate(gameObject, transform.position,transform.rotation);
```
#### 預置物(Prefab)
###### 將Hierarchy視窗裡的物件拖曳到Project視窗,即可視為創建預置物(Prefab),預置物可以從腳本中動態生成。
##### 1. 直接拖曳
* 在腳本裡宣告公開變數,之後直接從Project視窗拖曳至腳本上。


* 之後自由生成吧。
```
Instantiate(Obj, transform.position,transform.rotation);
```
##### 2. Resources方法
創建一個名為Resources的資料夾在Project的最外層即可使用。
```
Resources.Load<資料型態>(資料位置);
```
生成:
```
//如果是物件的話:
GameObject Obj = Resources.Load<資料型態>(資料位置);
Instantiate(Obj, transform.position,transform.rotation);
```
範例:
##### 3. AssetBundle:暫不做介紹。
### 刪除
將此物件從場景中刪除。
```
Destroy(gameObject);
```
### 隱藏、顯示
將此物件從場景中隱藏。
```
gameObject.SetActive(false);//隱藏
gameObject.SetActive(true);//顯示
```
----
## Transform
Transform儲存其位置相關訊息,也包含其父子關係。
### position位置
Unity想要調用位置訊息的話需用Vector3來進行存取。
2D的話也可以使用Vector2。
獲取自身的世界座標
```
transform.position
```
修改
```
transform.position = new Vector3(0,0,0);
//將自身位置改到世界座標(0,0,0)
```
### scale修改大小
```
transform.loclaScale(向量);
```
### 旋轉
旋轉比較特別,單位不是向量,而是歐拉角度,具體應用如下:
```
transform.rotation = Quaternion.Euler(向量);
```
### 父子物件關係
在視窗中,當把一個物件拖到另一個物件之中,它就會成為其的子物件。
而視窗中子物件的位置也將從世界座標改為本地座標。
本地座標就是相對於父物件世界座標的位置。
本地座標(0,0,0)就是跟父物件一樣的位置。
修改自己的父物件
```
transform.parent = 父物件.transform;
```
獲取自己的子物件
```
transform.GetChild(0);//獲取排在其下的第一個子物件
```
#### Find方法 抓取物件
效率低蠻耗資源的做法,可以在場景中依據物件名字去搜尋出來。
除此之外,能搜尋到的只限於沒有被隱藏的的物件。
建議只用在Start()中獲取再存在變數裡面,以便之後調用。
```
GameObject.Find("場景中的物件名稱");
```
----
## 元件添加與獲取元件
### 添加

### 獲取
```
物件名稱.GetComponent<元件名稱> ( ) .參數名稱
```
----
## 讀取鍵盤輸入
### 讀取按鍵
1. KeyDown: 按下該按鍵的當下會執行一次。
```
if (Input.GetKeyDown (KeyCode.W))
{
Debug.Log("您按下了W鍵");
}
```
2. KeyUp: 按下該按鍵後鬆手,按鍵彈起來後觸發。`
```
if (Input.GetKeyUp (KeyCode.W))
{
Debug.Log("您擡起了W鍵");
}
```
3. Key: 一直按著就會不斷返回true來執行。
```
if (Input.GetKey (KeyCode.A))
{
Debug.Log("AAAAA");
}
```
### Unity內建
1. Edit/Project Setting


2. 使用GetAxis可讀取列於Input Manager上的值
```
float Hor = Input.GetAxis("Horizontal");
float Ver = Input.GetAxis("Vertical");
//讀取到的值為-1~1
//以Horizontal為例 0為沒按 -1為按下了往左鍵 1為按下往右鍵
```
----
## Rigibody剛體跟碰撞
### Rigibody剛體
要讓物件有重力及碰撞反應需要的就是剛體,剛體可以在選取物件後在inspect標籤或上方工具列的Component - Physics 裡加入。

### 碰撞盒
當兩個碰撞盒撞上,則其中必須要有一個有剛體才能觸發判斷。

當勾上isTrigger時,碰撞盒將不存在實體可以穿過其他碰撞盒,但還是可以觸發判斷。
### 碰撞判斷
#### 當2個碰撞盒中有一個勾選了isTrigger的話 請使用OnTrigger判斷式
1.OnTrigger判斷式
```
void OnTriggerEnter2D(Collider2D col)
{
//碰撞時調用一次
}
void OnTriggerStay2D(Collider2D col)
{
//碰撞時持續調用
}
void OnTriggerExit2D(Collider2D col)
{
//離開碰撞時調用
}
```
2.OnCollision判斷式
```
void OnCollisionEnter2D(Collision2D collision)
{
//碰撞時調用一次
}
void OnCollisionStay2D(Collision2D collision)
{
//碰撞時持續調用
}
void OnCollisionExit2D(Collision2D collision)
{
//離開碰撞時調用
}
```
#### 標籤
可使用Unity標籤來判斷碰撞到的物體是啥,例如:碰撞的對象是子彈,那麼就扣血
##### 1.設置標籤

##### 2.腳本應用
```
int HP = 100;
void OnTriggerEnter2D(Collider2D col)
{
if (col.CompareTag("Bullet"))
{
HP -= 10;//當碰撞到子彈時,血量扣10
}
}
```
----
## 移動
### 一、向量相加減控制移動
```
transform.position += transform.up * Time.deltaTime*5f;//往上方移動
```
### 二、Translate控制移動
```
transform.Translate(Vector3.up * Time.deltaTime*5f);
//往上方移動
```
### 三、MoveTowards移動至目標點
```
transform.position = Vector3.MoveTowards(transform.position, 目標點, Time.deltaTime*速度);
```
### 四、剛體移動
```
public Rigibody Rig;
void Start()
{
Rig = GetComponent<Rigidbody2D>();//先獲取元件並存至變數裡
}
```
先獲取元件...
#### AddForce
使用AddForce時會受到物體質量影響
```
Rig.AddForce (Vector3.right * 10);//往右移動
```
#### Velocity
修改Velocity時,會給物體一個向量值,這個值就是物體當下的速度
```
Rig.velocity = new Vector2(0,10);//往上移動
Rig.velocity = new Vector2(0,-10);//往下移動
Rig.velocity = new Vector2(10,0);//往右移動
Rig.velocity = new Vector2(-10,0);//往左移動
```
----
## 計時器與協程
### 使用Time.deltaTime
一秒內從第1個Frame到最後一個Frame所花的時間,
所以不管電腦是一秒跑60格或者一秒30格、24格,值都會趨近於一。
就結果而言,deltaTime是為了遊戲「公平性」而產生的東西,
因此最常用於位移(translate),但拿來計時也是勉強可以用。
```
void Update()
{
float ti += Time.deltaTime;//
if(ti >= 3)
{
print("每三秒計一次時");
ti = 0;
}
}
```
### StartCoroutine協程
啟動一個「協程」。
協程簡言之就是執行一個可繼續、可中斷的函式,
所以我可以告訴程式我要隔幾秒在執行下一行陳述式,
因此可以拿來當做計時器。
載入場景時,會把資源都花在讀取上,場景畫面會卡住。
此時也可以用協程來做些Loading動畫。
```
IEnumerator Clock()
{
while(true)
{
yield return new WaitForSecondsRealtime(5);
print("每5秒執行一次");
}
}
void Start()
{
StartCoroutine(Clock());//必須用此函式才有辦法呼叫協程 不然不會呼叫
}
```
直接將協程放在Update不優,會大量產生Clock協程,產生不可預期之結果。
### InvokeRepeating
調用一個方法/秒 。
InvokeRepeating接受三個參數,第一個是方法名,第二個是「第一次調用」要隔幾秒,第三個則是「每隔幾秒調用一次」,與StartCoroutine一樣,是可以中斷的,只要使用CancelInvoke()。
```
void Clock()
{
print("每秒調用");
}
void Start()
{
InvokeRepeating("Clock",1f,1f);
}
```
### Time.time
從遊戲開始到現在所使用的時間。
----
---
# Unity好用插件推薦
----
## DoTween
可用程式直接生成各種關於位移、更改顏色等的動畫。
----
---
{"metaMigratedAt":"2023-06-15T12:52:52.952Z","metaMigratedFrom":"Content","title":"Unity程式入門","breaks":true,"contributors":"[{\"id\":\"faca3329-cc98-475b-ac42-c0a846116bf8\",\"add\":11954,\"del\":1269}]"}