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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 15:22:08  来源:igfitidea点击:

How to find inactive objects using GameObject.Find(" ") in Unity3D?

c#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 Findor 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 Findvariants 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 Startis 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.

您也可以在例如生成非活动对象后调用它。