【问题标题】:Using foreach loop to find GameObjects that are children of something else (Unity3d)使用 foreach 循环查找属于其他对象的游戏对象(Unity3d)
【发布时间】:2018-08-22 09:07:10
【问题描述】:

我正在制作一个视频游戏,虽然我有一个现有的通用 C# 代码,但我现在必须将它移到 Unity。我对通用 C# 有一些基础知识,但我刚刚开始学习 Unity 编码方式。

对于初学者,我想编写一个代码,将所有游戏区域定位到正确的位置,然后使它们不可见。是的,不要惊讶,他们需要都在同一个地方。

区域可以有三种尺寸选择,我称它们为小、中和大。小面积和大面积都必须手动写入。

 List <GameObject> SmallAreas = new List<GameObject>();

void DefineSmallAreas()
{
    SmallAreas.Add(areaConfirmLoad);
    SmallAreas.Add(areaConfirmQuit);
    SmallAreas.Add(areaConfirmSave);
    SmallAreas.Add(areaGameSaved);
    SmallAreas.Add(areaSave);
    SmallAreas.Add(areaLoad);   
}

大面积也是如此。

现在,所有其他领域,都是中等的,而且数量很多。

所以,我想检查所有属于“areaContainer”子项的游戏对象,检查它们的名称是否以“area”开头,如果是,我想将它们添加到 MediumAreas 列表中。强>

我就是这样尝试的:

void DefineMediumAreas()
{
    GameObject areaContainer = GameObject.Find("areaContainer");

    foreach (GameObject thisObject in areaContainer)
    {
        char[] a = thisObject.Name.ToCharArray();
        if (a.Length >= 4)
        {
        char[] b = { a[0], a[1], a[2], a[3] };
        string thisObjectType = new string(b);
                        (if (thisObjectType == "area")&&(!(SmallAreas.Contains(thisObject))
                        &&(!(LargeAreas.Contains(thisObject)))
                        {
                            MediumAreas.Add(thisObject);
                        }
    }   
}

然而,这显示了一个错误,“areaContainer”不能以这种方式使用,我现在无法访问 Unity,因此无法复制确切的消息。我认为它类似于“Gameobject 没有 IEnumerator”。

我确实尝试用谷歌搜索更好的方法,并找到了一种叫做“转换”的东西。

 foreach(Transform child in transform)
 {
     Something(child.gameObject);
 }

我不明白的是,如何在我的具体情况下使用这种“转换”。

如果这个问题很傻,请不要生我的气,我对Unity很陌生,必须从头开始学习。

还有一个小问题。将物体变为隐形的工作是否有效:

    foreach(GameObject thisObject in MediumAreas)
    {
        thisObject.position = MediumVector;
        thisObject.GetComponent<Renderer>().enabled = false;
    }

MediumVector 是对象必须移动到的位置,它似乎正在工作。

【问题讨论】:

  • 附注:您可以使用 thisObject.Name.StartsWith("area") 检查名称是否以“area”开头。
  • 另一个注意事项:如果您打算使用 Unity3D 做任何重要的事情,您绝对需要了解什么是变换以及如何使用它。这是引擎的关键概念之一。
  • 这不是你使用foreach的方式。 areaContainer 是否有一个属性是 GameObjects 的集合?如果是这样,请使用foreach(GameObject thisObject in areaContainer.collectionsOfObjects){...}(至少在 C# 中是这样的)
  • areaContainer 是包含所有其他对象的对象。另外,感谢您谈论“Name.StartsWith”

标签: c# loops unity3d foreach gameobject


【解决方案1】:

可以这样做:foreach(Transform child in transform)

因为Transform 类实现了IEnumerable 并且有一些机制使您能够使用foreach 循环访问子游戏对象。


很遗憾,您不能这样做:foreach (GameObject thisObject in areaContainer)

因为areaContainerGameObject 并且此实现不适用于GameObject 类。这就是您收到此错误的原因:

foreach 语句不能对类型变量进行操作 'UnityEngine.GameObject' 因为 'UnityEngine.GameObject' 没有 包含“GetEnumerator”的公共定义

要修复它,请在找到 GameObject 后将循环更改为使用 Transform

GameObject areaContainer = GameObject.Find("areaContainer");
foreach (Transform thisObject in areaContainer.transform){}

完整代码:

List<GameObject> MediumAreas = new List<GameObject>();

void DefineMediumAreas()
{
    GameObject areaContainer = GameObject.Find("areaContainer");
    foreach (Transform thisObject in areaContainer.transform)
    {
        //Check if it contains area
        if (thisObject.name.StartsWith("area"))
        {
            //Add to MediumAreas List
            MediumAreas.Add(thisObject.gameObject);
        }
    }
}

【讨论】:

  • 其他人似乎都看过foreach (GameObject thisObject in areaContainer)这个实际问题。我用foreach (Transform thisObject in areaContainer.transform){} 做了一个测试,它有效,所以这就是问题所在。使用 transform 属性解决了这个问题。
  • @PassetCronUs 没错。人们提到的其他方式也应该起作用。我只是想指出为什么代码不起作用,并以最少的代码更改添加壁橱类似的解决方案。
  • @Programmer 能否也回复一下如何让物体不可见?或者我使用的代码应该可以工作?
  • 这取决于对象的类型。那是什么类型的物体? “检查器”选项卡的屏幕截图会很有帮助,因为它将包含描述类型的组件
  • 您的回答有效,与上次评论相同。我将其标记为最佳答案。非常感谢!
【解决方案2】:

有多种方法可以解决您的问题。其中之一是使用标签。只需用一些标签标记您的 MediumArea 预制件,然后您就可以找到所有带有 FindGameObjectsWithTag(string) (Unity Docs) 的标签游戏对象。然后你可以像这样填充你的集合:

MediumAreas.AddRange(FindGameObjectsWithTag("MediumArea"));

第二种方法可能是查找具有相同附加脚本FindObjectsOfType&lt;T&gt;() (Unity Docs) 的所有对象。这在您搜索相同类型的实例时很有用,例如 Medium Area

假设您有一个 Area 脚本

public class Area : MonoBehaviour {
    public AreaSize Size;    // AreaSize is Enum
}

然后您可以简单地找到您的区域,例如:

var allAreas = FindGameObjectsOfType<Area>();
var mediumAreas = allAreas.Where(e => e.Size == AreaSize.Medium); // using System.Linq;

【讨论】:

  • 有意思,我去看看。我想所有其他对象也可以这样做?
【解决方案3】:

我创建了一个项目来回答你的问题,最终的结果会是这样的:

如您所见,我创建了一个名为“areaContainer”的游戏对象,并添加了 3 个具有各自名称的子对象:“area01”、“area02”和“anotherObject”。

设法获取所有以“area”开头的“areaContainer”子项的脚本如下所示:

public GameObject areaContainer;
public List<GameObject> MediumAreas = new List<GameObject>();

private void Start()
{
    DefineMediumAreas();
}

void DefineMediumAreas()
{
    for (int i = 0; i < areaContainer.transform.childCount; i++)
    {
        var childGameObject = areaContainer.transform.GetChild(i).gameObject;
        if (childGameObject.name.StartsWith("area"))
            MediumAreas.Add(childGameObject);
    }
}

1- 我最终在脚本中引用了 areaContainer 对象,而不是使用 GameObject.Find,因为它的性能更高。

2- 要获得游戏对象的子对象,您需要访问其变换并调用 GetChild(index)。因此,通过遍历父容器“areaContainer”,我们得到了它的 childCount。

3- 要检查名称是否以“area”开头,只需使用返回 true 或 false 的 .StartsWith("area")。


对于第二个问题,您可以隐藏禁用渲染器的对象或停用它(thisObject.SetActive(false);

希望对您有所帮助;编码愉快!

【讨论】:

    【解决方案4】:

    您想访问所有属于“areaContainer”子级的游戏对象

    void DefineMediumAreas()函数中,你需要Transform[]来获取一个childeren数组。使用这个:

    Transform[] areaContainer = GameObject.Find("areaContainer").GetComponentsInChildren<Transform>();
    
    foreach(Transform thisTransform in areaContainer)
    {
      ...
    }
    

    希望对你有帮助

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-24
      相关资源
      最近更新 更多