unity 中 c# :如何unity怎么找到子物体当前子物体的父对象? 因为我有很多相同的父对象 用gameobject.find 不行

程序写累了,就来玩玩酷跑小游戏吧,嘿嘿。
雨松MOMO送你一首歌曲,嘿嘿。
#你好Unity3D#Unity获取游戏对象详解(来自我的长微博)
#你好Unity3D#Unity获取游戏对象详解(来自我的长微博)
围观83218次
编辑日期: 字体:
我觉得Unity里面的Transform 和 GameObject就像两个双胞胎兄弟一样,这俩哥们很要好,我能直接找到你,你也能直接找到我。我看很多人喜欢在类里面去保存GameObject对象。解决GameObject.Find()无法获取天生activie = false的问题。
private GameO
我觉得你最好不要保存GameObject ,而是去保存Transform ,因为Transform下的方法要比GameObject多,使用频率也要高很多。
其实我心里一直有个疑问,为什么unity不把GameObject和Transform合并成一个对象。
1.GameObject.Find()
通过场景里面的名子或者一个路径直接获取游戏对象。
GameObject root = GameObject.Find(“GameObject”);
我觉得如果游戏对象没再最上层,那么最好使用路径的方法,因为有可能你的游戏对象会有重名的情况,路径用“/”符号隔开即可。
GameObject root = GameObject.Find(“GameObject/Cube”);
GameObject.Find()使用起来很方便,但是它有个缺陷如下图所示,就是如果你的这个GameObject天生acive = false的话。那么你用GameObject.Find()是永远也无法获取它的对象的。如果对象都获取不到,那么对象身上脚本啊 组件啊啥的都是获取不到的,变成了没有意义的对象。
就这个问题我查过很多资料,最终也无果。。但是我用另外一个巧妙的办法可以解决它。(后面详解)或者你也可以提前把所有的游戏对象保存在内存中。
GameObject.Find()方法在游戏中的使用频率很高。但是它也很消耗性能,你可以想想它的原理肯定也是用类似递归的形式来做的,那么我们就要尽量更少的调用GameObject.Find()方法,可以把获取的游戏对象,保存在内存里,这是再好不过的选择了。 尤其是在Update方法中不要去 Find()游戏对象!!
2 .Transform.Find()
还记得上面我说过用GameObject无法获取天生acive = false的游戏对象,如果你用Transform.Find()的话就可以很好的获取,另外Unity还提供了一个Transform.FindChind()的方法,这个方法未来会被unity废弃,大家最好就别用了,用Transform.Find()可以取代。
如下代码,我们先获取顶级对象root 。接着用Find()去找它的子节点”xxxx”的对象,无论”xxxx”对象是否active = true 都是可以直接找到对象的。
&&&&&& GameObject root = GameObject.Find("GameObject");&&&&&& &&&&&&&&& GameObject xxxx =&&root.transform.Find("xxxx").gameObject;&&&&&&&&&&xxxx.SetActive(true);
Find()方法只能直接去找子节点,如果你想找 孙节点,那么可以用”/“符号把层级关系隔开,找起来很方便。同样无论”xxxx”对象是否active = true 都是可以直接找到对象的。
&&&&GameObject cube =&&root.transform.Find("xxxx/Cube").gameObject;
值得注意的是,unity规定了比如父节点active = true 并且子节点的 active = true 都满足的情况下 才能全部显示。使用Transform.Find()可以很方便的获取游戏对象,因为有了游戏对象,那么它身上的脚本啊组件啊什么的都可以很方便的获取到。
但是Transform.Find()必须要保证你的顶级父对象的activity = true。举个例子,你做了一个场景有一些地图你在场景里面预先activie = false了, 你希望在游戏中的某个时间点把它们都打开 setActive(true)
你可以把“map”节点放在一个active = true的GameObject上,无论是关闭 或者 显示 代码中写起来都很方便。 假如你的map节点就是顶级节点,那么它一旦天生acive = false ,那么你将无法得到它的对象,更无法设置它的属性了。
GameObject root = GameObject.Find(“GameObject”);
GameObject map =
root.transform.Find(“map”).gameO
map.SetActive(true);
3. unity 还提供了几个获取游戏对象的方法,但是我个人觉得使用频率不高,这里也简单说两句。
GameObject.FindGameObjectsWithTag(“tag”)
GameObject.FindWithTag(“tag”)
根据一个标记来获取游戏对象,返回一个 或者 一个数组,我个人觉得这个两个方法没啥用,因为既然需要用到标记那么相比这个游戏对象必然是非常特殊的一个,所以我会把它存在内存中。
Object.FindObjectOfType
Object.FindObjectsOfType
Resources.FindObjectsOfTypeAll
根据一个类型返回Object,比如 GameObject 、Texture、Animation 、甚至还可以是你自己写的一个脚本 的范型。它找起来很方便,可以返回一个 或者一个数组。 我觉得这几个方法其实游戏中也没啥用,不过在编辑器中使用的确实很频繁,比如你要做批量检查场景的工具,查找场景中有没有使用某个特殊类型的对象。 或者查看内存的占用量,看看当前内存中那些Texture没有被释放掉。 等等。
还有一个方法,如果你知道自对象的索引,还可以用下面的方法来获取,参数是index的索引。
transform.GetChild(0)
找到了一个即使隐藏root节点gameObject也能进行查找的方法。
1234567891011121314151617181920212223242526
GameObject[] pAllObjects = (GameObject[])Resources.FindObjectsOfTypeAll(typeof(GameObject));
&&&&&&&& foreach (GameObject pObject in pAllObjects)&&&&&&&& {
if (pObject.transform.parent != null)&&&&&&&&&&&& {&&&&&&&&&&&&&&&&&&&& continue;&&&&&&&&&&&& }&
if (pObject.hideFlags == HideFlags.NotEditable || pObject.hideFlags == HideFlags.HideAndDontSave)&&&&&&&&&&&& {&&&&&&&&&&&&&&&& continue;&&&&&&&&&&&& }&
if (Application.isEditor)&&&&&&&&&&&& {&&&&&&&&&&&&&&&& string sAssetPath = AssetDatabase.GetAssetPath(pObject.transform.root.gameObject);&&&&&&&&&&&&&&&& if (!string.IsNullOrEmpty(sAssetPath))&&&&&&&&&&&&&&&& {&&&&&&&&&&&&&&&&&&&& continue;&&&&&&&&&&&&&&&& }&&&&&&&&&&&& }&
Debug.Log(pObject.name);&&&&&&&& }
本文固定链接:
转载请注明:
雨松MOMO提醒您:亲,如果您觉得本文不错,快快将这篇文章分享出去吧 。另外请点击网站顶部彩色广告或者捐赠支持本站发展,谢谢!
作者:雨松MOMO
专注移动互联网,Unity3D游戏开发
如果您愿意花10块钱请我喝一杯咖啡的话,请用手机扫描二维码即可通过支付宝直接向我捐款哦。
您可能还会对这些文章感兴趣!在unity中如何用C#实现相机相对于它的父物体,一直处于后方一定距离的位置? - 知乎6被浏览660分享邀请回答22 条评论分享收藏感谢收起public GameO
public float distance=5.0f;
在Update()里设置和target的间距与自身朝向if(target!=null)
float targetRotationY = target.transform.eulerAngles.y;
float targetAngleY = targetRotationY * Mathf.PI / 180f;
float unitVectorX = Mathf.Sin(targetAngleY);
float unitVectorZ = Mathf.Cos(targetAngleY);
Vector3 unitVector = new Vector3(unitVectorX,0, unitVectorZ);
Vector3 distanceVector = distance * unitV
transform.position = target.transform.position - distanceV
transform.localEulerAngles = new Vector3(0, 0, 0);
在其他脚本里:预设体生成后绑定相机为其子物体,并指定相机脚本中的target为它的父物体。12 条评论分享收藏感谢收起18031人阅读
Unity3d(6)
一 创建和使用脚本
GameObject的行为都是被附加到其上面的组件控制,脚本本质上也是一个组件。
在unity中创建一个脚本,默认内容如下:
using UnityE
using System.C
public class MainPlayer : MonoBehaviour {
// Use this for initialization
void Start () {
// Update is called once per frame
void Update () {
一个脚本通过实现一个派生自”MonoBehaviour”的类来与unity的内部工作机制建立联系。可以将新创建的组件类型的类作为一个蓝图,该类作为一个新类型的组件被附加到游戏对象。每次将一个脚本组件附加到游戏对象,都会创建一个该蓝图定义的对象的实例。创建的脚本文件的文件名必须与里面的类名相同,这样才能将其附加到游戏对象上。
需要注意到的主要事情是,该类中定义的两个函数。Update函数处理游戏对象的帧更新相关的操作。这个可能包括移动,触发动作以及对用户输入的反馈,基本上在游戏过程期间需要处理的任何事情都可以在这里面处理。Start函数在游戏开始前被unity调用(例如,在Update被第一次调用之前),因而是一个进行初始化操作的理想位置。
可能会疑惑为什么不把初始化操作放在类的构造函数中,这是因为对象的构造是由编辑器处理的,在游戏开始的时候并不如想象中那样会发生。如果为一个脚本组件定义构造器,它会和unity的正常操作发生干扰从而导致一些问题。
脚本被创建之后是处于不激活状态的,只有将它的一个实例附加到一个游戏对象之后代码才会被激活。同时,一个游戏对象的一个类型的组件只能有一个,也就是可以附加到游戏对象的脚本组件不能超过一个。
就是指的类的成员变量,只不过在unity这里将成员变量设为公有的时候,将其附加到游戏对象后,可以在游戏对象的监视面板中的脚本组件那栏里面看到该公有变量,也就是说可以在编辑器里面直接对该公有变量进行赋值,同时在debug状态下也可以在面板中看到它的值。
unity在编辑器中显示类的成员变量的时候,在类名中遇到(不是第一个字母的)大写字母的时候,会在该字母前加入一个空格,例如在类里面定义个公有成员变量名为”TestVar”,在编辑器中显示的变量名为”Test
Var”,中间加了一个空格,但是这只是一个unity显示变量的方式,真正在代码中访问这个变量仍然应该使用”TestVar”这个名字。
3 事件函数
unity中的脚本并不像传统意义上的程序,即在一个循环中连续的运行直到完成任务后再退出,而是unity通过调用定义在脚本内的某个函数,间断的将控制权交给一个脚本。一旦一个函数完成执行,控制权被交还给unity。这些函数就是所知的事件函数,因为它们由unity调用用于响应发生在游戏过程中的事件。unity支持的所有事件都定义在MonoBehaviour这个里面,可以在该类的中看到一系列的事件。下面介绍几个最常见、最重要的事件。
&1& 常规更新事件(Regular Update Events)
一个游戏更像一个在线生成动画帧的动画。游戏编程中的一个关键观念就是在每帧渲染前对游戏对象的位置、状态和行为进行改变。Update函数是unity中放置这类代码的主要地方,它在每帧被渲染前、同时也在动画被计算前调用。
update事件函数
void Update() {
float distance = speed * Time.deltaTime * Input.GetAxis(&Horizontal&);
transform.Translate(Vector3.right * speed);
物体引擎也以一个和帧渲染类似的方式每隔一个离散时间段进行更新。另一个叫FixedUpdate的函数在每次物理更新之前调用。由于物理更新和帧更新并不使用相同的频率更新,如果将上面的代码放到FixedUpdate函数而不是Update函数的话,可以得到更加精确的结果(即FixedUpdate会以一个比Update更加稳定的帧率运行)。
void FixedUpdate() {
Vector3 force = transform.forward * driveForce * Input.GetAxis(&Vertical&);
rigidbody.AddForce(force);
同时在场景中所有对象的Update和FixedUpdate调用之后,以及所有的动画计算之后仍然可能有需要进行一些额外的改变。一个例子就是在一个摄像头需要监视一个目标对象的时候,对摄像机朝向的调整必须在目标对象已经移动之后。另一个例子是在在需要用脚本代码来覆盖动画效果的场合(比如,使角色的头看向场景中的目标对象)。LateUpdate函数可以用于这些场合。
&2& &初始化事件
在游戏中的任何更新函数之前调用初始化代码是经常用到的,初始化函数Start在物体的第一帧更新或者物理更新之前被调用。Awake函数在场景被加载的时候,场景中的每个对象上的该函数都会被调用,所有的Awake函数都会在第一个Start函数(有好多对象的话就有好多个Start函数)调用之前被调用。这就意味着Start函数中的代码也可以使用发生在Awake阶段里面的初始化后的数据。
&3& GUI事件
unity有一个用于渲染GUI控件的系统,这些GUI控件处于场景之上并且响应鼠标事件。这个代码处理起来和常规的帧更新有些不大一样,因而它需要放到OnGUI函数中,周期性的被调用。
void OnGUI() {
GUI.Label(labelRect, &Game Over&);
同时当发生鼠标单击场景中的游戏对象的时候可以检测这些消息。这个可以用于调整武器或者显示当前在鼠标下面的角色信息。一系列的OnMouseXXX事件函数可以用于和用户的鼠标行为交互。
&4&物理事件
物理引擎会通过调用物体上的事件函数来报告发生的与另一物体的碰撞事件。OnCollisionEnter、OnCollisionStay和OnCollisionExit函数分别在刚接触,持续和离开(broken)的时候调用。当碰撞器被配置成触发器的时候发生碰撞后对应的OnCollisionEnter、OnCollisionStay和OnCollisionExit会被调用。假如在物理更新期间有多于一个接触被检测到这些函数可能会被多次按顺序调用(即调用完一轮再来一轮)。
二 控制游戏对象
在unity中,可以在监视器面板中修改物体的组件属性,但是更多的时候,需要使用脚本来进行这些操作。
1 访问组件
最常见的一个情形是需要使用脚本访问附加到相同游戏对象上的另一个组件(当前脚本就是一个组件,其他的组件也就是另一个组件了)。在引言中提到过,一个组件实质上是一个类的实例,因而首先需要做的是获取想要操作的组件实例的引用。这个通过GetComponent函数来实现。典型的,可能会想要将一个组件赋值给一个变量,如下代码所示
void Start () {
Rigidbody rb = GetComponent&Rigidbody&();
一旦获取了组件实例的引用,就可以对它的属性进行想要的操作,同时也可以调用它之上的一些功能函数。
如果想要访问另一个的脚本文件,也可以使用GetComponent,只需使用脚本的类名作为该函数的组件类型参数(因为脚本本来就也是一个组件)。
如果想要去获取一个并没有添加到当前游戏对象的组件,GetComponent函数会返回null,如果试图去改变一个null对象上的任何值,将会发生null引用错误。
由于一些组件类型经常使用,unity提供了一些内置的变量来访问它们,例如可以使用下面的代码
void Start () {
transform.position = Vector3.
而不用使用GetComponent去获得Transform组件,所有的内置组件变量列表在里面有。
2 访问其他对象
虽然游戏对象有的时候都是各自处理,使用代码进行跟踪其他物体是常有的事。例如,一个追赶的敌人可能需要知道玩家的位置,unity提供了一系列不同的方法来获取其他对象,各适合不同的场合。
&1& 将对象链接到变量
最直接的办法是将一个游戏对象添加到脚本的公有成员变量上,直接在编辑器中将需要访问的游戏对象拖到对应脚本组件的那个公有成员变量上,unity会自动根据变量的类型将添加的游戏对象中相同的组件类型映射到该变量。
例如将一个游戏对象拖给一个Transform的成员变量,就会自动的将游戏对象的Transform组件和该变量映射起来。
直接将对象和变量链接起来在处理需要有永久链接的对象的时候是最有用的方法。同时也可以使用一个数组变量和几个相同类型的对象链接起来,但是这种链接必须在unity编辑器中完成,而不能在运行时进行。通常使用下面的两种方法来在运行时定位对象。
&2& 查找子物体
有的时候,一个游戏场景中可能会用到很多同一类型的对象,例如敌人、路点(waypoints)和障碍物。这些对象在游戏中需要由一个特定的脚本来监视和响应。这个时候使用变量来链接这些对象太过麻烦不好操作。对于这种情况,通常更好的方法是将一系列的对象添加到一个父对象下面,这些子对象可以通过使用父对象的Transfrom组件来获得。
public class WaypointManager : MonoBehaviour {
void Start() {
waypoints = new Transform[transform.childCount];
int i = 0;
for (Transform t in transform) {
waypoints[i++] =
同时也可以使用Tranfrom.Find来查找某个具体的子对象。使用Transform来进行对象查找操作是因为每一个游戏对象都有Transfrom组件。
&3& 通过名称或标签访问对象
只要有一些信息,在层级场景中的任何位置定位到该游戏对象是可能的。单个对象可以通过GameObject.Find函数进行查找。如下:
void Start() {
player = GameObject.Find(&MainHeroCharacter&);
某个对象或者一系列的对象也可以分别通过GameObject.FindWithTag和GameObject.FindObjectsWidthTag函数进行定位。
&4&查找特定类型的对象
staticFindObjectOfType(Type
返回指定类型对象中的第一个活动的加载对象
需要注意的是这个函数很慢(可能是由于要在整个场景中进行遍历),不推荐每一帧都使用这个函数,在大多数情况下可以使用单件模式
Camera cam = FindObjectOfType(typeof(Camera)) as C
由于该函数返回的类型是Object,所以需要使用as进行一下强制转换。
static Object[] FindObjectsOfType(Type type);
返回指定类型的加载活动对象的列表,速度也慢
hinges = FindObjectsOfType(typeof())
三 创建和销毁对象
在运行时创建和销毁对象是常有的事。在unity中,可以使用Instantiate函数对现有的一个对象做一个拷贝来创建一个新的游戏对象。
public GameO
void Start() {
for (int i = 0; i & 5; i++) {
Instantiate(enemy);
值得注意的是用于进行拷贝的对象并不一定需要放置在场景中。更普遍的做法是将一个预设(Prefab)拖到脚本的对应公有成员变量上,实例化的时候直接对这个成员变量进行实例化即可。
同时也有一个Destroy函数在帧更新函数完成后或设定的一个延时时间后销毁一个对象。
void OnCollisionEnter(otherObj: Collision) {
if (otherObj == &Missile&) {
Destroy(gameObject,.5f);
注意到Destroy函数可以销毁单独的组件而不对游戏对象本身产生影响,一个通常范的错误是
Destroy(this);
这句代码仅仅效果脚本组件,而不会销毁该脚本所附加在的对象。
四 协程(Coroutines)
当调用一个函数的时候,它会在返回前运行到结束位置。
根据前面的知识可以知道,几乎所有的函数都由unity在帧更新的时候调用一次,然后到下一帧后再调用一次,那这个时候在函数中写一些循环体类的代码想要达到产生动画的效果可能会失败。如下
void Fade() {
for (float f = 1f; f &= 0; f -= 0.1f) {
Color c = renderer.material.
renderer.material.color =
这是想要产生一个透明渐变的效果,但是由于Fade函数会在一个frame update时间片中全部执行完,达不到渐变效果。要达到这个效果,可以在帧更新函数中进行处理即可。但是,对于这类情形更方便的做法是使用coroutine。
一个coroutine就像一个可以暂停执行并将控制权返回给unity的函数,但是在下一帧的时候又可以在它停止的位置继续执行。在C#中,这样声明一个coroutine:
IEnumerator Fade() {
for (float f = 1f; f &= 0; f -= 0.1f) {
Color c = renderer.material.
renderer.material.color =
实质上它是一个返回类型为IEnumerator的函数,同时在函数体中增加了yield return这句代码。yield
return这行就是会在执行的时候暂停、在下一帧的时候恢复执行的位置。要启动coroutine,需要使用StartCorutine函数。
void Update() {
if (Input.GetKeyDown(&f&)) {
StartCoroutine(&Fade&);
默认的情况下,一个coroutine在它暂停后的下一帧恢复,但是也可以使用WaitFroSeconds来引入一个延时。
IEnumerator Fade() {
for (float f = 1f; f &= 0; f -= 0.1f) {
Color c = renderer.material.
renderer.material.color =
yield return new WaitForSeconds(.1f);
这个可以用于产生一个随时间变化的效果,同时也是一个用于进行优化的有效方法。游戏中的许多人物需要周期性的执行,最常用的做法是将它们包含在Update函数中。但是Update函数通常每秒会调用多次。当有的任务并不需要这么频繁的被调用的时候,可以将它放在一个coroutine中按一定时间进行调用,而不是每一帧都调用。一个这样的例子就是用于在游戏中如果有敌人接近的话就提示玩家,代码如下
function ProximityCheck() {
for (int i = 0; i & enemies.L i++) {
if (Vector3.Distance(transform.position, enemies[i].transform.position) & dangerDistance) {
IEnumerator DoCheck() {
ProximityC
yield return new WaitForSeconds(.1f);
当有很多敌人的时候,使用coroutine0.1秒执行一次靠近检查,可以减少大量的计算量。
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:317364次
积分:3058
积分:3058
排名:第11346名
原创:48篇
转载:20篇
评论:27条
(3)(1)(2)(1)(6)(5)(3)(1)(1)(1)(8)(2)(6)(2)(3)(2)(4)(4)(1)(8)(3)(3)

我要回帖

更多关于 unity 找到所有子物体 的文章

 

随机推荐