Mini tutorial on changing sprite on runtime

Hello,

I have been searching in this forum on how to change a sprite image during runtime but could not find a complete answer to address this issue. Can someone just post a short (but complete) c# code that would show us how to do this simple task giving that a certain sprite was created in the editor named “fruits” and was sliced to be “fruits_0, fruits_1 …etc.” also an object was created in the editor named “fruitObject”, what code should I use to assign different frames to “fruitObject”? And please don’t tell me to use prefabs because I have a lot of frames and it seems it’s not the right way to do this using them.

Thanks in advance.

There does not seem to be an API reference to a sprite sheet, UnityEngine.Sprite only gives parameters of a single sprite within a sheet. Maybe you could make an animation that has one frame per sprite and sample from it?

No, you got me wrong, I want to access just one single sprite within a sheet, not the whole sheet of course! how could it be done? any one?

I’m pretty sure the API doesn’t support that. I posted a question on Answers and someone offered a decent alternative. I think the “proper” way to do sprite animations is the process shown by Max in this video.

Finally I have figured it out, here is how I have accomplished it:
1- First, I created a Resources folder inside my assets folder and put my texture “fruits.png” inside it.
2- In the Project pane I clicked on the fruits assets that I have created in step (1) then in the inspector I changed the Sprite Mode from Single to Multiple, using the sprite editor I created my sprite sheet with 60 frames “fruits_0, fruits_1 … fruits_59”.
3- Now it’s time for the code

public Sprite[] fruitSprites;

void Awake()
    {
        // load all frames in fruitsSprites array
        fruitSprites = Resources.LoadAll<Sprite>("fruits");
    }

void Start ()
    {
        // create the object
        GameObject fruit = new GameObject();
        // add a "SpriteRenderer" component to the newly created object
        fruit.AddComponent<SpriteRenderer>();
        // assign "fruit_9" sprite to it
        fruit.GetComponent<SpriteRenderer>().sprite = fruitSprites[9];
        // to assign the 5th frame
        fruit.GetComponent<SpriteRenderer>().sprite = fruitSprites[5];
    }

So basically we just loaded our sprites into an array of sprites and used them as a reference.

It took me some time to figure it out because someone in a another post has stated that sprites cannot be auto-atlased when put in Resources folder, that statement has discouraged me from using the Resources.LoadAll for a while until I decided to try it.

14 Likes

Thats really cool. I was wondering how this could be achieved myself. I’ll be sure to try out your script.

Ultimately I just ended up using the Animator and setting up different states. I think it’s very useful for organization purposes.

But this method looks good too

Thanks from me too. Makes sense when I see the solution :slight_smile:

Thanks that solutions saved me a lot of time!

Interesting. I did this without knowing about “Resources”, which may have been much easier. But I did.

public Sprite[] sprites;

In the script on my sprite.

I then set the created Size property in the inspector to 13 for the 13 different images I needed (this number could be whatever for anyone else), and clicked and dragged each individual image I wanted to change, into elements 0-12 in the inspector.

Then in code, I did something similar to you.

void Start() {
spriterenderer.sprite = sprites[0]
}

And just changed the element in the array for whichever sprite image I wanted at any given time.

4 Likes

Thank you, it really helped me!

I would really like to know how to do this on spite atlas loaded at runtime via web call? Suppose I load a sprite sheet / atlas in runtime and I like to show all the images that are in that atlas and perform mouse click / touch event on all of them.

Also what if the sprite sheet that is loaded at runtime having different sized images (not fixed size sheet) ? Where to store the information of x/y/width/height ? Like we used to do in a XML file in actionscript3/flash or cocos2d.

Trying to solve a similar problem, thot I’d post an alternative solution in case people are interested. Check out the solution breakdown on my site. Code below. Cheers!

// new stuff
using System.Linq;
using System;
using UnityEditor;

// standard
using UnityEngine;
using System.Collections;

public class SkinController : MonoBehaviour {

    // drop all the "skins" you want to be swappable here, these are the multi-sprite textures in your Assets
    public Texture2D[] skins;
    
    // the skin you want to use, you could add a public accessor so this can be changed at runtime.
    public int activeSkin;

    // Use this for initialization
    void Awake () 
    {
        // default loaded sprites
        SpriteRenderer[] loadedRenderers = GetComponentsInChildren <SpriteRenderer>(true);

        // sprites we want
        Sprite[] sprites = AssetDatabase.LoadAllAssetRepresentationsAtPath("Assets/Sprites/" + skins[activeSkin].name + ".png").OfType<Sprite>().ToArray();

        for (int i = 0; i < sprites.Length; i++)
        {
            replaceMatchingSprite (loadedRenderers, sprites[i]);
        }
    }

    void replaceMatchingSprite (SpriteRenderer[] loaded, Sprite newSprite)
    {
        for (int i = 0; i < loaded.Length; i++)
        {
            try
            {
                if (loaded[i].sprite.rect.x == newSprite.rect.x  loaded[i].sprite.rect.y == newSprite.rect.y)
                {
				    // we found a match, replaced loaded with new, we can have multiple matches
				    loaded[i].sprite = newSprite;
                }
            }
            catch (Exception e) 
            {
                // do nothing, we seem to get a few null values when looking through the loaded sprites, but seems we can safely ignore them :).
            }
        }
    }
}
4 Likes

Which of those method are best for memory optimazation ?

