Creating Editor 2D Animation Preview

Hi Everyone,

Since Unity does not does have API for creating sprite animation clips at runtime, I’ve made my own animation system.

As part of the system, I’m also creating editor tools.

I’m trying to create a preview tool that will show me the animation playing.

I’m using Json files to define the sprite atlas and another Json file to define the animation.

I have no problem getting all the data I need and make the tool switch to the correct sprite it needs to display.

The problem is, the display itself.

Here is what is happening (yes, I’m using LittleFighter2 for testing):
121263-example.gif

As you can see, the frames are being switched properly but the preview goes blank at times.
Here is the full code:

public class AnimationTool : EditorWindow
{
    private TextAsset _atlasJson;
    private TextAsset _animationJson;
    private Texture2D _texture;
    private SpriteSheetData _spriteMapping;
    private AnimationData _animationMapping;
    private int _currentAnimationIndex;
    private int _currentFrameIndex;
    private bool _playing;
    private double _lastTime;
    private Texture2D _currentFrameTexture;
    private Dictionary<string, Texture2D> _atlas;

    [MenuItem("Tools/Animation Tool")]
    static void Init()
    {
        // Get existing open window or if none, make a new one:
        var window = (AnimationTool)GetWindow(typeof(AnimationTool));
        window.Show();
    }

    private void Update()
    {
        if (!_playing) return;
        PlayAnimation();
    }

    private void OnGUI()
    {
        _atlasJson = (TextAsset)EditorGUILayout.ObjectField("atlas data", _atlasJson, typeof(TextAsset));
        _animationJson = (TextAsset)EditorGUILayout.ObjectField("animation data", _animationJson, typeof(TextAsset));
        _texture = (Texture2D)EditorGUILayout.ObjectField("image", _texture, typeof(Texture2D));

        if (_atlasJson == null || _animationJson == null || _texture == null) return;

        if (_spriteMapping == null)
        {
            _spriteMapping = JsonUtility.FromJson<SpriteSheetData>(_atlasJson.text);
        }

        if (_animationMapping == null)
        {
            _animationMapping = JsonUtility.FromJson<AnimationData>(_animationJson.text);
        }

        if(_atlas == null)
        {
            MapAtlas();
        }

        DrawArrowButtons();
        DrawPlayButton();
        Repaint();
        if(_currentFrameTexture == null)
        {
            Debug.LogError("AAAAA");
        }
        GUILayout.Box(_currentFrameTexture);
        DrawDataFields();
    }

    private void DrawArrowButtons()
    {
        GUILayout.Label(string.Format("{0}/{1}", _currentAnimationIndex, _animationMapping.Clips.Length - 1));
        GUILayout.BeginHorizontal();
        if (GUILayout.Button("<<<"))
        {
            _currentAnimationIndex--;
            if (_currentAnimationIndex < 0)
            {
                _currentAnimationIndex = 0;
            }
        }
        if (GUILayout.Button(">>>"))
        {
            _currentAnimationIndex++;
            if (_currentAnimationIndex > _animationMapping.Clips.Length - 1)
            {
                _currentAnimationIndex = _animationMapping.Clips.Length - 1;
            }
        }
        GUILayout.EndHorizontal();
    }

    private void DrawPlayButton()
    {
        if (!_playing)
        {
            if (GUILayout.Button("Play"))
            {
                _playing = true;
            }
        }
        else
        {
            if (GUILayout.Button("Stop"))
            {
                _playing = false;
            }
        }
    }

    private Texture2D GetTextureBySpriteIndex(int index)
    {
        var spriteData = _spriteMapping.Sprites[index];
        var sprite = new Texture2D((int)spriteData.Size.x, (int)spriteData.Size.y);
        var pixals = _texture.GetPixels((int)spriteData.StartPoint.x, (int)spriteData.StartPoint.y, (int)spriteData.Size.x, (int)spriteData.Size.y);
        sprite.SetPixels(pixals);

        return sprite;
    }

    private void PlayAnimation()
    {
        var frames = _animationMapping.Clips[_currentAnimationIndex].Frames;
        var diff = EditorApplication.timeSinceStartup - _lastTime;
        if (diff >= frames[_currentFrameIndex].Seconds)
        {
            for (int i  = 0; i < _spriteMapping.Sprites.Length; i++)
            {
                if (_spriteMapping.Sprites*.Name == frames[_currentFrameIndex].SpriteName)*

{
_currentFrameTexture = _atlas[_spriteMapping.Sprites*.Name];
_lastTime = EditorApplication.timeSinceStartup;
currentFrameIndex++;
if(_currentFrameIndex >= frames.Length)
_
{*

_currentFrameIndex = 0;
}
break;
}
}
}
}

private void DrawDataFields()
{
GUILayout.Label(_animationMapping.Clips[_currentAnimationIndex].Frames[_currentFrameIndex].SpriteName);
}

private void MapAtlas()
{
_atlas = new Dictionary<string, Texture2D>();
for (int i = 0; i < _spriteMapping.Sprites.Length; i++)
{
var texture = GetTextureBySpriteIndex(i);
atlas.Add(spriteMapping.Sprites*.Name, texture);*
}

}
}
Any help will be appreciated.
Thanks

Well, we know little about your own private sprite animation system. Suppose that it works.

It seems that the display of the animating sprites is painting in a repetitive way the same wrong sections of your Atlas.

That means that there is a predictable logic which can be debuged to fix the bug.

Is the problem in the _atlas contents? try displaying 100 different non animating boxes for each sprite in it to see if its contents get created properly in the first place.

Maybe you change the Atlas at run time, while the Editor has the old mapping somehow???

You should call Texture2D.Apply on your generated textures.
This makes sure that all changes made by SetPixels actually get applied.