【问题标题】:Destroy specific child object of prefab object in c# / Unity在c#/Unity中销毁预制对象的特定子对象
【发布时间】:2020-09-12 23:03:01
【问题描述】:

我搜索了很多,但找不到任何答案。我的问题:我实例化了 Prefab 的几个 Gameobjects,每个包含两个子对象:一个障碍物和一个 scorezone。当玩家碰到障碍物时,我想破坏 scorezone(仅那个特定实例)。所以在运行时我的层次结构看起来像这样:

  • -ObjectPrefab(克隆)
  • ---计分区
  • ---障碍
  • -ObjectPrefab(克隆)
  • ---计分区
  • ---障碍
  • ...

所以我需要找到一个子游戏对象(不是变换)然后将其销毁。我尝试了几个代码,但没有一个有效。这是我尝试过的三种替代方法的代码,但都没有奏效。:

private void OnCollisionEnter2D(Collision2D collision) {

if (collision.gameObject.tag == "obstacle") {

备选方案 1:

Destroy(GameObject.FindWithTag("scorezone"));

备选方案 1 非常接近,但它破坏了所有实例化对象的所有得分区。

备选方案 2:

Destroy(collision.gameObject.transform.parent.gameObject.FindWithTag("scorezone"));

备选方案 2 听起来合乎逻辑,但我收到错误消息“转换不包含“FindWithTag”的定义。

备选方案 3:

foreach (Transform child in collision.gameObject.transform)
  {
   if (child.tag == "scorezone")
      {
        Destroy(child.gameObject);
      }
  }

}

替代方案 3 没有给出错误,但当玩家撞到障碍物时实际上什么都不做。

非常感谢一些帮助,在此先感谢!

【问题讨论】:

  • 或者遍历对撞机游戏对象上的孩子

标签: unity3d parent-child collision-detection destroy


【解决方案1】:

存储参考!

如果它真的是一个“预制件”,这意味着你将它作为资产并在运行时实例化它,最好的方法(在我看来和性能方面)实际上是使用序列化字段:

// Put this component on your obstacle
public class ObjectController : MonoBehaviour
{
    // Drag the according object here via the Inspector in the prefab
    // => when the prefab is Instantiated this field is already referenced
    [SerializeField] private GameObject _scoreZone;

    // Read-only access to the reference
    public GameObject ScoreZone => _scoreZone;
}

此字段引用与您的预制资产一起存储。因此,预制件的每个实例都已经设置了自己的子引用。

然后做

private void OnCollisionEnter2D(Collision2D collision) 
{
    // Rather use CompareTag which is slightly more efficient
    // and adds some security (== silently fails if typo, CompareTag throws an exception instead)
    if (collision.gameObject.CompareTag("obstacle")) 
    {  
        var controller = collision.gameObject.GetComponent<ObjectController>();
        if(controller) 
        { 
            Destroy(controller.ScoreZone); 
        }
    }
}

按索引

或者,但容易出错,您可以简单地使用索引。您已经知道只有 2 个孩子,第一个是带有索引 0 的 Scorezone,所以您也可以这样做

if (collision.gameObject.CompareTag("obstacle")) 
{  
    var scoreZone = collision.transform.parent.GetChild(0);
    Destroy(scoreZone); 
}

按名称

使用名称,您也可以使用 Find - 我会推荐这个!它容易出错且速度慢

if (collision.gameObject.CompareTag("obstacle")) 
{  
    var scoreZone = collision.transform.parent.Find("ScoreZone");
    Destroy(scoreZone); 
}

【讨论】:

  • 非常感谢您的帮助!我尝试了您的第一个解决方案。它不起作用,因为代码中的变量“控制器”为空,所以它没有出现在 if 语句中。您的“ObstacleController”是附加到预制件的脚本吗?也许我应该补充:碰撞功能附加到播放器,它触及实例化的预制件。会不会是这个问题?
  • cmets 中所说的ObstacleController 应附加到您正在与之碰撞的Obstacle 对象(也附加了Collider 的对象)
  • 或者您也可以将其附加到预制件本身,然后使用GetComponentInParent
  • 成功了,谢谢!!!您使用了“ObjectController”,然后使用了“ObstacleController”,这就是我感到困惑的原因,但现在我明白了:)
  • 哦,我为这种混乱感到抱歉 - 现在已解决此问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-07-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-18
相关资源
最近更新 更多