這篇文章包含哪些內容
這篇文章從一個Empty的MonoBehaviour入手,首先討論一下C#的修飾符internal,default,virtual,sealed
接著討論一下MonoBehaviour,Component,Tranform,GameObject之間的關系 及腳本之間的如何互相關聯
從一個空類說起
using UnityEngine;using System.Collections;public class EmptyClass : MonoBehaviour { // Use this for initialization void Start () { } // Update is called once per frame void Update () { }}隨便在工程中建立一個C#腳本,Unity就替我們生成了這個空類. 里面包含有兩個方法Start,Update.
首先讓我們拋開這兩個方法的作用不談,先來談談這兩個函數的寫法.
EmptyClass : MonoBehaviour 這個很簡單,表示Empty繼承自MonoBehaviour 。在java或者AS中用extends這里用分號 這個無所謂了.
不過 void Start () {} 這行代碼就很奇怪了. 前面沒有任何的修飾符(PRiate,protected,public). 那這到底什么意思呢?
不加scope修飾符則為private(不建議這樣使用)
google一下有篇文章給出了很好地解釋
Should private members be declared explicitly as private in idiomatic C#?http://www.4byte.cn/question/543979/should-private-members-be-declared-explicitly-as-private-in-idiomatic-c.html
文章說的很清楚,在C#中如果不加任何的scope修飾符則默認為private,不過幾個Answer(我也贊同)不要使用這種方式,還是顯示的加上private為好。
internal的作用域為一個工程
internal這個應該不太能用到,不過還是提一句吧. 也是在我無意中搜索到的,網上文章說的不是很清楚 所以我重新做了一個Demo,
正好也驗證了defult的作用域

這張圖就是整個workspace的結構,每個Solution就是一個個的工程,
而按圖來說如果在LibSolution中定義了internal則MainSolution是訪問不到的

LibA中定義了internal的變量及方法


在同一個Solution內,其子類和非子類對其的訪問權限

在不同的Solution下對其的訪問權限
virtual表示子類可以override,sealed不行,C#不寫virtual則默認為sealed
這個很好理解,不過正好和Java反過來,Java是需要受到final掉才可以,如果有興趣可以閱讀這篇文章
http://stackoverflow.com/questions/1327544/what-is-the-equivalent-of-javas-final-in-c
Unity會自動調用Start,Update等方法即使他們為private
讓我們再回頭看Unity為我們創建的那個空類,里面只有兩個方法 并且兩個方法都沒有寫作用域,我們剛才也提到 沒寫作用域就是private的
那Unity為何能調用到這個函數呢?
http://stackoverflow.com/questions/24772681/c-sharp-when-do-i-need-override-when-dont-i-need-it
萬能的stackoverflow 有人已經給出了解釋 :單從語言的角度來說是不行的,除非你使用反射(反射可以訪問private?),不過Unity并沒有這么使用
我也不知道他們怎么做的...
不管怎樣 就可以理解為Unity搞定了這個問題 :)
MonoBehaviour,Component,Tranform,GameObject
老實的說 搞定了上面的一坨東西 其實對于我們理解Unity沒有任何的幫助...(萬惡的刨根問底啊)
那所建立的腳本和場景上的對象(GameObject)到底是什么關系呢?
了解GameObject
網上的教程 里面都會出現GameObject,Transfomr等等 然后讓你新建一個腳本往一個對象上一拽 ok 開始操作寫代碼
可以到底腳本是怎么和那個對象關聯起來的呢?
其實 GameObject是一個純粹的容器,一副骨架 什么都沒有 甚至連顯示都不行。其唯一的作用就是可以往上添加Component,
想成一個人的模型 那就是 添加了眼睛 你就看得見了,添加了嘴 你就能說話了。
同理想要碰撞就加BoxCollieder,想要聲音就加AudioSource,即使我們用菜單創建出一個Cube它本身也是由
Transform+Cube+BoxCollider+MeshRender 這幾個Component構成的.
我想你一定會發現,當你創建一個GameObject時候Unity已經為你自動添加了一個Component,那就是Transform。
從Inspector中也可以看出,每個GameObject都包含了一個Transform組件。
把GameObject連接起來的Transform
這個Component并不像他在Inspector中表現的那么簡單 只負責x,y,z

Transform還有另外一個作用 就是連接GameObject, 比如說有兩個Cube A B, 把B拖到A的下面 就變成了A得子
當改變A在世界坐標中的位置時候B也隨之改變,這其中的功勞就是Transform。
在Unity中寫的腳本就是Component
在我們進一步的研究Transform之前還是回過頭來看一下在文章開頭提到的那個空類
public class EmptyClass : MonoBehaviour
也就是說EmptyClass(我自己起的名字) 是繼承自MonoBehaviour , 查看Assmbly Browser 會看到 MonoBehaviour是繼承自Behaviour
而Behaviour呢 自然就是繼承自Component嘍。
也就是說 每個建立的腳本都是捆綁在GameObject上面的一個Component.
腳本之間如何互相關聯
有了以上只是的鋪墊 就可以進入這片文章的主題了....
還是由問題入手:
我在一個GameObject上面掛在兩個Class的實例(注意是實例而不是類,掛載就相當于new出來一個對象)ClassA,ClassB

那我如何能讓ClassA訪問到ClassB呢?
Unity里面拖拽
這個也是Unity很牛逼的一個地方,只要在ClassA中建立一個public的變量類型是ClassB 同理在ClassB中建立一個public的變量類型為ClassA
在編輯器中直接互相拖拽一下....
突然發現,咦 沒法拖拽啊... 恩 是的 因為Unity編輯器里面的拖拽綁定方式是GameObject級別的.在編輯器里面可以把兩個GameObject通過這種方式
進行互相或者單方向的引用,但是GameObject內部的Component是不可以的.
GameObject內部的互相引用
當寫的兩個腳本在同一個GameObject內部時候想互相引用就需要用到gameObject這個變量了
注意在腳本內部可以訪問到兩個gameObject, 一個是大寫的GameObject 一個是小寫的gameObject 大寫的GameObject相當于場景級的
也就是剛才想嘗試直接在Unity里面拖拽時候操作一樣,通過他提供的函數可以找到當前場景中所有的GameObject, 而小寫的gameObject呢
其實就是指的是當前Component被捆綁上的GameObject
當確定同一個類型的Component只有一個時候及可以使用
ClassA AInstance = gameObject.GetComponent<ClassA> ();ClassB BInstance = gameObject.GetComponent ("ClassB") as ClassB;
兩種方式都行,如果有多個Component的話則需要使用gameObject.GetComponents() 然后再進一步的for循環查找等. 相關的可以看下官方的文檔
這樣也就實現了之前說的目的,在同一個GameObject內部 實現了腳本(Component)之間的互相引用。
兩個互相連接的GameObject之間的內部腳本互相引用
把問題引申一步,還是那兩個腳本ClassA,ClassB,不過這回不是綁在同一個GameObject上面 而是分辨綁定在兩個GameObject
Parent(ClassA),Child(ClassB)

首先還是來嘗試拖拽,雖然無法在Unity的編輯器中通過拖拽互相引用腳本(Componet),不過綁定GameObject是可以.
所以只需要建立兩個public的變量 然后類型都是GameObject,在Unity里面互相拖拽引用,
最后在Start函數時候通過已經綁定好的gameObject調用其GetComponent方法即可
problem solved~
的確 這個方法是可行,不過有個更好的方法就是使用Transform.
剛才有提到Transform是一個很特殊的Component,其內部保留著GameObject之間的顯示樹結構.
所以就上面的例子來說 當要從Child訪問到Parent 只需要在Child對應的腳本里面寫
transform.parent.gameObject.GetComponent<ClassA>() 即可
返過來就相對麻煩一點,因為無法保證一個parent只有一個child,所以無法簡單的使用
transform.child.gameObject這樣訪問, 但是Unity給我們提供了一個很方便的函數,那就是Find.(Find=FindChild,FindChild 也許即將廢棄? API中并無該函數說明)
需要注意的是Find只能查找其Child,舉個復雜點的例子
Parent->ChildA->ChildB->ChildC
當在Patent中想要找到ChildC中的一個Component時候 調用transform.Find("ChildA/ChildB/ChildC").gameObject;
但是如果在ChildA中 同樣需要找到ChildC的一個Component時候 需要調用transform.FindChild ("ChildB/ChildC").gameObject;
總結
腳本之間需要互相引用的話,在不借助Unity編輯器幫忙的情況下 就需要首先通過Transform找到對應的GameObject節點,
在通過GameObject的GetComponent方法找到對應的腳本
That's All
BestEran
新聞熱點
疑難解答