当实例化一个Prefab的多个GameObjects时,只有最后一个创建的按钮可以工作。

问题描述 投票:0回答:1

我又有同样的问题。我花了几个小时的时间来尝试,但我无法找到这个问题的解决方案。我将尝试提供尽可能多的信息。

using JetBrains.Annotations;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UI;

public class Shop : MonoBehaviour
{
    [Header("List of items sold")]
    [SerializeField] private ShopItem[] shopItems;

    [Header("References")]
    [SerializeField] private Transform shopContainer;
    [SerializeField] private GameObject shopItemPrefab;
    List<GameObject> itemList = new List<GameObject>();
    public void Start()
    {
        PopulateShop();
    }

    private void PopulateShop()
    {

        for (int i = 0; i < shopItems.Length; i++)
        {
            ShopItem si = shopItems[i];

            GameObject item = Instantiate(shopItemPrefab, shopContainer) as GameObject;


            //grab buttons to make listeners on them
                //buy button 

            item.GetComponentsInChildren<Transform>(true).FirstOrDefault(t => t.name == "buyButton").GetComponent<Button>().onClick.AddListener(() => buyButtonClick(si));
            //info button
            item.transform.Find("infoButton").GetComponent<Button>().onClick.AddListener(() => infoButtonClick(item));
            //infoReturnButton
            item.transform.GetChild(5).GetChild(1).GetChild(1).GetComponent<Button>().onClick.AddListener(() => infoCancelButtonClick(item));



            //change prefabs values
            item.transform.name = si.name;
            item.transform.GetChild(4).GetComponent<Image>().sprite = si.sprite;
            item.transform.GetChild(3).GetComponent<Text>().text = si.price.ToString();
            item.transform.GetChild(5).GetChild(1).GetChild(0).GetComponent<Text>().text = si.infoText;

            itemList.Add(item);
        }


    }
    public void infoButtonClick(GameObject item)
    {
        GameObject currentInfo =item.transform.GetComponentsInChildren<Transform>(true).FirstOrDefault(t => t.name == "infoBackgroundImg").gameObject;
        currentInfo.SetActive(true);

    }

    public void buyButtonClick(ShopItem item)
    {
        Debug.Log("aki ist gay");
    }

    public void infoCancelButtonClick(GameObject item) 
    {
        GameObject currentInfo = item.transform.GetComponentsInChildren<Transform>(true).FirstOrDefault(t => t.name == "infoBackgroundImg").gameObject;
        currentInfo.SetActive(false);

    }

}

我正在创建一个Prefab的对象。Prefabs的结构如下。

enter image description here

我有一个ShopItem,它提供了一些值插入到Prefab中,就像这样。

enter image description here

现在一共创建了3个对象 所有这些都很正常 问题是现在只有最后一个对象的按钮能用了 让我给你看看我的事件系统。

enter image description here:

这是Object3(最后一个)。正如你所看到的,指针按压工作(buyButton)。如果我们现在去看Object2,你可以看到它甚至不能识别按钮的存在。

enter image description here

但是当看对象的时候,你可以看到按钮是存在的。

enter image description here

所以我不明白,为什么Obj1&2也有按钮的时候,按钮只对Obj3有效,而当我删除Obj3时,Obj2的按钮就开始工作了。

我也不知道为什么不能用了。

c# unity3d
1个回答
-1
投票

好吧,我明白了问题所在,解决方法希望能让你的生活更轻松。

要理解你的代码是相当困难的,看来你可能误解了一些函数,导致你给自己增加了不必要的难度,导致了这个问题。

首先。 GameObject item = Instantiate(shopItemPrefab, shopContainer) as GameObject;如果你要实例化一个预制件(即预制件是第一个参数),或者任何一个 GameObject, Instantiate() 返回一个 GameObject 所以没必要投。这也是由于下一部分的问题。

        //grab buttons to make listeners on them
            //buy button
        item.GetComponentsInChildren<Transform>(true).FirstOrDefault(t => t.name == "buyButton").GetComponent<Button>().onClick.AddListener(() => buyButtonClick(si));
        //info button
        item.transform.Find("infoButton").GetComponent<Button>().onClick.AddListener(() => infoButtonClick(item));

在这里,你要添加所有的功能来听一个按钮。你有注释说你期望找到的东西,你正在按名称搜索它们。然而......你在一个循环中完成了这一切,而且它们都添加到了循环变量中 item.

你的代码工作正常。它把三个监听器都加到了三个按钮上(如果可以的话)。

如果你正在写一个循环,并且你有一堆不同的硬编码情况,你可能实际上并不需要一个循环。它应该是这样的

foreach(prefab in menu) 
    { //initialize prefab.button}

另一种码味突出。

        //infoReturnButton
        item.transform.GetChild(5).GetChild(1).GetChild(1).GetComponent<Button>().onClick.AddListener(() => infoCancelButtonClick(item));

多重的... GetChild 调用表示你清楚地知道你要找的东西和你要找的地方。那就没有理由在脚本中做这些,这就是你使用Unity的全部原因,所以你可以使用检查器来设置这些东西。如果你的按钮不是在运行时动态生成的,那么你就不需要这整个脚本。如果它们是,那就会继续不工作。

你的画布应该有一个名为 Info Menu 或其他东西,这样你就可以在检查器中或运行时简单地启用和禁用,如果你需要隐藏按钮,你甚至可以把整个预制的东西丢到一个新的场景中。

此外,你的 GetComponent<Button>().onClick.AddListener 正在向一个 Button 组件的 UnityEvent 列表,它的存在完全是为了让你在检查器中分配事件。如果您试图在运行时订阅监听器,您应该只需要一个普通的 C# event 并让组件在它们的应用程序中订阅和取消订阅。OnEnable()OnDisable() 回调。否则,你应该在检查器的prefabs中设置这些回调。

作为一个经常花几个小时来理解我把自己的生活搞得过于复杂而产生的bug的人,我希望这能帮助到你!

© www.soinside.com 2019 - 2024. All rights reserved.