这篇文章包含哪些内容

这篇文章从一个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

Best
Eran

刨根问底U3D---从一个空类说起的更多相关文章

  1. C++语法小记---经典问题之一(一个空类包含什么)

    问题:一个空类包含什么 空的构造函数 拷贝构造函数(浅拷贝) 重载赋值操作符函数(浅拷贝) 析构函数 取址运算符 取址运算符const 注意 所有的这些默认函数,只有在代码中调用了才会生成,否则也不会 ...

  2. php建立一个空类: stdClass

    $pick = new stdClass; $pick->type = 'full'; ;

  3. 转:C/C++中,空数组、空类、类中空数组的解析及其作用

    转自:http://blog.sina.com.cn/s/blog_93b45b0f01015s95.html 我们经常会遇到这些问题: (1)C++中定义一个空类,他们它的大小(sizeof) 为多 ...

  4. c++空类的大小

    初学者在学习面向对象的程序设计语言时,或多或少的都些疑问,我们写的代码与最终生编译成的代码却大相径庭,我们并不知道编译器在后台做了什么工作.这些都是由于我们仅停留在语言层的原因,所谓语言层就是教会我们 ...

  5. C++空类以及没有成员变量的类的大小

    关于C++中空类的大小为1,我们大家都有所了解,但是除了空类之外的其他一些没有成员变量的类的大小,还是有很多不明之处的. 我们来看如下一个例子: #include<iostream> us ...

  6. C++空类中的默认函数

    定义一个空的C++类,例如 class Empty { } 一个空的class在C++编译器处理过后就不再为空,编译器会自动地为我们声明一些member function,一般编译过去就相当于 cla ...

  7. 空类的默认函数—— SAP电面(2)/FEI

    定义一个空类 class Empty { }; 默认会生成以下几个函数 2. 拷贝构造函数 Empty(const Empty& copy) { } 3. 赋值运算符 Empty& o ...

  8. C++中的空类,编译器默认可以产生哪些成员函数

    C++中的空类,编译器默认可以产生哪些成员函数 C++中创建一个空类:class Empty {};默认会生成4个函数,其函数的原型如下: public: Empty() { ... } Empty( ...

  9. 为什么C++空类的实例大小为1

    [为什么C++空类的实例大小为1] 每个实例在内存中都有一个独一无二的地址,为了达到这个目的,编译器往往会给一个空类隐含的加一个字节,这样空类在实例化后在内存得到了独一无二的地址.所以大小为1. 参考 ...

随机推荐

  1. breadth-first depth-first best-first

    Computer Science An Overview _J. Glenn Brookshear _11th Edition For our example in Figure 11.7, we c ...

  2. android studio无法关联源码

    1.查看源码的时候报这个, 说找不到API 23的源码 2.本地的SDK 3.google  stackoverflow 给出解决方案 http://stackoverflow.com/questio ...

  3. ASP.NET WebForm与ASP.NET MVC的不同点

    ASP.NET WebForm ASP.NET MVC ASP.NET Web Form 遵循传统的事件驱动开发模型 ASP.NET MVC是轻量级的遵循MVC模式的请求处理响应的基本开发模型 ASP ...

  4. Servlet Threading Model

    Servlet Threading Model The scalability issues of Java servlets are caused mainly by the server thre ...

  5. SQL Server数据库层面自定义数据同步性能测试

    场景: A DB Server位于上海 B DB Server位于广州 现有特殊需求,需要通过数据链接将数据从A服务器表T1数据同步至B表T2 性能测试: 现模拟T1表9000笔数据 方式一:直接将9 ...

  6. zepto源码--filtered, contains,funcArg,setAttribute,className,deserializeVale--学习笔记

    几个方法 1.filtered 目标是对节点按照一定的选择器进行过滤. 如果传入了过滤选择器,则在nodes节点下,选择符合选择器的节点: 如果没有传入选择器,则返回节点本身,转化为zepto节点. ...

  7. 设计模式:享元模式(Flyweight)

    定   义:运用共享技术有效地支持大量细粒度的对象. 结构图: 内部状态:在享元对象内部并且不会随环境而改变的共享部分. 外部状态:随环境改变而改变的.不可共享的状态. Flyweight类,具体享元 ...

  8. Android.mk学习 笔记

    感谢: 原创作品 转载请注明出处:http://www.cnblogs.com/langlang/ 作者email: dayhappyhappy@163.com LOCAL_PATH := $(cal ...

  9. HAL层Camera模块Dump图片--工作积累

    Camera的raw data一般都是YUV420的格式,数据的特点是: YUV 4:2:0采样,每四个Y共用一组UV分量 YUV420格式: 先Y,后V,中间是U.其中的Y是w * h,U和V是w/ ...

  10. Android笔记:C memory copy

    socket通讯问题之一: 在c中按字节发送数据  比如设备1状态(1字节)值(1字节)设备2状态(1字节)值(1字节)....这种格式拆分的问题 在c中可以利用struct的 memory copy ...