- Home /
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):
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[i].Name == frames[_currentFrameIndex].SpriteName)
{
_currentFrameTexture = _atlas[_spriteMapping.Sprites[i].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[i].Name, texture);
}
}
}
Any help will be appreciated.
Thanks
Answer by liortal · Jul 22, 2018 at 07:48 PM
You should call Texture2D.Apply on your generated textures. This makes sure that all changes made by SetPixels actually get applied.
I totally forgot about that... Rookie mistake. Thanks @liortal !
Answer by Helical · Jul 21, 2018 at 04:14 PM
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???
The problem is not with the mapping. I've triad showing all sprites as you suggested. Each time I reopen the window and assign all the objects, different "previews" are broken....
Your answer
Follow this Question
Related Questions
How to make multiple selected Animation's Preview window as editor window 0 Answers
Custom Editor wIndow not being detected by the Animation window 1 Answer
Get default preview for GameObject Editor 2 Answers
Need help on skeletal Animation 0 Answers
How can I listen for remove and add component events 1 Answer