Close search results
Close search results

Unity3D: Replace Sprite Programmatically in Animation

Luigi says: Don't repeat yourself

August 4th, 2016

Just starting out creating a 2D game using Unity3D, I came across a simple problem: There seems to be no easy way to swap out the sprite in an animation (think: Luigi using the same animations as Mario). It may seem simple to just create a new animation, but with many different characters (sprites) using the same animation it gets old fast. Read on for a short explanation and some code.

Note: A solution based on the same approach is also described in this video starting at 20:00. The link to the source code is broken, however.

It is assumed that you have already done the following:

  1. Imported the sprite sheets and split them up in sprites accordingly, making sure corresponding sprites have the same name
  2. Created one or more animations from the sprites
Important: In the sprite editor, name each sprite (frame) consistently across all sprite sheets. The name is used as key for the mapping between sprite sheets.
Important: In the sprite editor, name each sprite (frame) consistently across all sprite sheets. The name is used as key for the mapping between sprite sheets.
The sprites to swap between are located in a 'Resources' sub folder - 'player' and 'player2'.
The sprites to swap between are located in a 'Resources' sub folder - 'player' and 'player2'.
A simple walk animation.
A simple walk animation.

Now, in order to swap out the sprite programmatically, we create the following script and add it to the game object:


using System.Collections.Generic;
using UnityEngine;
using System.Linq;

public class SpriteSwapDemo : MonoBehaviour
{
    // The name of the sprite sheet to use
    public string SpriteSheetName;

    // The name of the currently loaded sprite sheet
    private string LoadedSpriteSheetName;

    // The dictionary containing all the sliced up sprites in the sprite sheet
    private Dictionary<string, Sprite> spriteSheet;

    // The Unity sprite renderer so that we don't have to get it multiple times
    private SpriteRenderer spriteRenderer;

    // Use this for initialization
    private void Start()
    {
        // Get and cache the sprite renderer for this game object
        this.spriteRenderer = GetComponent<SpriteRenderer>();

        this.LoadSpriteSheet();
    }

    // Runs after the animation has done its work
    private void LateUpdate()
    {
        // Check if the sprite sheet name has changed (possibly manually in the inspector)
        if (this.LoadedSpriteSheetName != this.SpriteSheetName)
        {
            // Load the new sprite sheet
            this.LoadSpriteSheet();
        }

        // Swap out the sprite to be rendered by its name
        // Important: The name of the sprite must be the same!
        this.spriteRenderer.sprite = this.spriteSheet[this.spriteRenderer.sprite.name];
    }

    // Loads the sprites from a sprite sheet
    private void LoadSpriteSheet()
    {
        // Load the sprites from a sprite sheet file (png). 
        // Note: The file specified must exist in a folder named Resources
        var sprites = Resources.LoadAll<Sprite>(this.SpriteSheetName);
        this.spriteSheet = sprites.ToDictionary(x => x.name, x => x);

        // Remember the name of the sprite sheet in case it is changed later
        this.LoadedSpriteSheetName = this.SpriteSheetName;
    }
}

By changing the parameter "Sprite Sheet Name", we can apply a different sprite sheet at runtime.

The script 'Sprite Swap Demo' was created and added to the game object. The parameter 'Sprite Sheet Name' is set to 'player', which is the name of the PNG file of the player. We can start the game and change it to another value on the fly.
The script 'Sprite Swap Demo' was created and added to the game object. The parameter 'Sprite Sheet Name' is set to 'player', which is the name of the PNG file of the player. We can start the game and change it to another value on the fly.
The player with the default sprite is seen at the bottom.
The player with the default sprite is seen at the bottom.
The name of the sprite is changed to 'player2'.
The name of the sprite is changed to 'player2'.
By typing 'player2' in the sprite sheet name text box, the sprite is swapped out.
By typing 'player2' in the sprite sheet name text box, the sprite is swapped out.

I hope you don't mind my terrible artwork. Have fun creating awesome games!

Update:

It happened - the game is released!

May 23rd, 2017 20:44
jam

Awesome! Worked right away, thanks for this!

May 23rd, 2017 21:28
Erik

Hi jam,

glad to be of help! It's funny - I was too busy and sort of gave up on this game quite some time ago, just to pick up the ball again only a few days ago. Coming to a store near you in... 2035??

August 4th, 2017 02:40
Dorian

