1. 程式人生 > >Unity基礎知識 - Beginner Scripting

Unity基礎知識 - Beginner Scripting

主要內容

從C++的角度開始學C#

Awake和Start函式

Update和FixedUpdate函式

LateUpdate函式

向量計算

元件的啟用與不啟用

啟用遊戲物件

Translate和Rotate函式

LookAt函式

Destroy函式

GetButton 和 GetKey

得到座標系

OnMouseDown函式

GetComponent函式

Delta Time變數

資料型別

Instantiate函式

陣列

Invoke函式

列舉

Switch語句


 

置頂:掛載到遊戲物件上的指令碼必須繼承自Mono,其他的指令碼不需要


 

Start learning C# from a C++ perspective

從C++的角度開始學C#

· 在C#中,變數與函式的定義、if語句、for迴圈、do-whilewhile迴圈等方面的語法都與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函式

· AwakeStart函式在指令碼被載入時自動被呼叫,相當於指令碼定義的類(父類)的建構函式。

 

· Awake函式會首先被呼叫,且指令碼不被啟用時依然能被呼叫。Start函式在Awake函式之後,第一次呼叫Update函式之前被呼叫,但是指令碼不被啟用時不會被呼叫。

 

· 因此Awake函式適合在指令碼被啟用前做一些指令碼間的引用初始化工作,而用Start函式去做指令碼被啟用後才能開始的工作。

 

· AwakeStart函式在生命週期內只能被呼叫一次。


 

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函式

· TranslateRotate函式是用來修改遊戲物件的位置和使遊戲物件旋轉。使用這兩個函式的前提遊戲物件不屬於物理物件,即不含rigidbodyrigidbody為勾選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屬性的函式。

forwardupVector3結構體的成員(屬性)。除了forwardup還有backdownleftright等成員,分別表示向前,順時針,向後,逆時針,左,右。

Time.deltaTime表示單位是m/s而不是m/幀,m/幀的效果會比m/s快很多。

 

· 注意TranslateRotate函式都是在區域性座標系裡工作,而且只能用於非物理物件,若要發生物理互動,就得使用物理函式來進行變換。

 

拓展:Vector3的靜態屬性


 

LookAt Function

LookAt函式

· LookAt函式可以使物件甲(如攝像機)一直看向世界座標系裡的某個物件。

 

· 示例

public class New : MonoBehaviour {

public Transform target;


void Update () {

        transform.LookAt(target);

    }

}

 

Destroy Function

Destroy函式

· Destroy函式可以在遊戲執行時移除遊戲物件,或者遊戲物件上的元件。也可以通過第二個引數(浮點數),在移除前加上一段延遲。

· 需要注意的是,移除元件sprite renderermesh 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中,GetButtonGetKey是獲取鍵盤按鍵或手柄按鍵的方式。GetKey對於鍵盤操作來說是沒問題的,但建議用GetButton來代替它,更加詳細地配置遊戲的控制。

 

· 在Edit/ProjectSettings/Input中調整按鍵以及按鍵內容。

 

· GetButtonDownGetButtonGetButtonUp分別代表三種狀態,按下按住鬆開,並傳遞一個布林值

 

· 示例,給按鍵命名為Jump後,給它配置一個positive buttonspace

 Debug.Log(Input.GetButtonDown("Jump"));


 

GetAxis

得到座標系

· GetAxisGetButtonGetKey的工作方式類似,但是它返回的是一個浮點數,數值在-1到1之間。

Debug.Log(Input.GetAxis("Horizontal"));

 

· 如果用GetAxisRaw(),則返回值只有-1,01,這適用於需要準確控制而不是平滑值的2D遊戲,所以也不需要GravitySensitivity

 

· 在Input的設定中

(想象一下搖桿)

  1. Gravity屬性表示數值在按鍵釋放後返回0的速度,數值越大,速度越快
  2. Sensitivity屬性與Gravity相反,它表示數值返回值到達-11的速度,同樣是數值越大,速度越快
  3. Dead屬性表示盲區,盲區值越大,搖桿就需要移到更遠使GetAxis返回非0
  4. 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.identitytransform.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_(:з」∠)_”