Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Resolved How can I can I change sprites in an Animation at runtime?

Discussion in '2D' started by PaperMouseGames, Apr 28, 2022.

  1. PaperMouseGames

    PaperMouseGames

    Joined:
    Jul 31, 2018
    Posts:
    434
    Hi there! I'm working on a 2Dish pixel art game, and my animations are frame animations (not skeletal).

    What I'm trying to do is merge sprites together and then animate them in a procedural way.

    So for example, I have a player sprite that is of size 32x32 with 8 frames of animation. Full sheet is size 256x32.

    I also have a hat sprite, also 32x32 with 8 frames of animation. Full sheet is size 256x32.

    I want to merge these sprites together via code (planning on doing this with SetPixel), and then I want the animator to animate that new sheet that has been merged.

    I have an animator controller for my player animation and an override animator controller for my hat animation, but once the sprites have been merged I have no idea how to send that new sprite sheet to the animator.
     
    Rany_AN likes this.
  2. rarac

    rarac

    Joined:
    Feb 14, 2021
    Posts:
    570
    put the animator in a different object than the sprite renderer

    change the sprite of the sprite renderer based on the current frame on the animator

    after you setpixel+spritecreate you should put stuff in an array for convenience it could be in the same order as the frames of your animation so if its frame 3 you could set the sprite to newsprite[3] for example

    working with the sprite sheet like that will be an hassle both for processing and codewise so you might just start working with individual sprites and use sprite atlas to pack the sprites
     
    Olj11 likes this.
  3. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,710
    You don't really send the updated sprites to the animator.

    Rather the animation engine "plays" the animation, which means "uses the property timelines in the animation to change properties in whatever you are animating." This could mean changing rotation, position, sprite shape, color, etc of the parts of your animated object.

    If you are doing animations of what the sprite shape itself is, that is what you need to change, and as such you can't easily get to what you want from here, eg, "reach into" the animated property track and change it.

    Instead you could animate the frames yourself in code, perhaps from a
    public Sprite[] MySequence;
    that you populate with the new sprites you want.
     
  4. PaperMouseGames

    PaperMouseGames

    Joined:
    Jul 31, 2018
    Posts:
    434
    Is your suggestion to create my own Animator rather than using the Animator component? I thought about doing this if that's what you mean. That way I can merge and change up every sprite I want and then just loop through some array of sprites to animate a sprite renderer.

    I was mostly wondering if there was a headache free way of doing this with Unity's Animator though.

    I think layers could work instead of sprite merging, but then I would need my one animator to hold all possible animations for all possible things and it feels like it could get super messy.
     
  5. PaperMouseGames

    PaperMouseGames

    Joined:
    Jul 31, 2018
    Posts:
    434
    You mention that I can "put stuff in an array for convenience it could be in the same order as the frames of your animation so if its frame 3 you could set the sprite to newsprite[3] for example" but that's what I'm asking I guess.

    Once I have some collection of new sprites I made, how do I put them into the appropriate animation frames.
     
  6. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,710
    Are you ONLY changing out frames of animation by changing the entire sprite?

    If so then there is no value in using the animator if you intend to change what you are showing.

    For instance if your walk cycle without the hat is A-B-C-D

    and your walk cycle with the hat is J-K-L-M

    And you change nothing else about positions or rotations of those sprites, then the animator is really providing you almost zero value. This would be an equivalent way to replace it:

    Code (csharp):
    1. public Sprite[] MySpriteSequence; /// fill this up!
    2. public float FramesPerSecond = 20;  // adjust to suit
    3.  
    4. // poor man's cheesy-sprite sequencer. Put this on the GameObject with the SpriteRenderer
    5. void Update()
    6. {
    7.   int frame = (int)(Time.time * FramesPerSecond);
    8.  
    9.   // loop
    10.   frame = frame % MySpriteSequence;
    11.  
    12.   // set sprite
    13.   var renderer = GetComponent<SpriteRenderer>();
    14.   renderer.sprite = MySpriteSequence[frame];
    15. }
    You could then have code to inject sprites J-K-L-M into the fields where A-B-C-D is... or you could even replace the whole thing with a different-sized array and it should just keep working, since it uses the array length to wrap / loop.
     
    gooby429 likes this.
  7. PaperMouseGames

    PaperMouseGames

    Joined:
    Jul 31, 2018
    Posts:
    434
    Right yeah, all of my animations are sprite frame animations, there's no scaling, rotating, or any of that kind of stuff.

    So I'm basically trying to overlay sprites onto sprites, and then animated them (like a hat on a character).

    So would you recommend writing up my won animator for this? It seems easy enough, but I don't know if there's some big advantage I'm not thinking of for using Unity's Animator.
     
  8. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,710
    Generally the Animator is used to have multiple animation states, eg

    IDLE -> RUN -> IDLE -> ATTACK -> RUN -> JUMP

    Each state would be driven from code, usually by a property or by calling Play()

    The Animation associated with a given state above will go through all the frames of IDLE, all the frames of RUN, etc.

    If all you have is constant walk, the cheesy approach I list above could suffice.

    If you're also using the animator to change states, then it gets more complicated. In this case, it would likely need to be some hybrid of the two... or perhaps precisely-overlaid "lined up" sprites.
     
  9. PaperMouseGames

    PaperMouseGames

    Joined:
    Jul 31, 2018
    Posts:
    434
    I am using states yeah, idle, run, walk, etc. But they are all sprite renderer animations. Maybe I'll write up my own sprite animator, it really does seem like the easiest solution and states could be easily handled by enums and scriptable objects.

    So I could have a scriptable object that holds multiple arrays of sprites, each one corresponding to a state. Then I pick the ones I need from 1 or more SOs, merge them and play the final product.

    I haven't actually tried to do this, so if I get something working or run into issues, I'll post again!
     
  10. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,710
    This is actually an extremely smart solution.

    It really would become trivial then to swap out the RUN for the IDLE for the WALK for the ATTACK ScriptableObject, each instance of which has its own custom frame list, and just hack up the "poor man's cheesy sprite sequencer" I posted above to source the Sprites from your ScriptableObject.
     
  11. rarac

    rarac

    Joined:
    Feb 14, 2021
    Posts:
    570
    so you would do

    Code (CSharp):
    1. public Sprite[] newsprites = new Sprite[9];
    2.  
    3. newsprites[3] = Sprite.Create( etc...
    in my opinion its not worth the effort of making your own animator tho, dont really see the point since the one included in unity is already robust and performant

    I would tho advocate for using animator.Play() instead of transitions, if you want a nice article on these things you can check this link:https://www.gamasutra.com/blogs/JoeStrout/20150807/250646/2D_Animation_Methods_in_Unity.php
     
  12. PaperMouseGames

    PaperMouseGames

    Joined:
    Jul 31, 2018
    Posts:
    434
    Really good article, thanks for the link!

    I'll take a closer look later, but I think the issue is that I still don't know how to add those newly made sprites onto an animation.

    Like if I have an animation called "Run" that I want to play, but I want the "Run" animation to be made up of the sprites I just created, how can I do that without making my own animator? I'm not sure it's possible
     
  13. rarac

    rarac

    Joined:
    Feb 14, 2021
    Posts:
    570
    you dont add them to the animation, the animation remains the same, however since you removed the animator to a separate object it no longer drives the sprite renderer

    now you can just check the current frame on the animator and change the sprite on the sprite renderer to newsprites[x]

    heres an example: say i have an animation with 8 frames, all frames are just a black square, it runs at the speed of your choice. Although its always a black square since all frames are the same, the animator stores the current frame of the animation. Now you just use that info to set the sprite so

    Code (CSharp):
    1. spriterenderer.sprite = newsprites[currentanimationframe]
     
  14. PaperMouseGames

    PaperMouseGames

    Joined:
    Jul 31, 2018
    Posts:
    434
    Hmm I see, though this seems really similar to the approach of just making my own animator component. Where are you getting the currentanimationframe from?
     
  15. PaperMouseGames

    PaperMouseGames

    Joined:
    Jul 31, 2018
    Posts:
    434
    Dropping in an update:

    So I made a custom sprite frame animator that takes in an array of sprites, and animates through them based on an exposed animationSpeed variable. Very similar to what Kurt posted, with some details being different and more methods added for functionality.

    This part was simple, but then I ended up making a bunch of other scripts to make this work better in practice with an actual game.

    I added scriptable objects that take in a texture where you can set the start and end index of each animation and what state that animation represents and I made the states using enums so I can easily compare them.

    Some objects have the same start/end indices and states, such as the player animations and all the player hat animations. So when I want to add a new hat to my game, I import the texture, create a new SO with the same settings as the player, and just change the texture on it. Super quick and easy. In my opinion, much easier that making a whole new set of animations in Unity's default animator.

    So with all these SOs with textures and indices, I also made a sprite merger. This takes in those SOs in a layer order that I defined, merges the textures in that order, and creates an object of a new type SpriteAnimations that holds the final texture, animation indices, and possible states.

    This SpriteAnimations object then gets passed into my custom SpriteAnimator, and can play it's animations easily, and change states (like running forward, running sideways, idle, etc.)

    This is working beautifully, and even runs in the UI without issue, which is super nice. I have yet to notice and performance issues with this too, so I'm really happy with the result. Hope this helps other sprite frame animators out there!
     
    swashbucklr, Rany_AN and quijibo like this.
  16. blu3drag0n

    blu3drag0n

    Joined:
    Nov 9, 2018
    Posts:
    94
    Kindly let me all tell you about my massive 2D Games guide:
    https://forum.unity.com/threads/sha...before-starting-with-tilemaps-2d-2-5d.771020/

    Expert knowledge, guide chapter 9.)
    9. How to swap sprites for an animated object in the Animator / Animator Controller / Animation Clip, including blend trees - ON RUNTIME ?



    It can be tedious, but once setup its working smooth, fast and scales very good :)
    Sorry that the pictures are missing, I couldnt upload them anywhere that they stay forever. But the script and explanation will help you :)

    KR