Hi, thank you so much for your code. I really search for that... but i have a problem... I recieve this : "KeyNotFoundException: The given key was not present in the dictionary.
System.Collections.Generic.Dictionary`2[System.String,UnityEngine.Sprite].get_Item (System.String key) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Collections.Generic/Dictionary.cs:150)
SpriteSwapDemo.LateUpdate () (at Assets/Scripts/SpriteSwapDemo.cs:40)
"

What is the problem ? I don't understand the reaction of Unity.

August 4th, 2017 03:48
Dorian

^^' I have found myself (I forgot to rename each sliced image with the same name)

Your code just work perfectly :) thank you so much for your help and time :)

August 4th, 2017 17:06
Erik

Hi Dorian,

glad you got it figured out. Good luck with your game!

August 26th, 2017 02:28
Raitan Biz Rigon

Thank you, it worked fine!

September 7th, 2017 15:06
Dajka Ferenc

Hi!
Could you share the source please? I can't reproduce this in my project, maybe the Animator has different settings.

September 7th, 2017 21:23
Erik

Hi Dajka Ferenc,

maybe there is a simple solution to it. At which step are you having trouble?

January 11th, 2018 15:55
Lucas

Thank you so much! It works perfect! You saved me so much time.

May 28th, 2018 16:58
Rafael

The code works perfectly, but the console keep showing this every frame:

ArgumentException: An element with the same key already exists in the dictionary.
System.Collections.Generic.Dictionary`2[System.String,UnityEngine.Sprite].Add (System.String key, UnityEngine.Sprite value) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Collections.Generic/Dictionary.cs:404)
System.Linq.Enumerable.ToDictionary[Sprite,String,Sprite] (IEnumerable`1 source, System.Func`2 keySelector, System.Func`2 elementSelector, IEqualityComparer`1 comparer)
System.Linq.Enumerable.ToDictionary[Sprite,String,Sprite] (IEnumerable`1 source, System.Func`2 keySelector, System.Func`2 elementSelector)
SpriteSwapDemo.LoadSpriteSheet () (at Assets/Scripts/SpriteSwapDemo.cs:49)
SpriteSwapDemo.LateUpdate () (at Assets/Scripts/SpriteSwapDemo.cs:35)

May 28th, 2018 17:53
Erik

Hi Rafael,

you probably have to or more sprites with the same name in the same sprite sheet. Take a look at the first image in this guide. Click each sprite in the sheet and make sure the name is unique across the sprites in the sheet, such as "walk0", "walk1" etc. Then also make sure to name each sprite in the second sheet with the same name as in the first one. Hope this helps!

September 4th, 2018 20:48
Obsession

Life saver.

November 4th, 2018 14:14
Martin

Erik, thanks a lot for this, it is simply awesome! You helped me a lot.

July 10th, 2019 17:29
Xenonq

Does the sheet need to look exactly like yours. Sprite next to sprite?
[][][][][] < your sheet
[] []
[] [] < my sheet
can I have it like this?

July 10th, 2019 17:50
Xenon

I did exactly what you showed and this came up KeyNotFoundException: The given key was not present in the dictionary.
System.Collections.Generic.Dictionary`2[System.String,UnityEngine.Sprite].get_Item (System.String key) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Collections.Generic/Dictionary.cs:150)
SkinSwapper.LateUpdate () (at Assets/SkinSwapper.cs:41)
I don't know what to do pls help :(

August 14th, 2019 22:11
Erik

Hi Xenon,

please check the answer above, Dorian had the same issue.

September 9th, 2019 16:20
Shamahan

Holy hell thank you so much for this piece of code, you wouldn't believe how long I searched for something like this, it is exactly what I needed!

One question - is there some way to make the sprite sheet change only once at the press of a button or does it have to constantly be in the LateUpdate function?

September 10th, 2019 21:18
Erik

Hi Shamahan,

that's great! And with the above code, the sprite sheet should only reload once if changed on the fly. Let me know if you have found that it doesn't.

September 15th, 2019 00:46
John

Ok so I followed the instructions item by item, and have named each frame of both my sprite sheets individual cells to match each other, I am getting the same error as above:

KeyNotFoundException: The given key was not present in the dictionary.
System.Collections.Generic.Dictionary`2[TKey,TValue].get_Item (TKey key) (at <7d97106330684add86d080ecf65bfe69>:0)
Crowd_Sprite_Sheet_Swapper.LateUpdate () (at Assets/Crowd_Sprite_Sheet_Swapper.cs:40)

What's up fam?

September 15th, 2019 03:18
John

Just to update - got it working - thank you! But I'm getting spammed hard in the console:

ArgumentException: An item with the same key has already been added. Key: Crowd_Generic_0
System.Collections.Generic.Dictionary`2[TKey,TValue].TryInsert (TKey key, TValue value, System.Collections.Generic.InsertionBehavior behavior) (at <7d97106330684add86d080ecf65bfe69>:0)

Any idea why?

September 15th, 2019 07:22
Erik

Hi John,

probably, two sprites in the sprite sheet have the same name. Make sure each name only appears once ("walk1", "walk2"...). If you can't find the problem, try setting a debugging breakpoint in your code in visual studio. Then click "attach to unity" and start your game. Hopefully, stepping through the code will make the problem obvious. Hope this helps!

September 16th, 2019 14:10
John

Hey thanks for getting back to me so quickly. I can confirm that within the sheet, no sliced sprites are named the same. Each has a unique name (Crowd_Generic_0, Crowd_Generic_1, etc...)

So I am using the sprite swapping because I am emulating a large crowd (50+ NPCS). A crowd NPC is to use the same animation set, but I am swapping their sheets in order to avoid having to create unique animations over and over again for each NPC.

So essentially 50 NPCs are in the scene - all at once - all referencing one "leader" NPC's animations.

I appreciate the help!

September 17th, 2019 17:20
Tutmo

Hey John,

I ran into the exact same issue - all my Sprite names were unique per Sprite Sheet, but still getting the exception error.

After debugging in circles, I realized the issue (for me anyways) was that my initial Sprite in the Sprite Renderer was set to 'None'. I just needed to set the initial Sprite to any one of the Sprites in my Resource Sprite Sheets and the error went away. Hope that helps.

Thanks for the original post Erik! This was way clearer than anything else I've found after hours of Googling!!

September 17th, 2019 17:39
Erik

Hi Tutmo,

that's great to know, let's hope it helps John - or someone else for that matter.

And happy to hear you found the post useful!

September 21st, 2019 09:29
Tuan

Hi Erik,
I have 1 character with 7 spriteRenders for Head, hand, weapon, horse .... and I want change sprites for 1 or more of them, maybe change all of them.
I learn from your youtube , but when change sprite in Head and Body Sprite Render, it have problem is run change 1st sprite and not others.
With multi spriteRenderer in one character, so I would like ask how is the best way to solve it?

Leave a comment






This will just take a second.

Submitting your comment...
Erik Moberg  2019