- Home /
Unity 4.3 - Animating SpriteRenderer.Sprite With Legacy Animation
Title sums it neatly, I'm trying to animate a SpriteRenderer's Sprite field using legacy animation, but it does not seem to have any effect. Other fields on SpriteRenderer animate just fine (enabled for example), but when I modify the Sprite field and play, nada, just stuck on the first frame.
As for the use case - I have animated effects in my game, explosions for argument's sake. Their behavior is quite simple, they need to play an animation and cleanup themselves as soon as they are done, e.g
AnimatedVFX : VFXBase
{
protected Animation m_animation;
void Awake()
{
m_animation = GetComponent< Animation >();
}
void Update()
{
if ( !m_animation.isPlaying )
{
Deactivate();
}
}
override void OnInstantiated()
{
m_animation.Play();
}
}
If that's not at all possible, here's my main concerns for using the new Animator controller:
It adds clutter and maintenance cost by having to manage 2 assets for each effect, an animation clip and an animation controller.
There's no easy way to poll the animation play state ( animator.GetCurrentAnimatorStateInfo( 0 ).normalizedTime >= 1.0f ), atleast at glance, looks more expensive than animation.isPlaying and less reliable.
For love's sake, it's an 8 image sprite sequence, all it has to do is display 8 images and go poof. I does not transition to anything, it has no states, nothing. There's no reason what so ever to use a super complex FSM for that, especially when it's counter intuitive for what I'm trying to achieve AND comes with a bigger performance price tag.
So, to wrap it up - what's my best course of action here for playing a simple sprite sequence as efficiently as possible?
Thanks :)
Going to leave this question open, as it is still relevant. In the meantime, I've thrown together a simple sprite animation system in 30 $$anonymous$$utes for my use case. Nothing to write home about, however, if anyone finds it useful, by all means, help yourself to it.
using UnityEngine;
using System.Collections;
[RequireComponent ( typeof( SpriteRenderer ) ) ]
public class SpriteAnimation : $$anonymous$$onoBehaviour
{
/**********************************************************************************
*
* Copyright (c) 2013 $$anonymous$$artin Climatiano
*
* Distributed under $$anonymous$$IT License: http://opensource.org/licenses/$$anonymous$$IT
*
**********************************************************************************/
public bool isPlaying
{
get { return m_playing; }
}
[SerializeField]
private Sprite[] m_frames; // User defined frames
[SerializeField]
private float m_fps; // User defined framerate
[SerializeField]
private bool m_playOnAwake; // Should we play animation automatically on awake?
private float m_frameTime; // Cached target frame time
private int m_currentFrame; // Current frame index
private float m_timer; // Current frame time
private bool m_playing = false; // Is currently playing?
private SpriteRenderer m_renderer;
public void Play()
{
if ( m_fps <= 0 )
{
Debug.LogError("[ASSERT] SpriteAnimation frame rate must be positive non-zero" );
return;
}
m_frameTime = 1.0f / m_fps; // DOH! time is in seconds, not milis :)
m_playing = true;
SetFrame( 0 );
}
void Update()
{
if ( m_playing )
{
m_timer += Time.deltaTime;
if ( m_timer > m_frameTime )
{
GetNextFrame();
}
}
}
void Awake()
{
m_renderer = renderer as SpriteRenderer; // Cache sprite renderer
if ( m_playOnAwake )
{
Play();
}
}
private void GetNextFrame()
{
m_currentFrame++;
if ( m_currentFrame < m_frames.Length )
{
SetFrame( m_currentFrame );
return;
}
m_playing = false;
}
private void SetFrame( int frame )
{
m_currentFrame = frame;
m_timer = 0;
m_renderer.sprite = m_frames[ frame ];
}
}
I will try it $$anonymous$$artinCA, having the same issue here.
Answer by Womrwood · Dec 29, 2013 at 07:41 PM
Hello Martin, after following through your code, I thought to create an SpriteManager which will have a list of SpriteAnimation with its own fps, frames array, wrap mode and interpolation between frames since I realize that I could use the Animation Component information to take care of these parameters. So I create a simple class (the code is simple but it is for example purposes):
using UnityEngine;
using System.Collections;
[RequireComponent(typeof(SpriteRenderer))]
[RequireComponent(typeof(Animation))]
public class SpriteManager : MonoBehaviour
{
private SpriteRenderer SpriteRenderer { get; set; }
private Animation Animation { get; set; }
void Awake()
{
SpriteRenderer = renderer as SpriteRenderer;
Animation = animation as Animation;
}
public void Play(string animationName)
{
Animation.Play(animationName);
}
public void SetNextSprite(Sprite sprite)
{
SpriteRenderer.sprite = sprite;
}
}
The trick happens when creating your animation clips, when setting your sprites over the keyframes just create an event calling the SetNextSprite method in the SpriteManager object and drag the same sprite texture used in the keyframe (note that you don't need to put the right sprite in the animation clip keyframes but it is good for visualization). The neat about this approach is that you don't need to take care of the animation time etc... It would be even better if you could create the animation clips on the fly.
Cool, I'll give it a look sometime tomorrow, see if I can hack together a quick editor tool to create frame animations using sequential frames :)
I also need to follow up with some guys at Unity, trying to figure out the roadmap for the animation feature and if this is a bug or intentionally not supported by default.
Answer by fermmmm · Sep 05, 2014 at 11:55 PM
This is a better approach, follow this simple steps:
1) Add this component:
using UnityEngine;
[RequireComponent(typeof(SpriteRenderer))]
[RequireComponent(typeof(Animation))]
[ExecuteInEditMode]
public class LegacyAnimateSpriteRenderer : MonoBehaviour
{
public float Frame; // Animate this value with the legacy Animator using the timeline, you can preview changes in edit mode.
public Sprite[] Frames; // Here yoy add all the sprites that will be animated
private SpriteRenderer Renderer;
void Start ()
{
Renderer = GetComponent<SpriteRenderer>();
}
void Update ()
{
int frame = Mathf.FloorToInt(Frame);
if (frame < 0 || Frames == null)
{
Frame = 0f;
return;
}
if (frame >= Frames.Length)
{
Frame = Frames.Length;
return;
}
if (Frames[frame] != Renderer.sprite)
Renderer.sprite = Frames[frame];
}
//This executes in Editor mode, we call Update so you can preview your animation in the editor.
void OnRenderObject()
{
if (!Application.isPlaying)
Update();
}
}
2) In the editor add all the frames sprites to the "Frames" List
3) Move the "Frame" value, note the frames changing.
4) Now you only need to animate the "Frames" value using the legacy system.
With this approach you don't need to play the game to preview the animation, can use curves, add events and create all the animation clips you need because you are using the Unity tools.
Your answer
Follow this Question
Related Questions
2d component animation, change sprites in runtime. 0 Answers
Crushy Bird Game Doubt 1 Answer
Change sprite in a sprite renderer with an animation 0 Answers