Thank you!
Play an animation on editor mode
Hey, does anyone know why WiseKodama had to multiply the animation duration by 0.5f here?
I find that if i do not do this, the animation scrubs through twice as fast as it should and its on its final frame by t = trackEntry.Animation.Duration / 2
.
But i don't understand why that would be the case? Is Animation.Duration
reporting a duration that's twice as long as the actual duration? What's going on there?
- تم التحرير
@SpiralCircus Good catch! Actually this is a bug in the source code provided by @WiseKodama, since the same delta time is applied twice to the AnimationState here:
skeletonAnimation.Update(currentTime); // this calls animationState.Update(currentTime)
skeletonAnimation.state.Update(currentTime); // this line should be removed
So to correct the code, remove the second line, then you no longer need to halve the duration.
Change this line:
maxRange = anim.Animation.Duration * 0.5f;
To the following:
maxRange = anim.Animation.Duration;
Awesome, thanks Harald!
You're welcome, thanks for reporting.
Harald Thanks for the fix
Inspired by these articles, I created a script 'EditorSkeletonPlayer' that supports SkeletonAnimation and SkeletonGraphic. I hope you find this useful.
(If possible, I think it would be great if this feature could be officially adopted into the Spine Unity Runtime )
#if UNITY_EDITOR
using Spine;
using Spine.Unity;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEngine;
[ExecuteInEditMode]
public class EditorSkeletonPlayer : MonoBehaviour
{
private IEditorSkeletonWrapper _skeletonWrapper;
private TrackEntry _trackEntry;
private string _oldAnimationName;
private bool _oldLoop;
private double _oldTime;
[DidReloadScripts]
private static void OnReloaded()
{
//force awake when scripts are reloaded
var editorSpineAnimations = FindObjectsOfType<EditorSkeletonPlayer>();
foreach (var editorSpineAnimation in editorSpineAnimations)
{
editorSpineAnimation.Awake();
}
}
private void Awake()
{
if (Application.isPlaying) return;
if (_skeletonWrapper == null)
{
if (TryGetComponent<SkeletonAnimation>(out var skeletonAnimation))
{
_skeletonWrapper = new SkeletonAnimationWrapper(skeletonAnimation);
}
else if (TryGetComponent<SkeletonGraphic>(out var skeletonGraphic))
{
_skeletonWrapper = new SkeletonGraphicWrapper(skeletonGraphic);
}
}
_oldTime = EditorApplication.timeSinceStartup;
EditorApplication.update += EditorUpdate;
}
private void OnDestroy()
{
EditorApplication.update -= EditorUpdate;
}
private void EditorUpdate()
{
if (Application.isPlaying) return;
if (_skeletonWrapper == null) return;
if (_skeletonWrapper.State == null) return;
if (_oldAnimationName != _skeletonWrapper.AnimationName || _oldLoop != _skeletonWrapper.Loop)
{
_trackEntry = _skeletonWrapper.State.SetAnimation(0, _skeletonWrapper.AnimationName, _skeletonWrapper.Loop);
_oldAnimationName = _skeletonWrapper.AnimationName;
_oldLoop = _skeletonWrapper.Loop;
}
if (_trackEntry != null)
{
_trackEntry.TimeScale = _skeletonWrapper.Speed;
}
float deltaTime = (float)(EditorApplication.timeSinceStartup - _oldTime);
_skeletonWrapper.Update(deltaTime);
_oldTime = EditorApplication.timeSinceStartup;
//force repaint to update animation
EditorApplication.QueuePlayerLoopUpdate();
}
private class SkeletonAnimationWrapper : IEditorSkeletonWrapper
{
private SkeletonAnimation _skeletonAnimation;
public SkeletonAnimationWrapper(SkeletonAnimation skeletonAnimation)
{
_skeletonAnimation = skeletonAnimation;
}
public string AnimationName
{
get { return _skeletonAnimation.AnimationName; }
}
public bool Loop
{
get { return _skeletonAnimation.loop; }
}
public float Speed
{
get { return _skeletonAnimation.timeScale; }
}
public Spine.AnimationState State
{
get { return _skeletonAnimation.state; }
}
public void Update(float deltaTime)
{
_skeletonAnimation.Update(deltaTime);
}
}
private class SkeletonGraphicWrapper : IEditorSkeletonWrapper
{
private SkeletonGraphic _skeletonGraphic;
public SkeletonGraphicWrapper(SkeletonGraphic skeletonGraphic)
{
_skeletonGraphic = skeletonGraphic;
}
public string AnimationName
{
get { return _skeletonGraphic.startingAnimation; }
}
public bool Loop
{
get { return _skeletonGraphic.startingLoop; }
}
public float Speed
{
get { return _skeletonGraphic.timeScale; }
}
public Spine.AnimationState State
{
get { return _skeletonGraphic.AnimationState; }
}
public void Update(float deltaTime)
{
_skeletonGraphic.Update(deltaTime);
}
}
private interface IEditorSkeletonWrapper
{
string AnimationName { get; }
bool Loop { get; }
float Speed { get; }
Spine.AnimationState State { get; }
void Update(float deltaTime);
}
}
#endif
@Sekomu Awesome, thanks very much for sharing!
Sekomu (If possible, I think it would be great if this feature could be officially adopted into the Spine Unity Runtime
)
We would be happy to include such a component in the spine-runtimes officially (potentially with some slight modifications)! Could you please send us a signed contributor license agreement (CLA)
to contact@esotericsoftware.com as listed in the Contributing
section here:
https://github.com/EsotericSoftware/spine-runtimes/tree/4.1/#contributing
Harald
https://esotericsoftware.com/forum/d/11476-unity-nested-prefab-overrides/11
I've written a CLA in the past, please let me know if I need to rewrite it.
Here is the Pull Request. I would appreciate a review.
@Sekomu Thanks very much for the pull request, sorry that I missed that you already sent a CLA in the past!
We have merged the pull request and added some improvements on top of it, e.g. added single frame preview as well via a Fixed Track Time
parameter. This has been requested in this issue ticket.
From the changelog:
Added experimental
EditorSkeletonPlayer
component to allow Editor playback of the initial animation set atSkeletonAnimation
orSkeletonGraphic
components. Add this component to your skeleton GameObject to enable the in-editor animation preview. Allows configurations for continuous playback when selected, deselected, and alternative single-frame preview by settingFixed Track Time
to any value other than 0. Limitations: At skeletons with variable material count the Inspector preview may be too unresponsive. It is then recommended to disable theEditorSkeletonPlayer
component (at the top of the Inspector) to make it responsive again, then you can disablePlay When Selected
and re-enable the component to preview playback only when deselected.
New spine-unity 4.1 and 4.2-beta unitypackages are available here as usual:
https://esotericsoftware.com/spine-unity-download