想當年剛學Unity的時候,這個問題困擾了我好幾天,因此來分享一下當初解決問題的思路.
我們通過Unity構建場景的過程中,經常發現一個現象,就是物體在拖進場景中后,我們會發現物體是反的,通過改變物體的rotation屬性后,得到了正確的方向,可物體的坐標系又變得和默認坐標系(右上角系統自帶的坐標系)不一樣了,這樣就給后續的腳本工作(通常是控制(Transform)腳本和生成(Intantiate)腳本)帶來了困擾,因為腳本寫作的過程中是按照世界坐標系來的,物體自己的坐標系和系統坐標系不一樣了就會出現諸如按W向下 按S向上 按A向右 按D向左的現象.
其實,在游戲場景的創建過程中,不僅僅是物體的方向,一個游戲物體的坐標 方向 大小,都可能和我的的預期,或者是我們希望的樣子有很大的區別,為了得到我們想要的樣子,勢必要改變該物體,那么這時為了不出現上一段出現的問題,我們就需要借助於Unity中的空物體,我們通過實例試驗來得出結論(很多時候,為了記憶深刻,自己在Unity里試驗一遍是個非常好的方法).
如上圖,我通過互聯網找到了一個飛機和一個炮彈的模型,我要實現一個飛機發射炮彈的demo, 要求是通過WSAD控制飛機的上下左右,按下空格鍵發射炮彈, 炮彈發射的位置是飛機前面的卡槽.
通過上圖可以看出: 飛機和炮彈的比例很不協調 而且炮彈的方向也是反的 這里我們暫時不管這些問題 下面我們來一步一步地達到我們的要求
首先給飛機起名字叫player 炮彈起名字叫zidan
先給player寫控制腳本
1 void float m_speed=3.0f; 2 3 void Update () { 4 //移動量 5 float movex = 0, movez = 0; 6 //前 7 if (Input .GetKey(KeyCode.W)) 8 { 9 movez += m_speed * Time.deltaTime; 10 } 11 //后 12 if (Input.GetKey(KeyCode.S)) 13 { 14 movez -= m_speed * Time.deltaTime; 15 } 16 //左 17 if (Input.GetKey(KeyCode.A)) 18 { 19 movex -= m_speed * Time.deltaTime; 20 } 21 //右 22 if (Input.GetKey(KeyCode.D)) 23 { 24 movex += m_speed * Time.deltaTime; 25 } 26 //變換 27 this.transform.Translate(new Vector3(movex, 0, movez)); 28 29 }
此時 把腳本掛在player上面 可以實現飛機的前后左右移動了.
然后 給炮彈寫移動腳本
1 //子彈移動速度 2 public float m_speed = 3.0f; 3 //子彈生存時間 4 public float livetime = 3.0f; 5 6 7 void Update () { 8 //生存時間遞減 9 livetime -= Time.deltaTime; 10 //生存時間結束后銷毀物體 11 if (livetime <=0) 12 { 13 Destroy(this.gameObject); 14 } 15 //實現移動 16 this.transform.Translate(Vector3.forward * m_speed * Time.deltaTime); 17 18 }
掛在zidan上面 此時可以實現子彈基於自身坐標系向前(forward)的移動了
要把飛機和炮彈結合在一起 這里需要在player的腳本里加上Instantiate(生成/實例化)命令 並且把zidan拖到project面板里的prefabs文件夾里形成預制體
1 //移動速度 2 public float m_speed = 3.0f; 3 //生成的子彈 4 public Transform playerRocket; 5 //子彈生成速率 6 public float m_rate = 1.0f; 7 8 //子彈生成函數 9 void Fire() 10 { 11 Instantiate(playerRocket,this.transform.position,Quaternion.identity); 12 } 13 // Use this for initialization 14 void Start () 15 16 { 17 18 //按空格鍵發射炮彈 19 20 if (Input.GetKey(KeyCode.Space)) 21 { 22 InvokeRepeating("Fire", 1, m_rate); 23 } 24 25 } 26 27 // Update is called once per frame 28 void Update () { 29 //移動量 30 float movex = 0, movez = 0; 31 //前 32 if (Input .GetKey(KeyCode.W)) 33 { 34 movez += m_speed * Time.deltaTime; 35 } 36 //后 37 if (Input.GetKey(KeyCode.S)) 38 { 39 movez -= m_speed * Time.deltaTime; 40 } 41 //左 42 if (Input.GetKey(KeyCode.A)) 43 { 44 movex -= m_speed * Time.deltaTime; 45 } 46 //右 47 if (Input.GetKey(KeyCode.D)) 48 { 49 movex += m_speed * Time.deltaTime; 50 } 51 //變換 52 this.transform.Translate(new Vector3(movex, 0, movez)); 53 54 }
我們發現這時是這個樣子的 炮彈偏大而且方向是反的
這個問題很好解決 我們把zidan的預制體調小到了合適的大小 方向也通過改rotation調整到了合適的方向 再次運行 發現是這個樣子的
炮彈從飛機的后方發射出來了 為什么呢 因為此時炮彈自身的坐標的向前(forward)就是飛機的后方 腳本是沒有錯了 錯就錯在子彈的朝向和它自身坐標的forward的朝向不一致
怎么解決這個問題呢 這就需要空物體了 給zidan創建一個空物體 把子彈拖進去 給空物體而不是子彈掛腳本 這時你子彈怎么變換已經與空物體無關了 因為子彈是空物體的子類 影響不到空物體 於是我們把外面包裹着空物體的子彈做成預制體並且調好大小方向和飛機關聯 再次運行
實現目標
總結,創建場景過程中,如果遇到模型和自己預期的不一致的時候,通過為模型創建父級空物體並把腳本掛到空物體上,可以實現腳本內容和模型transform的分離 從而更加靈活的調節我們需要的效果
