Unity基礎知識 - Beginner Scripting
主要內容
置頂:掛載到遊戲物件上的指令碼必須繼承自Mono,其他的指令碼不需要
Start learning C# from a C++ perspective
從C++的角度開始學C#
· 在C#中,變數與函式的定義、if語句、for迴圈、do-while和while迴圈等方面的語法都與C++相同或者相似,下面只是提一下比較陌生的foreach迴圈。
· foreach迴圈用來遍歷陣列中的每個元素,但不能對元素進行修改,想要修改元素還得用到for迴圈。
· 示例
void Start () {
string[] strings = new string[3]; /*new了一個包含3個字串的陣列,並把地址給指向字串陣列的指標strings*/
strings[0] = "119"; /*賦值*/
strings[1] = "117";
strings[2] = "0330";
foreach (string item in strings) /*在foreach函式的括號中定義一個與遍歷元素型別(string)相同的臨時變數,接上關鍵詞in和需要遍歷的陣列名*/
Debug.Log(item);
}
Awake and Start Functions
Awake和Start函式
· Awake和Start函式在指令碼被載入時自動被呼叫,相當於指令碼定義的類(父類)的建構函式。
· Awake函式會首先被呼叫,且指令碼不被啟用時依然能被呼叫。Start函式在Awake函式之後,第一次呼叫Update函式之前被呼叫,但是指令碼不被啟用時不會被呼叫。
· 因此Awake函式適合在指令碼被啟用前做一些指令碼間的引用或初始化工作,而用Start函式去做指令碼被啟用後才能開始的工作。
· Awake和Start函式在生命週期內只能被呼叫一次。
Update and FixedUpdate Functions
Update和FixedUpdate函式
· Update(應該)是Unity最常用的函式,它在每幀,每個使用它的指令碼上都會被呼叫一次,幾乎所用需要被週期性修改的東西都發生在這,例如非物理物件(無rigidbody)移動,簡單的計時器(Timer),輸入檢測等。
· 但是Update函式並不按照規律的時間被呼叫,如果處理某幀的時間比後面的長,時間間隔就不同了。FixedUpdate的作用跟Update類似,但是FixedUpdate被呼叫的時間間隔是固定的。
· FixedUpdate被呼叫之後,任何必要的物理計算開始進行。因此,任何影響物理物件(含有rigidbody)的操作,都應該在FixedUpdate裡執行。
在用FixedUpdate處理物理效果時,一個好習慣是用力來觸發運動。
LateUpdate Function
LateUpdate函式
· LateUpdate函式在所用Update函式呼叫之後執行,跟隨遊戲物件的相機必須要放在這個函式裡,因為遊戲物件可能在Update函式裡發生位置的變化。
Vector Maths
向量計算
· 向量長度:Vector2.magnitude
· 向量點乘:Vector2.Dot(VectorA,VectorB)
· 向量叉乘:Vector2.Cross(VectorA,VectorB)
Enabling and Disabling Components
元件的啟用與不啟用
· 啟用和不啟用指令碼也能通過這種方式實現。
· 示例:
public class New : MonoBehaviour {
private Light light;
void Start () {
light = GetComponent<Light>(); /*獲取指令碼所在物件上的元件的泛型函式*/
}
void Update () {
if(Input.GetKeyDown(KeyCode.Space))
{
light.enabled = !light.enabled; /*設定成開關*/
}
}
}
Activating GameObjects
啟用遊戲物件
· 在指令碼中控制遊戲物件是否被啟用,要使用語句:
gameObject.SetActive(false);
· 在層次檢視中,父節點不被啟用時,即使子節點處於啟用狀態也不會在場景中顯示出來。想要在控制檯觀察子節點的啟用狀態可以使用語句:
public class New : MonoBehaviour {
public GameObject myObject; /*獲取指令碼所在物件*/
void Start () {
Debug.Log(myObject.activeSelf); /*獲取物件啟用狀態*/
Debug.Log(myObject.activeInHierarchy); /*獲取物件在層次檢視(場景)中的啟用狀態*/
}
}
Translate and Rotate Functions
Translate和Rotate函式
· Translate和Rotate函式是用來修改遊戲物件的位置和使遊戲物件旋轉。使用這兩個函式的前提是遊戲物件不屬於物理物件,即不含rigidbody或rigidbody為勾選Is Kinematic。
public class New : MonoBehaviour {
public float moveSpeed = 10f;
public float turnSpeed = 50f;
void Update () {
if (Input.GetKeyDown(KeyCode.UpArrow))
transform.Translate(Vector3.forward * moveSpeed * Time.deltaTime);
if (Input.GetKeyDown(KeyCode.DownArrow))
transform.Translate(Vector3.forward * -moveSpeed * Time.deltaTime);
if (Input.GetKeyDown(KeyCode.LeftArrow))
transform.Rotate(Vector3.up * -turnSpeed * Time.deltaTime);
if (Input.GetKeyDown(KeyCode.RightArrow))
transform.Rotate(Vector3.up * turnSpeed * Time.deltaTime);
}
}
· Translate()和Rotate()是transform屬性的函式。
forward和up是Vector3結構體的成員(屬性)。除了forward和up還有back,down,left,right等成員,分別表示向前,順時針,向後,逆時針,左,右。
Time.deltaTime表示單位是m/s而不是m/幀,m/幀的效果會比m/s快很多。
· 注意Translate和Rotate函式都是在區域性座標系裡工作,而且只能用於非物理物件,若要發生物理互動,就得使用物理函式來進行變換。
拓展:Vector3的靜態屬性
LookAt Function
LookAt函式
· LookAt函式可以使物件甲(如攝像機)一直看向世界座標系裡的某個物件。
· 示例
public class New : MonoBehaviour {
public Transform target;
void Update () {
transform.LookAt(target);
}
}
Destroy Function
Destroy函式
· Destroy函式可以在遊戲執行時移除遊戲物件,或者遊戲物件上的元件。也可以通過第二個引數(浮點數),在移除前加上一段延遲。
· 需要注意的是,移除元件sprite renderer或mesh renderer與移除遊戲物件的效果相同。
//移除其他遊戲物件:
public class New : MonoBehaviour {
public GameObject other;
void Update () {
if(Input.GetKeyDown(KeyCode.Space))
{
Destroy(other);
}
}
}
//移除指令碼所在物件:
void Update () {
if(Input.GetKeyDown(KeyCode.Space))
{
Destroy(gameObject);
}
}
//移除物件上的元件:
void Update () {
if(Input.GetKeyDown(KeyCode.Space))
{
Destroy(GetComponent<MeshRenderer>());
}
}
GetButton and GetKey
GetButton 和 GetKey
· 在Unity中,GetButton和GetKey是獲取鍵盤按鍵或手柄按鍵的方式。GetKey對於鍵盤操作來說是沒問題的,但建議用GetButton來代替它,更加詳細地配置遊戲的控制。
· 在Edit/ProjectSettings/Input中調整按鍵以及按鍵內容。
· GetButtonDown、GetButton或GetButtonUp分別代表三種狀態,按下、按住、鬆開,並傳遞一個布林值。
· 示例,給按鍵命名為Jump後,給它配置一個positive button為space。
Debug.Log(Input.GetButtonDown("Jump"));
GetAxis
得到座標系
· GetAxis和GetButton、GetKey的工作方式類似,但是它返回的是一個浮點數,數值在-1到1之間。
Debug.Log(Input.GetAxis("Horizontal"));
· 如果用GetAxisRaw(),則返回值只有-1,0或1,這適用於需要準確控制而不是平滑值的2D遊戲,所以也不需要Gravity和Sensitivity。
· 在Input的設定中
(想象一下搖桿)
- Gravity屬性表示數值在按鍵釋放後返回0的速度,數值越大,速度越快
- Sensitivity屬性與Gravity相反,它表示數值返回值到達-1或1的速度,同樣是數值越大,速度越快
- Dead屬性表示盲區,盲區值越大,搖桿就需要移到更遠使GetAxis返回非0值
- Snap允許在positive鍵和negative鍵同時按下的時候返回0
OnMouseDown Function
OnMouseDown函式
· OnMouseDown及相關函式可以檢測在碰撞體或GUI(圖形使用者介面)元素上的點選。
· 示例:
//通過滑鼠點選擊飛一扇門,擊飛指令碼掛在門上
void OnMouseDown() {
rigidbody.AddForce(-transform.forward * 500);
rigidbody.useGravity = true;
}
GetComponent Function
GetComponent函式
· 示例:
//AnotherScript為同一物件上的另一個指令碼,YetAnotherScript為另一個物件上的指令碼
public GameObject otherGameObject; /*引用另一個物件*/
private AnotherScript another; /*引用其他指令碼,這種引用方式實際上新建一個定義在指令碼中的類的例項*/
private YetAnotherScript yetanother;
private void Awake() /*在Awake中完成初始化*/
{
another = GetComponent<AnotherScript>(); /*尖括號內為元件型別*/
yetanother = otherGameObject.GetComponent<YetAnotherScript>();
}
· 注意:GetComponent()比較耗費處理能力,應該儘可能少地呼叫。在Awake或Start函式中使用,或者只在第一次需要時呼叫一次是最好的選擇。
Delta Time
Delta Time變數
· Delta的術語意思為兩個值的差別。Time類中的deltaTime屬性本質是兩次Update或者FixedUpdate的間隔,它可以平滑過程中數值的運算。
Data Types
資料型別
· 基本資料型別分別是值型別(value types)和引用型別(reference types)。
· 值型別裡又有int、float、double、bool、char、Structs等,兩個常用的Structs分別是Vector3(三維向量)和Quaternion(四元數)。
· 引用型別要簡單一些,基本上任何屬於某個類的物件的變數,都是引用型別。
· 常用的引用型別(也就是類)分別是Transform和GameObject,其餘還有Rigidbody、MeshRenderer等。
· 區別:值型別儲存了某個特定的值,而引用型別儲存了某個值所在的記憶體地址。
Classes
類
· 在Unity中,每個指令碼包含了一個類的定義,類繼承自MonoBehaviour。
· 各遊戲物件上不同的功能最好分別寫在不同的指令碼上,這樣更易於管理。
· 在指令碼中建立的類都屬於子類,浮點數數值後要加f。
· 編寫指令碼需要的語法跟C#相同,內容包括類的定義,建構函式的定義,類的例項化等。
· 示例(認真觀察一下,能看出多少知識)
public class Stuff
{
public int bullets;
public int rockets;
public float fule;
public Stuff()
{
bullets = 1;
rockets = 1;
}
public Stuff(int bul,int roc)
{
bullets = bul;
rockets = roc;
}
public Stuff(int bul,float ful)
{
bullets = bul;
fule = ful;
}
}
public Stuff myStuff1 = new Stuff();
public Stuff myStuff2 = new Stuff(2, 2);
public Stuff myStuff3 = new Stuff(2, 2.5f); /*浮點數數值後要加f*/
Instantiate Function
Instantiate函式
· Instantiate函式用來建立遊戲物件的克隆體,通常這是指克隆一個預製體(Prefab)。預製體是預先設定好,存放在資源庫中的遊戲物件。
//發射導彈!!!
public Rigidbody rocketPrefab; /*導彈預製體的引用*/
public Transform barrelEnd; /*給導彈一個發射位置的引用*/
void Update () {
if(Input.GetButtonDown("Fire")) /*在Input裡設定好“Fire”對應的positive button*/
{
Rigidbody rocketInstance; /*為了讓導彈作為一個物理物件出現而建立的變數*/
rocketInstance = Instantiate(rocketPrefab, barrelEnd.position, barrelEnd.rotation) as Rigidbody;
/*Instantiate函式內有三個引數,分別是(預製體,位置屬性,旋轉屬性)*/
/*as Rigidbody的作用是物理物件化*/
rocketInstance.AddForce(barrelEnd.forward * 500f); /*給導彈發射位置給一個力*/
}
}
· 通常生成一個預製體後還要有移除它的方式,譬如導彈發射出去後就應該消失併產生相應的特效和聲音。
//導彈的“自爆”指令碼
void Start() {
Destroy(gameObject, 1.5f); /*呼叫Destroy函式,注意第二個引數是浮點型*/
}
Arrays
陣列
· 陣列定義方式(以整型陣列為例):
第一種:
int[] Array = new int[3]; //跟C#有點不一樣
void Start()
{
Array[0] = 0;
Array[1] = 1;
Array[2] = 2;
}
第二種:
int[] Array = { 0, 1, 2 };
· 在遊戲製作中陣列的用途:
public GameObject[] players;
void Start()
{
players = GameObject.FindGameObjectsWithTag("Player");
/*找到所有帶“Player”標籤的遊戲物件,並把它們放在一個數組中統一進行管理*/
for(int i = 0;i < players.Length;i++)
{
Debug.Log("Player Number " + i + " is named " + players[i].name);
}
}
Invoke Function
Invoke函式
· Invoke函式用於安排一個延遲一定時間呼叫的函式,有助於構造一個有時效性的呼叫系統。
· 示例:
public class New : MonoBehaviour {
public GameObject target;
void Start()
{
Invoke("Spawn", 2); /*只調用一次,延遲2s,第一個引數為函式名*/
InvokeRepeating("Spawn", 2, 1); /*重複呼叫,延遲2s呼叫後,每次呼叫間隔1s*/
}
void Spawn()
{
float x = Random.Range(-2.0f, 2.0f); /*生成隨機數*/
float z = Random.Range(-2.0f, 2.0f);
Instantiate(target, new Vector3(x, 2, z), Quaternion.identity); /*生成預製體*/
}
}
注意:
1)Invoke()能作用的函式只有void型別,不需要引數的函式。
2)Quaternion意思是四元數,又稱為超複數,形式為a+bi+cj+dk(a,b,c,d為實數),i表示x軸到y軸的轉動,j表示z軸到x軸的轉動,k表示y軸到z軸的轉動,所以四元數可以表示三維空間的旋轉。
3)Quaternion.identity和transform.rotation的區別:
Quaternion.identity就是指Quaternion(0,0,0,0),就是每旋轉前的初始角度,是一個確切的值,而transform.rotation是指本物體的角度,值是不確定的,比如可以這麼設定transform.rotation = Quaternion.identity;
4)CancelInvoke("Spawn"); 可以用來停止Invoke函式。
Enumerations
列舉
· 跟C#的內容一樣,這裡不重新寫了。
Switch Statements
Switch語句
· switch語句常跟列舉型別搭配使用,預估也可用於遊戲中的特定觸發事件或隨機觸發事件。
· 內容也與C#一致,這裡不重新寫了。
路過的圈毛君:“這篇介紹的是一些基礎的指令碼知識,以後會慢慢把進階的肝出來啦~挖坑太多,感覺有點填不過來了hhh_(:з」∠)_”