1

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.

Prefab appearance

Prefab hierarchy

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:

List with updated text but not updated images

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

Code at execution

and here's my inspector:

Inspector

man-teiv
  • 419
  • 3
  • 16
  • It looks like it's doing what it's supposed to. You are Instantiating a prefab, you are assigning a new text, but the value of mySprite doesn't appear to ever change. You've made it a property to be assigned in the Inspector but after that it never changes. – jiveturkey Apr 20 '20 at 12:53
  • Mind your phrasing: `Prefab` refers to a template asset. Once this prefab was instantiated or when talking about objects in a scene in general it is no longer called "prefab" but simply a `GameObject` – derHugo Apr 20 '20 at 12:56
  • It seems that your `mySprite` is simply `null` in the moment your code is executed .... – derHugo Apr 20 '20 at 12:57
  • In the inspector, I assigned a sprite to the mySprite variable, and I'm assigning this to the Prefab Image. I thought it was supposed to work like this? Can I formulate it in another way so that it works? – man-teiv Apr 20 '20 at 13:00
  • Could you a) show us a screenshot of your settings and b) set a breakpoint and debug your code at `newObj.GetComponentInChildren().sprite = mySprite;` .. I bet `mySprite` will be `null` at this point otherwise you would get exceptions in the console pointing out what's wrong. Also note: `Panel` itself usually contains an `Image` component by default .... is it possible you are stting the sprite there ? – derHugo Apr 20 '20 at 13:02
  • What do you mean my settings? you mean Edit > Preferences? I'm taking the screenshot of the breakpoint at the code right away. – man-teiv Apr 20 '20 at 13:03
  • @derHugo I can confirm that the sprite gets assigned and that it's not null: https://i.imgur.com/RHhBHpZ.png however it isn't registered as such during code execution – man-teiv Apr 20 '20 at 13:08
  • I mean a screenshot of the Inspector of your component. Anyway as said: Does your `Panel` object itself have an `Image` component? If this is the case you are setting the sprite in the wrong spot – derHugo Apr 20 '20 at 13:09
  • @derHugo the Panel object does have an Image component, and its sprite getting recognized and assigned during runtime (as per the previous screenshot I posted). Here's my Inspector: https://i.imgur.com/DYYgHwu.png – man-teiv Apr 20 '20 at 13:13

2 Answers2

2

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 e.g.

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

or as mentioned create your own class and attach it to the prefab like e.g.

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);
    }
}
derHugo
  • 83,094
  • 9
  • 75
  • 115
  • you wizard! It works! I'm still getting used to Unity and I didn't realize my mistake. I'm so grateful, thanks! I'll use your and cesartalves suggestion of creating a specific Prefab class in the future to avoid ambiguity. – man-teiv Apr 20 '20 at 13:27
1

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.

cesartalves
  • 1,507
  • 9
  • 18
  • There's only one image in the Panel, I thought it would get that one to be modified, right? Or is it possible that it's not finding it somehow? I'm confused because the Text has been found, but I don't know if under the hood they behave differently. Anyhow I'll try to create a Monobehaviour class for the prefab and I'll see how it goes, thanks! – man-teiv Apr 20 '20 at 12:59
  • I checked your code against an old one of mine and you're assigning the sprite correctly. So doing a Debug.Log on the image component could be used to check it's the right one. From my experience there's always a chance there's some hidden Image component being picked up. Just try and see if it works ^^; – cesartalves Apr 20 '20 at 13:12
  • I've checked during execution and the sprite *does* get assigned! i.imgur.com/RHhBHpZ.png this is weird because it seems like it "forgets" its assignment somehow – man-teiv Apr 20 '20 at 13:14