C# 如何在 Unity3D 中使用 GameObject.Find(" ") 查找非活动对象?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19584127/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to find inactive objects using GameObject.Find(" ") in Unity3D?
提问by user2914179
I needed to find inactive objects in Unity3D using C#.
我需要使用 C# 在 Unity3D 中查找非活动对象。
I have 64 objects, and whenever I click a button then it activates / inactivates objects for the corresponding button at runtime. How can I find inactive objects at this time?
我有 64 个对象,每当我单击一个按钮时,它就会在运行时激活/停用相应按钮的对象。此时如何找到不活动的对象?
回答by Bart
Well, using GameObject.Find(...)
will never return any inactive objects. As the documentation states:
好吧, usingGameObject.Find(...)
永远不会返回任何非活动对象。正如文档所述:
This function only returns active gameobjects.
此函数仅返回活动的游戏对象。
Even if you could, you'd want to keep these costly calls to a minimum.
即使可以,您也希望将这些昂贵的电话降到最低。
There are "tricks" to finding inactive GameObjects, such as using a Resources.FindObjectsOfTypeAll(Type type)
call (though that should be used with extreme caution).
有一些“技巧”可以找到不活动的游戏对象,例如使用Resources.FindObjectsOfTypeAll(Type type)
调用(尽管应该非常谨慎地使用)。
But your best bet is writing your own management code. This can be a simple class holding a list of objects that you might want to find and use at some point. You can put your object into it on first load. Or perhaps add/remove them on becoming active or inactive. Whatever your particular scenario needs.
但最好的办法是编写自己的管理代码。这可以是一个简单的类,其中包含您在某个时候可能想要查找和使用的对象列表。您可以在第一次加载时将对象放入其中。或者也许在变得活跃或不活跃时添加/删除它们。无论您的特定场景需要什么。
回答by kay
Although its not the correct answer, but this is what I did in my case.
1) Attach a script to (inactive) game objects and instead of setting then inactive keep it active.
2) Position them out of the scene somewhere.
3) Set a flag in the script which says inactive.
4) In Update() check for this inactive flag and skip function calls if false.
5) When needed the object, position it at the proper place and set the flag active.
虽然它不是正确的答案,但这就是我在我的情况下所做的。
1)将脚本附加到(非活动)游戏对象,而不是设置然后非活动保持活动状态。
2)将它们放置在场景之外的某个地方。
3) 在脚本中设置一个标记为非活动状态。
4) 在 Update() 中检查这个非活动标志,如果为假则跳过函数调用。
5)当需要对象时,将其放置在适当的位置并设置标志为活动状态。
It will be a bit of a performance issue but that's the only workaround I could think of so far.
这将是一个性能问题,但这是迄今为止我能想到的唯一解决方法。
回答by Dzianis Yafimau
If you have parent object (just empty object that plays role of a folder) you can find active and inactive objects like this:
如果您有父对象(只是充当文件夹角色的空对象),您可以找到活动和非活动对象,如下所示:
this.playButton = MainMenuItems.transform.Find("PlayButton").gameObject;
MainMenuItems - is your parent object.
MainMenuItems - 是您的父对象。
Please note that Find() is slow method, so consider using references to objects or organize Dictionary collections with gameobjects you need access very often
请注意 Find() 是一种缓慢的方法,因此请考虑使用对对象的引用或使用您需要经常访问的游戏对象组织字典集合
Good luck!
祝你好运!
回答by Bamdad
You can use Predicates. Just get the gameObjects and check them whith a Predicate as below:
您可以使用谓词。只需获取游戏对象并使用 Predicate 检查它们,如下所示:
public List<GameObject> FindInactiveGameObjects()
{
GameObject[] all = GameObject.FindObjectsOfType<GameObject> ();//Get all of them in the scene
List<GameObject> objs = new List<GameObject> ();
foreach(GameObject obj in all) //Create a list
{
objs.Add(obj);
}
Predicate inactiveFinder = new Predicate((GameObject go) => {return !go.activeInHierarchy;});//Create the Finder
List<GameObject> results = objs.FindAll (inactiveFinder);//And find inactive ones
return results;
}
and don't forget using System;using System.Collections.Generic;
并且不要忘记使用系统;使用 System.Collections.Generic;
回答by derHugo
First of all
首先
In general any usage of Find
or it's variants should be avoided.
一般来说,Find
应避免使用或其变体。
Actually they are never really required but only a "hot-fix" used to cover an implementation "laziness".
实际上,它们从来都不是真正需要的,而只是用于覆盖实现“懒惰”的“热修复”。
Usually from the beginning storing and passing on required references is always the better approach.
通常从一开始就存储和传递所需的引用总是更好的方法。
Especially in your case you seem to have a fix amount of objects so you could probably already reference them all in a certain "manager" component and store them in a list or array (them you can get a reference by index) or even a Dictionary<string, GameObject>
(then you can also get the according reference by name - you can find an example below).
特别是在您的情况下,您似乎拥有固定数量的对象,因此您可能已经在某个“管理器”组件中引用了它们,并将它们存储在列表或数组中(您可以通过索引获取它们)甚至是Dictionary<string, GameObject>
(那么您还可以通过名称获取相应的引用 - 您可以在下面找到一个示例)。
Workarounds
解决方法
There are alternative solutions (FindObjectsWithTag
, FindObjectsOfType
) but it will always be quite expensive (though most of the Find
variants are expensive anyway).
有替代解决方案 ( FindObjectsWithTag
, FindObjectsOfType
) 但它总是相当昂贵(尽管大多数Find
变体无论如何都很昂贵)。
You could e.g. also "manually" iterate through all objects in the scene using Scene.GetRootGameObjects
例如,您还可以使用“手动”遍历场景中的所有对象 Scene.GetRootGameObjects
Returns all the root game objects in the Scene.
返回场景中的所有根游戏对象。
And then search through them until you find your object. This way you get also inactive GameObject.
然后搜索它们,直到找到您的对象。这样你也会得到不活动的游戏对象。
public static GameObject Find(string search)
{
var scene = SceneManager.GetActiveScene();
var sceneRoots = scene.GetRootGameObjects();
GameObject result = null;
foreach(var root in sceneRoots)
{
if(root.name.Equals(search)) return root;
result = FindRecursive(root, search);
if(result) break;
}
return result;
}
private static GameObject FindRecursive(GameObject obj, string search)
{
GameObject result = null;
foreach(Transform child in obj.transform)
{
if(child.name.Equals(search) return child;
result = FindRecursive (child, search);
if(result) break;
}
return result;
}
But ofcourse this should be strongly avoided and the usage of such deep searches reduced to a minimum!
但是,当然应该强烈避免这种情况,并将这种深度搜索的使用减少到最低限度!
What I would do
我会怎么做
Another way - in my eyes the best approach here - could be to have a certain component attached to all your objects and actually store all the references once as said before in a dictionary like e.g.
另一种方式 - 在我看来这里最好的方法 - 可能是将某个组件附加到您的所有对象,并像之前所说的那样将所有引用实际存储在字典中,例如
public class FindAble : MonoBehaviour
{
private static readonly Dictionary<string, GameObject> _findAbles = new Dictionary<string, GameObject>();
public static GameObject Find(string search)
{
if(!_findAbles.ContainsKey(search)) return null;
return _findAbles[search];
}
private IEnumerator Start()
{
// Wait one frame
// This makes it possible to spawn this object and
// assign it a different name before it registers
// itself in the dictionary
yield return null;
if(_findAbles.ContainsKey(name))
{
Debug.LogError($"Another object with name /"{name}/" is already registered!", this);
yield break;
}
_findAbles.Add(name, gameObject);
}
private void OnDestroy ()
{
if(_findAbles.ContainsKey(name))
{
_findAbles.Remove(name);
}
// Optionally clean up and remove entries that are invalid
_findAbles = _findAbles.Where(kvp => kvp.Value).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
}
}
and then use it like
然后像这样使用它
var obj = FindAble.Find("SomeName");
if(obj)
{
// ...
}
Also for this the component would need to be enabled at least once so Start
is called.
同样为此,组件至少需要启用一次,因此Start
被调用。
Again an alternative would be to have instead a
同样,另一种选择是拥有一个
public void Initialize(string newName)
{
if(_findAbles.ContainsKey(name))
{
Debug.LogError($"Another object with name /"{name}/" is already registered!", this);
return;
}
name = newName;
_findAbles.Add(name, gameObject);
}
which you could call also after e.g. spawning an inactive object.
您也可以在例如生成非活动对象后调用它。