C# 如何使用GameObject.find在Unity3D中查找非活动对象?

C# 如何使用GameObject.find在Unity3D中查找非活动对象?,c#,unity3d,C#,Unity3d,我需要使用C#在Unity3D中查找非活动对象 我有64个对象,每当我单击一个按钮,它就会在运行时激活/禁用相应按钮的对象。此时如何查找非活动对象?好吧,使用游戏对象。查找(…)将永远不会返回任何非活动对象: 此函数仅返回活动的游戏对象 即使你可以,你也要把这些昂贵的电话费降到最低 查找非活动的游戏对象有一些“技巧”,例如使用调用(尽管使用时应特别小心) 但最好的办法是编写自己的管理代码。这可以是一个简单的类,其中包含一个对象列表,您可能希望在某个时候找到并使用这些对象。您可以在第一次加载时将对

我需要使用C#在Unity3D中查找非活动对象


我有64个对象,每当我单击一个按钮,它就会在运行时激活/禁用相应按钮的对象。此时如何查找非活动对象?

好吧,使用
游戏对象。查找(…)
将永远不会返回任何非活动对象:

此函数仅返回活动的游戏对象

即使你可以,你也要把这些昂贵的电话费降到最低

查找非活动的游戏对象有一些“技巧”,例如使用调用(尽管使用时应特别小心)


但最好的办法是编写自己的管理代码。这可以是一个简单的类,其中包含一个对象列表,您可能希望在某个时候找到并使用这些对象。您可以在第一次加载时将对象放入其中。或者在变为活动或非活动时添加/删除它们。无论您的特定场景需要什么

虽然这不是正确的答案,但我就是这么做的。
1) 将脚本附加到(非活动)游戏对象,然后将其保持活动状态,而不是设置为非活动状态。
2) 将他们安置在场景之外的某个地方。
3) 在脚本中设置一个表示不活动的标志。
4) 在Update()中检查此非活动标志,如果为false,则跳过函数调用。
5) 需要时,将对象放置在适当的位置,并将标志设置为活动状态

这将是一个性能问题,但这是迄今为止我能想到的唯一解决办法。

如果您有父对象(只是扮演文件夹角色的空对象),您可以找到如下活动和非活动对象:

this.playButton = MainMenuItems.transform.Find("PlayButton").gameObject;
MainMenuItems-是您的父对象

请注意FIDER()是慢方法,所以考虑使用对象引用或用GAMIONS组织字典集合,您需要经常访问

祝你好运

您可以使用谓词。 只需获取游戏对象并使用谓词检查它们,如下所示:

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;
}
public List FindInactiveGameObjects()
{
GameObject[]all=GameObject.FindObjectSoftType();//在场景中获取所有对象
List objs=新列表();
foreach(gameobjectobj总计)//创建一个列表
{
objs.Add(obj);
}
Predicate inactiveFinder=新谓词((GameObject go)=>{return!go.activeInHierarchy;});//创建查找器
列出结果=objs.FindAll(inactiveFinder);//并查找非活动的结果
返回结果;
}
不要忘记使用系统使用System.Collections.Generic

首先 通常应避免使用
Find
或其变体

实际上,它们从来都不是真正需要的,只是一个“热修复”,用于覆盖实现“惰性”

通常从一开始,存储和传递所需的引用总是更好的方法

特别是在您的情况下,您似乎有固定数量的对象,因此您可能已经在某个“管理器”组件中引用了它们,并将它们存储在列表或数组中(您可以通过索引获取引用),甚至可以通过
字典
(然后您还可以通过名称获取相应的引用-您可以在下面找到一个示例)


权变措施 有替代解决方案(
FindObjectsWithTag
FindObjectsOfType
),但它总是非常昂贵(尽管大多数
Find
变体都很昂贵)

例如,您还可以使用“手动”迭代场景中的所有对象

返回场景中的所有根游戏对象

然后搜索它们直到找到你的目标。这样你也可以得到非活动的游戏对象

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.gameObject;

        result = FindRecursive (child.gameObject, search);

        if(result) break;
    }

    return result;
}
但是,当然,这应该被强烈地避免,这样的深度搜索的使用减少到最低限度


我会怎么做 另一种方法——在我看来,这是最好的方法——可以将某个组件附加到所有对象上,并像前面所说的那样将所有引用存储在字典中,例如

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);
    }
}
此外,该组件需要至少启用一次,以便调用
Start

另一种选择是使用

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);
}

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

在提出此问题后的几年中,Unity提供了您所需要的东西。至少,这正是我需要的。为未来的人们张贴在这里

要查找特定类型的对象,无论它位于活动或非活动游戏对象上,您可以使用
FindObjectsOfType(true)

只有当
inactiveObjects
设置为
true
时,才包括附加到非活动游戏对象的对象

因此,只需像您经常使用的那样使用它,还可以传入
true

以下代码需要
系统。Linq

List<SpriteRenderer> onlyActive = GameObject.FindObjectsOfType<SpriteRenderer>().ToList();

List<SpriteRenderer> activeAndInactive = GameObject.FindObjectsOfType<SpriteRenderer>(true).ToList();

List<SpriteRenderer> onlyInactive = GameObject.FindObjectsOfType<SpriteRenderer>(true).ToList()
    .FindAll(sr => !sr.gameObject.activeInHierarchy);
List onlyActive=GameObject.FindObjectsOfType().ToList();
List activeAndInactive=GameObject.FindObjectsOfType(true.ToList();
List ONLYNINActive=GameObject.FindObjectsOfType(true).ToList()
.FindAll(sr=>!sr.gameObject.activeInHierarchy);

第一个列表仅包括活动游戏对象上的spriterenders,第二个列表包括活动和非活动游戏对象上的spriterenders,第三个列表使用System.Linq仅包括非活动游戏对象上的spriterenders。

尽管第一步
FindObjectsOfType
也将搜索限制为已启用对象的子对象。使用
System.Linq
在场景根级别上找不到禁用的对象,您可以说
List results=all.ToList().FindAll(go=>!go.activeInHierarchy)
@shieldgenerator7是的,这是一个谓词,与我所做的一样,但代码较少。请注意,
transform.Find
不会在变换层次结构下执行递归下降操作。
因此它将不适用于其他嵌套对象。
List<SpriteRenderer> onlyActive = GameObject.FindObjectsOfType<SpriteRenderer>().ToList();

List<SpriteRenderer> activeAndInactive = GameObject.FindObjectsOfType<SpriteRenderer>(true).ToList();

List<SpriteRenderer> onlyInactive = GameObject.FindObjectsOfType<SpriteRenderer>(true).ToList()
    .FindAll(sr => !sr.gameObject.activeInHierarchy);