简体   繁体   中英

Change Unity3D prefab image during runtime

I have an inventory of objects in a game, with image and description.

To represent it, I want a scrollable list with a grid (made with a UI canvas with a scroll view inside), and every element of the grid is a prefab, composed by a UI Panel with an Image and a Text.

预制外观

预制层次结构

I've tried assigning the values of the Image and the Text programmatically in the script:

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

The funny thing is, the Text gets correctly updated, while the Image doesn't . I'm accessing them in the same way, but one acts like I want it to and the other one does his thing.

This is the end result:

包含更新文本但未更新图像的列表

UPDATE : more details! Here's the code at runtime showing that the Image sprite does indeed get assigned

执行时的代码

and here's my inspector:

督察

Are you sure GetComponentInChildren is pointing to the right Image component? I would create a custom MonoBehaviour class for your prefab, and create a SetImage() method that points to the correct Image. When using GetComponentInChildren, it will return the first component found, and this can lead to unexpected behaviour.

You can do a Debug on newObj.GetComponentInChildren<Image>() to verify that it's the right component. Your code seems to be OK.

Since you say that the Panel object itself also has an Image component you do not want to use GetComponentInChildren since it also returns any Image attached to the newObj itself!

Your code doesn't throw an exception but assigns the mySprite to the Image component of the Panel object, not its children called Image .


You would either have to use eg

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

or as mentioned create your own class and attach it to the prefab like eg

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

and then do

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM