mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2025-12-22 02:06:03 +08:00
AnimationState queued animation for more runtimes, plus fixes.
This commit is contained in:
parent
2c3453f4dd
commit
ed0bae85c3
@ -94,7 +94,7 @@ void AnimationState_addAnimation (AnimationState* self, Animation* animation, in
|
||||
|
||||
if (delay <= 0) {
|
||||
if (previousAnimation)
|
||||
delay = self->animation->duration - AnimationStateData_getMix(self->data, self->animation, previousAnimation) + delay;
|
||||
delay = previousAnimation->duration - AnimationStateData_getMix(self->data, previousAnimation, animation) + delay;
|
||||
else
|
||||
delay = 0;
|
||||
}
|
||||
@ -141,6 +141,7 @@ void AnimationState_clearAnimation (AnimationState* self) {
|
||||
}
|
||||
|
||||
void AnimationState_update (AnimationState* self, float delta) {
|
||||
_Entry* next;
|
||||
_Internal* internal = SUB_CAST(_Internal, self);
|
||||
|
||||
self->time += delta;
|
||||
@ -149,7 +150,7 @@ void AnimationState_update (AnimationState* self, float delta) {
|
||||
|
||||
if (internal->queue && self->time >= internal->queue->delay) {
|
||||
_AnimationState_setAnimation(self, internal->queue->animation, internal->queue->loop);
|
||||
_Entry* next = internal->queue->next;
|
||||
next = internal->queue->next;
|
||||
FREE(internal->queue);
|
||||
internal->queue = next;
|
||||
}
|
||||
|
||||
@ -33,9 +33,10 @@ namespace Spine {
|
||||
public float Time { get; set; }
|
||||
public bool Loop { get; set; }
|
||||
private Animation previous;
|
||||
float previousTime;
|
||||
bool previousLoop;
|
||||
float mixTime, mixDuration;
|
||||
private float previousTime;
|
||||
private bool previousLoop;
|
||||
private float mixTime, mixDuration;
|
||||
private List<QueueEntry> queue = new List<QueueEntry>();
|
||||
|
||||
public AnimationState (AnimationStateData data) {
|
||||
if (data == null) throw new ArgumentNullException("data cannot be null.");
|
||||
@ -46,6 +47,14 @@ namespace Spine {
|
||||
Time += delta;
|
||||
previousTime += delta;
|
||||
mixTime += delta;
|
||||
|
||||
if (queue.Count > 0) {
|
||||
QueueEntry entry = queue[0];
|
||||
if (Time >= entry.delay) {
|
||||
SetAnimationInternal(entry.animation, entry.loop);
|
||||
queue.RemoveAt(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Apply (Skeleton skeleton) {
|
||||
@ -62,13 +71,40 @@ namespace Spine {
|
||||
Animation.Apply(skeleton, Time, Loop);
|
||||
}
|
||||
|
||||
public void SetAnimation (String animationName, bool loop) {
|
||||
Animation animation = Data.SkeletonData.FindAnimation(animationName);
|
||||
if (animation == null) throw new ArgumentException("Animation not found: " + animationName);
|
||||
SetAnimation(animation, loop);
|
||||
public void AddAnimation (String animationName, bool loop) {
|
||||
AddAnimation(animationName, loop, 0);
|
||||
}
|
||||
|
||||
public void SetAnimation (Animation animation, bool loop) {
|
||||
public void AddAnimation (String animationName, bool loop, float delay) {
|
||||
Animation animation = Data.SkeletonData.FindAnimation(animationName);
|
||||
if (animation == null) throw new ArgumentException("Animation not found: " + animationName);
|
||||
AddAnimation(animation, loop, delay);
|
||||
}
|
||||
|
||||
public void AddAnimation (Animation animation, bool loop) {
|
||||
AddAnimation(animation, loop, 0);
|
||||
}
|
||||
|
||||
/** Adds an animation to be played delay seconds after the current or last queued animation.
|
||||
* @param delay May be <= 0 to use duration of previous animation minus any mix duration plus the negative delay. */
|
||||
public void AddAnimation (Animation animation, bool loop, float delay) {
|
||||
QueueEntry entry = new QueueEntry();
|
||||
entry.animation = animation;
|
||||
entry.loop = loop;
|
||||
|
||||
if (delay <= 0) {
|
||||
Animation previousAnimation = queue.Count == 0 ? Animation : queue[queue.Count - 1].animation;
|
||||
if (previousAnimation != null)
|
||||
delay = previousAnimation.Duration - Data.GetMix(previousAnimation, animation) + delay;
|
||||
else
|
||||
delay = 0;
|
||||
}
|
||||
entry.delay = delay;
|
||||
|
||||
queue.Add(entry);
|
||||
}
|
||||
|
||||
private void SetAnimationInternal (Animation animation, bool loop) {
|
||||
previous = null;
|
||||
if (animation != null && Animation != null) {
|
||||
mixDuration = Data.GetMix(Animation, animation);
|
||||
@ -84,13 +120,25 @@ namespace Spine {
|
||||
Time = 0;
|
||||
}
|
||||
|
||||
public void SetAnimation (String animationName, bool loop) {
|
||||
Animation animation = Data.SkeletonData.FindAnimation(animationName);
|
||||
if (animation == null) throw new ArgumentException("Animation not found: " + animationName);
|
||||
SetAnimation(animation, loop);
|
||||
}
|
||||
|
||||
public void SetAnimation (Animation animation, bool loop) {
|
||||
queue.Clear();
|
||||
SetAnimationInternal(animation, loop);
|
||||
}
|
||||
|
||||
public void ClearAnimation () {
|
||||
previous = null;
|
||||
Animation = null;
|
||||
queue.Clear();
|
||||
}
|
||||
|
||||
/** Returns true if no animation is set or if the current time is greater than the animation duration, regardless of looping. */
|
||||
public bool isComplete () {
|
||||
public bool IsComplete () {
|
||||
return Animation == null || Time >= Animation.Duration;
|
||||
}
|
||||
|
||||
@ -99,3 +147,9 @@ namespace Spine {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class QueueEntry {
|
||||
public Spine.Animation animation;
|
||||
public bool loop;
|
||||
public float delay;
|
||||
}
|
||||
|
||||
@ -25,15 +25,17 @@
|
||||
|
||||
package com.esotericsoftware.spine;
|
||||
|
||||
import com.badlogic.gdx.math.MathUtils;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.Pools;
|
||||
|
||||
/** Stores state for an animation and automatically mixes between animations. */
|
||||
public class AnimationState {
|
||||
private final AnimationStateData data;
|
||||
Animation current, previous;
|
||||
float currentTime, previousTime;
|
||||
boolean currentLoop, previousLoop;
|
||||
float mixTime, mixDuration;
|
||||
private Animation current, previous;
|
||||
private float currentTime, previousTime;
|
||||
private boolean currentLoop, previousLoop;
|
||||
private float mixTime, mixDuration;
|
||||
private Array<QueueEntry> queue = new Array();
|
||||
|
||||
public AnimationState (AnimationStateData data) {
|
||||
if (data == null) throw new IllegalArgumentException("data cannot be null.");
|
||||
@ -44,6 +46,15 @@ public class AnimationState {
|
||||
currentTime += delta;
|
||||
previousTime += delta;
|
||||
mixTime += delta;
|
||||
|
||||
if (queue.size > 0) {
|
||||
QueueEntry entry = queue.first();
|
||||
if (currentTime >= entry.delay) {
|
||||
setAnimationInternal(entry.animation, entry.loop);
|
||||
Pools.free(entry);
|
||||
queue.removeIndex(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton) {
|
||||
@ -63,18 +74,15 @@ public class AnimationState {
|
||||
public void clearAnimation () {
|
||||
previous = null;
|
||||
current = null;
|
||||
clearQueue();
|
||||
}
|
||||
|
||||
/** @see #setAnimation(Animation, boolean) */
|
||||
public void setAnimation (String animationName, boolean loop) {
|
||||
Animation animation = data.getSkeletonData().findAnimation(animationName);
|
||||
if (animation == null) throw new IllegalArgumentException("Animation not found: " + animationName);
|
||||
setAnimation(animation, loop);
|
||||
private void clearQueue () {
|
||||
Pools.freeAll(queue);
|
||||
queue.clear();
|
||||
}
|
||||
|
||||
/** Set the current animation. The current animation time is set to 0.
|
||||
* @param animation May be null. */
|
||||
public void setAnimation (Animation animation, boolean loop) {
|
||||
private void setAnimationInternal (Animation animation, boolean loop) {
|
||||
previous = null;
|
||||
if (animation != null && current != null) {
|
||||
mixDuration = data.getMix(current, animation);
|
||||
@ -90,6 +98,57 @@ public class AnimationState {
|
||||
currentTime = 0;
|
||||
}
|
||||
|
||||
/** @see #setAnimation(Animation, boolean) */
|
||||
public void setAnimation (String animationName, boolean loop) {
|
||||
Animation animation = data.getSkeletonData().findAnimation(animationName);
|
||||
if (animation == null) throw new IllegalArgumentException("Animation not found: " + animationName);
|
||||
setAnimation(animation, loop);
|
||||
}
|
||||
|
||||
/** Set the current animation. Any queued animations are cleared and the current animation time is set to 0.
|
||||
* @param animation May be null. */
|
||||
public void setAnimation (Animation animation, boolean loop) {
|
||||
clearQueue();
|
||||
setAnimationInternal(animation, loop);
|
||||
}
|
||||
|
||||
/** @see #addAnimation(Animation, boolean) */
|
||||
public void addAnimation (String animationName, boolean loop) {
|
||||
addAnimation(animationName, loop, 0);
|
||||
}
|
||||
|
||||
/** @see #addAnimation(Animation, boolean, float) */
|
||||
public void addAnimation (String animationName, boolean loop, float delay) {
|
||||
Animation animation = data.getSkeletonData().findAnimation(animationName);
|
||||
if (animation == null) throw new IllegalArgumentException("Animation not found: " + animationName);
|
||||
addAnimation(animation, loop, delay);
|
||||
}
|
||||
|
||||
/** Adds an animation to be played delay seconds after the current or last queued animation, taking into account any mix
|
||||
* duration. */
|
||||
public void addAnimation (Animation animation, boolean loop) {
|
||||
addAnimation(animation, loop, 0);
|
||||
}
|
||||
|
||||
/** Adds an animation to be played delay seconds after the current or last queued animation.
|
||||
* @param delay May be <= 0 to use duration of previous animation minus any mix duration plus the negative delay. */
|
||||
public void addAnimation (Animation animation, boolean loop, float delay) {
|
||||
QueueEntry entry = Pools.obtain(QueueEntry.class);
|
||||
entry.animation = animation;
|
||||
entry.loop = loop;
|
||||
|
||||
if (delay <= 0) {
|
||||
Animation previousAnimation = queue.size == 0 ? current : queue.peek().animation;
|
||||
if (previousAnimation != null)
|
||||
delay = previousAnimation.getDuration() - data.getMix(previousAnimation, animation) + delay;
|
||||
else
|
||||
delay = 0;
|
||||
}
|
||||
entry.delay = delay;
|
||||
|
||||
queue.add(entry);
|
||||
}
|
||||
|
||||
/** @return May be null. */
|
||||
public Animation getAnimation () {
|
||||
return current;
|
||||
@ -116,4 +175,10 @@ public class AnimationState {
|
||||
public String toString () {
|
||||
return (current != null && current.getName() != null) ? current.getName() : super.toString();
|
||||
}
|
||||
|
||||
static private class QueueEntry {
|
||||
Animation animation;
|
||||
boolean loop;
|
||||
float delay;
|
||||
}
|
||||
}
|
||||
|
||||
@ -54,7 +54,7 @@ public class AnimationStateTest extends ApplicationAdapter {
|
||||
|
||||
// Define mixing between animations.
|
||||
AnimationStateData stateData = new AnimationStateData(skeletonData);
|
||||
stateData.setMix("walk", "jump", 0.4f);
|
||||
stateData.setMix("walk", "jump", 0.2f);
|
||||
stateData.setMix("jump", "walk", 0.4f);
|
||||
|
||||
state = new AnimationState(stateData);
|
||||
|
||||
@ -41,11 +41,11 @@ void spineboy () {
|
||||
|
||||
// Configure mixing.
|
||||
AnimationStateData* stateData = AnimationStateData_create(skeletonData);
|
||||
AnimationStateData_setMixByName(stateData, "walk", "jump", 0.4f);
|
||||
AnimationStateData_setMixByName(stateData, "walk", "jump", 0.2f);
|
||||
AnimationStateData_setMixByName(stateData, "jump", "walk", 0.4f);
|
||||
|
||||
SkeletonDrawable* drawable = new SkeletonDrawable(skeletonData, stateData);
|
||||
drawable->timeScale = 0.5f;
|
||||
drawable->timeScale = 1;
|
||||
|
||||
Skeleton* skeleton = drawable->skeleton;
|
||||
skeleton->flipX = false;
|
||||
@ -57,6 +57,12 @@ void spineboy () {
|
||||
Skeleton_updateWorldTransform(skeleton);
|
||||
|
||||
AnimationState_setAnimationByName(drawable->state, "walk", true);
|
||||
AnimationState_addAnimationByName(drawable->state, "jump", false, 0);
|
||||
AnimationState_addAnimationByName(drawable->state, "walk", true, 0);
|
||||
AnimationState_addAnimationByName(drawable->state, "jump", false, 3);
|
||||
AnimationState_addAnimationByName(drawable->state, "walk", true, 0);
|
||||
AnimationState_addAnimationByName(drawable->state, 0, true, 0);
|
||||
AnimationState_addAnimationByName(drawable->state, "walk", false, 1);
|
||||
|
||||
sf::RenderWindow window(sf::VideoMode(640, 480), "Spine SFML");
|
||||
window.setFramerateLimit(60);
|
||||
@ -69,11 +75,11 @@ void spineboy () {
|
||||
float delta = deltaClock.getElapsedTime().asSeconds();
|
||||
deltaClock.restart();
|
||||
|
||||
if (drawable->state->loop) {
|
||||
/*if (drawable->state->loop) {
|
||||
if (drawable->state->time > 2) AnimationState_setAnimationByName(drawable->state, "jump", false);
|
||||
} else {
|
||||
if (drawable->state->time > 1) AnimationState_setAnimationByName(drawable->state, "walk", true);
|
||||
}
|
||||
}*/
|
||||
|
||||
drawable->update(delta);
|
||||
|
||||
|
||||
@ -40,8 +40,7 @@ namespace Spine {
|
||||
GraphicsDeviceManager graphics;
|
||||
SkeletonRenderer skeletonRenderer;
|
||||
Skeleton skeleton;
|
||||
Animation animation;
|
||||
float time;
|
||||
AnimationState state;
|
||||
|
||||
public Example () {
|
||||
graphics = new GraphicsDeviceManager(this);
|
||||
@ -59,13 +58,25 @@ namespace Spine {
|
||||
protected override void LoadContent () {
|
||||
skeletonRenderer = new SkeletonRenderer(GraphicsDevice);
|
||||
|
||||
Atlas atlas = new Atlas("data/goblins.atlas", new XnaTextureLoader(GraphicsDevice));
|
||||
String name = "spineboy"; // "goblins";
|
||||
|
||||
Atlas atlas = new Atlas("data/" + name + ".atlas", new XnaTextureLoader(GraphicsDevice));
|
||||
SkeletonJson json = new SkeletonJson(atlas);
|
||||
skeleton = new Skeleton(json.ReadSkeletonData("data/goblins.json"));
|
||||
skeleton.SetSkin("goblingirl");
|
||||
skeleton = new Skeleton(json.ReadSkeletonData("data/" + name + ".json"));
|
||||
if (name == "goblins") skeleton.SetSkin("goblingirl");
|
||||
skeleton.SetSlotsToBindPose(); // Without this the skin attachments won't be attached. See SetSkin.
|
||||
skeleton.SetAttachment("left hand item", "spear");
|
||||
animation = skeleton.Data.FindAnimation("walk");
|
||||
|
||||
// Define mixing between animations.
|
||||
AnimationStateData stateData = new AnimationStateData(skeleton.Data);
|
||||
if (name == "spineboy") {
|
||||
stateData.SetMix("walk", "jump", 0.2f);
|
||||
stateData.SetMix("jump", "walk", 0.4f);
|
||||
}
|
||||
|
||||
state = new AnimationState(stateData);
|
||||
state.SetAnimation("walk", false);
|
||||
state.AddAnimation("jump", false);
|
||||
state.AddAnimation("walk", true);
|
||||
|
||||
skeleton.RootBone.X = 320;
|
||||
skeleton.RootBone.Y = 440;
|
||||
@ -89,8 +100,8 @@ namespace Spine {
|
||||
protected override void Draw (GameTime gameTime) {
|
||||
GraphicsDevice.Clear(Color.Black);
|
||||
|
||||
time += gameTime.ElapsedGameTime.Milliseconds / 1000f;
|
||||
animation.Apply(skeleton, time, true);
|
||||
state.Update(gameTime.ElapsedGameTime.Milliseconds / 1000f);
|
||||
state.Apply(skeleton);
|
||||
skeleton.UpdateWorldTransform();
|
||||
skeletonRenderer.Begin();
|
||||
skeletonRenderer.Draw(skeleton);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user