- Home /
2D Sprite Animation & 2D Image component
I have many (100+) sprite sheets, each of which contain many frames of a characters in 2D preforming various animations.
Every sprite frame has different dimensions (the frame size around an attacking unit, is different than that of him laying down dead)
These have been imported into Unity as sprite assets.
The pivots have been setup, such that when an animation plays using a sprite renderer, the sprite will position itself properly (pivot is on the feet, even if the animation is leaning forward or back).
I can render these as sprite animations on a SpriteRenderer, and everything works fine. However I want to make use of the Unity5 uGUI for this game. It looks like I need to have these render as an Image rather than a SpriteRenderer, and thats no problem for a single frame....
... however for animations the Image component will keep the dimensions based on the original frame in the animation. If I try and set each frame to "native size" every time the frame changes, the Animator will interpolate between the different frame sizes, which of course looks horrible.
TLDR; I have sprites with frames of different sizes. I want to display an animation in an Image component on a canvas, and by default Image does not handle animations where the image changes size. How can this be done?
Answer by Geddem · Jul 25, 2015 at 03:56 PM
So I wound up figuring this out. Oddly enough there is little to no documentation on how to go about creating Animators via script. So hopefully this helps the next person out some.
The overview is when I create Animator, I also have a script that lives on the same GameObject with the Image component.
[ExecuteInEditMode]
public class ImageSprite : MonoBehaviour
{
public bool SpriteChanged = false;
/// <summary>
/// Lates the update.
/// </summary>
public void LateUpdate()
{
if (SpriteChanged)
OnSpriteChanged ();
}
/// <summary>
/// Raises the sprite changed event.
/// </summary>
public void OnSpriteChanged()
{
Image.rectTransform.pivot = GetCurrentPivot();
Image.SetNativeSize ();
SpriteChanged = false;
}
/// <summary>
/// Accecssor getter
/// </summary>
/// <value>The image.</value>
private Image Image
{
get
{
return GetComponent<Image>();
}
}
/// <summary>
/// Gets the current pivot.
/// </summary>
/// <returns>The current pivot.</returns>
private Vector2 GetCurrentPivot()
{
var i = Image;
var pivot = i.sprite.pivot;
var bounds = i.sprite.bounds;
pivot.x /= bounds.size.x * i.sprite.pixelsPerUnit;
pivot.y /= bounds.size.y * i.sprite.pixelsPerUnit;
return pivot;
}
}
When I create the animator (via script), I have the animator tell the script to update the Image component. I could have done this w/ an event (they are easier to script), however whats not in the documentation is that Animator events are not executed in the editor! However, PPtrCurve bindings are, and for some unknown reason, Image.SetNativeSize () only works in LateUpdate anyway...
/// <summary>
/// Creates the sprite animation clip.
/// </summary>
/// <returns>The sprite animation clip.</returns>
/// <param name="raw">Raw.</param>
/// <param name="sprites">Sprites.</param>
/// <param name="fps">Fps.</param>
private static AnimationClip CreateSpriteAnimationClip(RawAnimationData raw, List<Sprite> sprites, int fps)
{
AnimationClip clip = new AnimationClip();
// Setup the clip params
clip.frameRate = fps;
clip.name = raw.name;
AnimationUtility.GetAnimationClipSettings(clip).loopTime = true;
// Addthe initial sprite keyframes
var frames = AddSpriteKeyframes (clip, raw, sprites);
// Add the sprite changed flags
AddSpriteChangedKeyframes (clip, frames);
// Set the wrapping mode
clip.wrapMode = raw.loop?WrapMode.Loop:WrapMode.Once;
return clip;
}
/// <summary>
/// Adds the sprite keyframes.
/// </summary>
/// <returns>The sprite keyframes.</returns>
/// <param name="clip">Clip.</param>
/// <param name="raw">Raw.</param>
private static ObjectReferenceKeyframe[] AddSpriteKeyframes(AnimationClip clip, RawAnimationData raw, List<Sprite> sprites)
{
// Get the number of frames
var framecount = raw.Frames.Count;
// Init the binding
EditorCurveBinding curveBinding = new EditorCurveBinding();
curveBinding.type = typeof(Image);
curveBinding.propertyName = "m_Sprite";
ObjectReferenceKeyframe[] keyFrames = new ObjectReferenceKeyframe[framecount];
float time = 0.0f;
for (int i = 0; i < framecount; i++)
{
var frameId = raw.Frames[i].x;
var duration = ((float)raw.Frames[i].y)/1000f;
ObjectReferenceKeyframe kf = new ObjectReferenceKeyframe();
kf.time = time;
kf.value= sprites[frameId];
keyFrames[i] = kf;
time += duration;
}
AnimationUtility.SetObjectReferenceCurve(clip, curveBinding, keyFrames);
return keyFrames;
}
/// <summary>
/// Adds the sprite changed keyframes.
/// </summary>
/// <param name="clip">Clip.</param>
/// <param name="keyFrames">Key frames.</param>
private static void AddSpriteChangedKeyframes(AnimationClip clip, ObjectReferenceKeyframe[] keyFrames)
{
var props = EditorCurveBinding.PPtrCurve ("", typeof(ImageSprite), "SpriteChanged");
Keyframe [] frames = new Keyframe[keyFrames.Length];
for (var i=0; i<keyFrames.Length; i++)
{
var frame = keyFrames[i];
var e = new Keyframe();
e.time = frame.time;
e.value = 1.0f;
frames[i] = e;
}
AnimationUtility.SetEditorCurve (clip, props, new AnimationCurve (frames));
}
Your answer
Follow this Question
Related Questions
Button animator not playing. 0 Answers
Animator is not playing an AnimatorController 1 Answer
root motion on in place animation 0 Answers