I am thinking changing the state of a static object for example from deactivated to activate,
by only changing the Sprite Renderer “sprite” would save memory than adding an animation.

What do you think ?

Just getting back from a major hard drive failure otherwise I would have replied to this sooner.

I came across this method which I’ve found really helpful on the game (my first) I’m currently making. It allows you to replace a sprite sheet on a prefabbed model, effectively allowing you to skin and reuse one model for a variety of character.

There’s probably a better way of doing it (I’m a three month beginner) but I made a video about my walk cycle a few weeks ago but it has the skinning in there if anybody is interested in seeing the results.

This is the code I’m using. Not sure it’s the most efficient but it seems to do the job.

Texture2D sprite_sheet = Resources.Load<Texture2D> ("Character" + character_skin.ToString ());

Component[] parts;

parts = gameObject.GetComponentsInChildren<SpriteRenderer> ();

foreach (SpriteRenderer sr in parts) {

MaterialPropertyBlock myblock = new MaterialPropertyBlock ();
myblock.AddTexture ("_MainTex", sprite_sheet);
sr.SetPropertyBlock (myblock);

}

}
3 Likes
IEnumerator Start () {
        GameObject test = new GameObject ("SampleBundle");
        test.AddComponent (typeof(SpriteRenderer));

        yield return StartCoroutine (DownloadManager.Instance.WaitDownload ("MyBundle.assetBundle"));

        WWW www = DownloadManager.Instance.GetWWW("MyBundle.assetBundle");  
        AssetBundle bundle = www.assetBundle;
        Texture2D tex = bundle.Load (assetName) as Texture2D;
        Sprite sprite = GameObject.Find ("New Sprite").GetComponent<SpriteRenderer> ().sprite;
        Debug.Log (sprite.bounds);
        test.GetComponent<SpriteRenderer> ().sprite = Sprite.Create(tex, sprite.rect, new Vector2(0.5f,0.5f));
        Debug.Log (test.GetComponent<SpriteRenderer> ().bounds);
        Debug.Log (test.GetComponent<SpriteRenderer> ().sprite.bounds);
        //        test.GetComponent<SpriteRenderer> ().sprite = Resources.Load <Sprite>(assetName);
    }

Im facing a problem I have sprite in scene. When i load a sprite from resources its exactly the same as the one in the scene. But when i Load it from AssetBundle the bounds of the sprite change and if i do ti like

Sprite.Create(tex, sprite.rect, new Vector2(0.5f,0.5f), 80);

Then it works but its not the correct way.

Output:
Center: (0.0, 0.0, 0.0), Extents: (6.4, 4.0, 0.1)
Center: (0.0, 0.0, 0.0), Extents: (5.1, 3.2, 0.1)
Screenshot mages is attached for referece.

1731875--109279--Screen Shot 2014-08-12 at 5.22.45 pm.png

Hey Guys,
I was really happy to find the code “amjadyahya” posted. I tried to use it for a code in my project (to create a SpriteFont) but It’s not working…

public var font : Sprite[];

function Awake(){
 
     // the File is a PNG in "Assets/Resources/Font/SpriteFont.png" sliced into 101 pieces
    font = Resources.LoadAll("Font/SpriteFont", Sprite) as Sprite[];
 
  if (font != null){
  
              Char = new GameObject();
              Char.AddComponent(SpriteRenderer);
              Char.GetComponent(SpriteRenderer).sprite = font[0];
              Char.transform.position.Set(0,0,0);
                                                   
    }
     else {
          Debug.LogWarning("Loaded no SpriteSheet for Font."); // it always ends up here
     }
  
}

Any Ideas?

Edit: If you’re interested in the full code you can find it here: SpriteFont - Pastebin.com

okey, it works! this change fixed it

public var font : Object[];  //Unity recognizes a SpriteSheet as Object[]

...

font = Resources.LoadAll(path, typeof(Sprite)); // this worked for me

...
1 Like

If you want to look for a specific name instead of the index of the array just use foreach and compare the name of the sprite:

foreach(Sprite s in fruitSprites)
            if (s.name.Equals("The name you are looking for.")){
                fruit.GetComponent<SpriteRenderer>().sprite = s;
                break;
            }

This way you can use the names you gave to each slice of the sprite.

I was really jazzed when I came across this thread, but when I went to implement it, it didn’t work.

Although as a slight difference, I’m using these as textures to display on my game’s HUD. I’ve drawn a lot of textures on the screen before, but I’d love to be able to use “multiple sprites” instead of having a separate file for each individual graphic I want to display; especially for oddly-sized graphics and boxes that get divided into pieces.

Anyway, here’s the code I used:

    private Texture[] ProgressIcons;

...

        ProgressIcons = Resources.LoadAll<Texture>("HUD/ProgressIcons");

...

        DrawScaledTexture(ProgressIcons[1], 160, 112, 16, 16, 1);

That last function is one I made to keep everything the right size on all resolutions and aspect ratios. The first item it requires is a texture. When I run this code, it stops on that line giving me the error: “IndexOutOfRangeException: Array index is out of range.” so it feels like the array doesn’t include a “1” index.

I never got any errors from telling it to load those textures though, so it looks like it loaded right. And I double checked the spelling of the path. I do not see why this did not work.

Is there option not to load the whole atlas at runtime, but only frames I need, from it?

1 Like