刨根问底U3D---从一个空类说起

这篇文章包含哪些内容

 

这篇文章从一个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的作用域

刨根问底U3D---从一个空类说起

这张图就是整个workspace的结构,每个Solution就是一个个的工程,

而按图来说如果在LibSolution中定义了internal则MainSolution是访问不到的

刨根问底U3D---从一个空类说起

LibA中定义了internal的变量及方法

刨根问底U3D---从一个空类说起

 

刨根问底U3D---从一个空类说起

在同一个Solution内,其子类和非子类对其的访问权限

刨根问底U3D---从一个空类说起

在不同的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

刨根问底U3D---从一个空类说起

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

刨根问底U3D---从一个空类说起

那我如何能让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)

刨根问底U3D---从一个空类说起

首先还是来尝试拖拽,虽然无法在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

Best
Eran 

你可能感兴趣的:(3D)