在运行时更改 Unity3D 预制图像

Change Unity3D prefab image during runtime

我有一个游戏中的物品清单,有图片和描述。

为了表示它,我想要一个带有网格的可滚动列表(用 UI canvas 制作,里面有一个滚动视图),网格的每个元素都是一个预制件,由UI 带有图像和文本的面板。

我已经尝试在脚本中以编程方式分配图像和文本的值:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class PopulateGrid : MonoBehaviour
{
    public GameObject prefab;
    public Sprite mySprite;

    private void Start()
    {
        Populate();
    }

    public void Populate()
    {
        GameObject newObj;

        for (int i = 0; i < 20; i++)
            {
            newObj = (GameObject)Instantiate(prefab, transform); //prefab has image + text
            newObj.GetComponentInChildren<Text>().text = "test" + i;
            newObj.GetComponentInChildren<Image>().sprite = mySprite; //I've also tried .overrideSprite
        }
    }
}

有趣的是,文本得到正确更新,而图像没有。我正在以相同的方式访问它们,但其中一个按照我的意愿行事,而另一个则按照他的方式行事。

这是最终结果:

更新:更多细节!这是运行时的代码,显示图像精灵确实被分配了

这是我的检查员:

您确定 GetComponentInChildren 指向正确的图像组件吗?我会为您的预制件创建一个自定义 MonoBehaviour class,并创建一个指向正确图像的 SetImage() 方法。使用 GetComponentInChildren 时,它将 return 找到第一个组件,这可能会导致意外行为。

您可以在 newObj.GetComponentInChildren<Image>() 上进行调试以验证它是正确的组件。您的代码似乎没问题。

既然你说 Panel 对象本身也有一个 Image 组件你不想使用 GetComponentInChildren 因为它也 returns 任何 Image 附加到 newObj 本身!

您的代码没有抛出异常,而是将 mySprite 分配给 Panel 对象的 Image 组件,而不是其名为 Image 的子组件。


您要么必须使用例如

newObj.transform.GetChild(0).GetComponent<Image>()

或如上所述创建您自己的 class 并将其附加到预制件上,例如

public class TileController : Monobehaviour 
{   
    // Reference these via the Inspector in the prefab
    [SerializeField] private Text _text;
    [SerializeField] private Image _image;

    public void Initialize(Sprite sprite, String text)
    {
        _image.sprite = sprite;
        _text.text = text;
    }
}

然后

// using the correct type here makes sure you can only reference a GameObject
// that actually HAS the required component attached
[SerializeField] private TileController _prefab;

public void Populate()
{
    TileController newObj;

    for (int i = 0; i < 20; i++)
    {
        // no cast needed since Instantiate returns the type of the prefab anyway
        newObj = Instantiate(_prefab, transform);
        newObj.Initialize(mySprite, "test" + i);
    }
}