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 (delay <= 0) {
|
||||||
if (previousAnimation)
|
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
|
else
|
||||||
delay = 0;
|
delay = 0;
|
||||||
}
|
}
|
||||||
@ -141,6 +141,7 @@ void AnimationState_clearAnimation (AnimationState* self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AnimationState_update (AnimationState* self, float delta) {
|
void AnimationState_update (AnimationState* self, float delta) {
|
||||||
|
_Entry* next;
|
||||||
_Internal* internal = SUB_CAST(_Internal, self);
|
_Internal* internal = SUB_CAST(_Internal, self);
|
||||||
|
|
||||||
self->time += delta;
|
self->time += delta;
|
||||||
@ -149,7 +150,7 @@ void AnimationState_update (AnimationState* self, float delta) {
|
|||||||
|
|
||||||
if (internal->queue && self->time >= internal->queue->delay) {
|
if (internal->queue && self->time >= internal->queue->delay) {
|
||||||
_AnimationState_setAnimation(self, internal->queue->animation, internal->queue->loop);
|
_AnimationState_setAnimation(self, internal->queue->animation, internal->queue->loop);
|
||||||
_Entry* next = internal->queue->next;
|
next = internal->queue->next;
|
||||||
FREE(internal->queue);
|
FREE(internal->queue);
|
||||||
internal->queue = next;
|
internal->queue = next;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,9 +33,10 @@ namespace Spine {
|
|||||||
public float Time { get; set; }
|
public float Time { get; set; }
|
||||||
public bool Loop { get; set; }
|
public bool Loop { get; set; }
|
||||||
private Animation previous;
|
private Animation previous;
|
||||||
float previousTime;
|
private float previousTime;
|
||||||
bool previousLoop;
|
private bool previousLoop;
|
||||||
float mixTime, mixDuration;
|
private float mixTime, mixDuration;
|
||||||
|
private List<QueueEntry> queue = new List<QueueEntry>();
|
||||||
|
|
||||||
public AnimationState (AnimationStateData data) {
|
public AnimationState (AnimationStateData data) {
|
||||||
if (data == null) throw new ArgumentNullException("data cannot be null.");
|
if (data == null) throw new ArgumentNullException("data cannot be null.");
|
||||||
@ -46,6 +47,14 @@ namespace Spine {
|
|||||||
Time += delta;
|
Time += delta;
|
||||||
previousTime += delta;
|
previousTime += delta;
|
||||||
mixTime += 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) {
|
public void Apply (Skeleton skeleton) {
|
||||||
@ -62,13 +71,40 @@ namespace Spine {
|
|||||||
Animation.Apply(skeleton, Time, Loop);
|
Animation.Apply(skeleton, Time, Loop);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetAnimation (String animationName, bool loop) {
|
public void AddAnimation (String animationName, bool loop) {
|
||||||
Animation animation = Data.SkeletonData.FindAnimation(animationName);
|
AddAnimation(animationName, loop, 0);
|
||||||
if (animation == null) throw new ArgumentException("Animation not found: " + animationName);
|
|
||||||
SetAnimation(animation, loop);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
previous = null;
|
||||||
if (animation != null && Animation != null) {
|
if (animation != null && Animation != null) {
|
||||||
mixDuration = Data.GetMix(Animation, animation);
|
mixDuration = Data.GetMix(Animation, animation);
|
||||||
@ -84,13 +120,25 @@ namespace Spine {
|
|||||||
Time = 0;
|
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 () {
|
public void ClearAnimation () {
|
||||||
previous = null;
|
previous = null;
|
||||||
Animation = 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. */
|
/** 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;
|
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;
|
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. */
|
/** Stores state for an animation and automatically mixes between animations. */
|
||||||
public class AnimationState {
|
public class AnimationState {
|
||||||
private final AnimationStateData data;
|
private final AnimationStateData data;
|
||||||
Animation current, previous;
|
private Animation current, previous;
|
||||||
float currentTime, previousTime;
|
private float currentTime, previousTime;
|
||||||
boolean currentLoop, previousLoop;
|
private boolean currentLoop, previousLoop;
|
||||||
float mixTime, mixDuration;
|
private float mixTime, mixDuration;
|
||||||
|
private Array<QueueEntry> queue = new Array();
|
||||||
|
|
||||||
public AnimationState (AnimationStateData data) {
|
public AnimationState (AnimationStateData data) {
|
||||||
if (data == null) throw new IllegalArgumentException("data cannot be null.");
|
if (data == null) throw new IllegalArgumentException("data cannot be null.");
|
||||||
@ -44,6 +46,15 @@ public class AnimationState {
|
|||||||
currentTime += delta;
|
currentTime += delta;
|
||||||
previousTime += delta;
|
previousTime += delta;
|
||||||
mixTime += 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) {
|
public void apply (Skeleton skeleton) {
|
||||||
@ -63,18 +74,15 @@ public class AnimationState {
|
|||||||
public void clearAnimation () {
|
public void clearAnimation () {
|
||||||
previous = null;
|
previous = null;
|
||||||
current = null;
|
current = null;
|
||||||
|
clearQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @see #setAnimation(Animation, boolean) */
|
private void clearQueue () {
|
||||||
public void setAnimation (String animationName, boolean loop) {
|
Pools.freeAll(queue);
|
||||||
Animation animation = data.getSkeletonData().findAnimation(animationName);
|
queue.clear();
|
||||||
if (animation == null) throw new IllegalArgumentException("Animation not found: " + animationName);
|
|
||||||
setAnimation(animation, loop);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Set the current animation. The current animation time is set to 0.
|
private void setAnimationInternal (Animation animation, boolean loop) {
|
||||||
* @param animation May be null. */
|
|
||||||
public void setAnimation (Animation animation, boolean loop) {
|
|
||||||
previous = null;
|
previous = null;
|
||||||
if (animation != null && current != null) {
|
if (animation != null && current != null) {
|
||||||
mixDuration = data.getMix(current, animation);
|
mixDuration = data.getMix(current, animation);
|
||||||
@ -90,6 +98,57 @@ public class AnimationState {
|
|||||||
currentTime = 0;
|
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. */
|
/** @return May be null. */
|
||||||
public Animation getAnimation () {
|
public Animation getAnimation () {
|
||||||
return current;
|
return current;
|
||||||
@ -116,4 +175,10 @@ public class AnimationState {
|
|||||||
public String toString () {
|
public String toString () {
|
||||||
return (current != null && current.getName() != null) ? current.getName() : super.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.
|
// Define mixing between animations.
|
||||||
AnimationStateData stateData = new AnimationStateData(skeletonData);
|
AnimationStateData stateData = new AnimationStateData(skeletonData);
|
||||||
stateData.setMix("walk", "jump", 0.4f);
|
stateData.setMix("walk", "jump", 0.2f);
|
||||||
stateData.setMix("jump", "walk", 0.4f);
|
stateData.setMix("jump", "walk", 0.4f);
|
||||||
|
|
||||||
state = new AnimationState(stateData);
|
state = new AnimationState(stateData);
|
||||||
|
|||||||
@ -41,11 +41,11 @@ void spineboy () {
|
|||||||
|
|
||||||
// Configure mixing.
|
// Configure mixing.
|
||||||
AnimationStateData* stateData = AnimationStateData_create(skeletonData);
|
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);
|
AnimationStateData_setMixByName(stateData, "jump", "walk", 0.4f);
|
||||||
|
|
||||||
SkeletonDrawable* drawable = new SkeletonDrawable(skeletonData, stateData);
|
SkeletonDrawable* drawable = new SkeletonDrawable(skeletonData, stateData);
|
||||||
drawable->timeScale = 0.5f;
|
drawable->timeScale = 1;
|
||||||
|
|
||||||
Skeleton* skeleton = drawable->skeleton;
|
Skeleton* skeleton = drawable->skeleton;
|
||||||
skeleton->flipX = false;
|
skeleton->flipX = false;
|
||||||
@ -57,6 +57,12 @@ void spineboy () {
|
|||||||
Skeleton_updateWorldTransform(skeleton);
|
Skeleton_updateWorldTransform(skeleton);
|
||||||
|
|
||||||
AnimationState_setAnimationByName(drawable->state, "walk", true);
|
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");
|
sf::RenderWindow window(sf::VideoMode(640, 480), "Spine SFML");
|
||||||
window.setFramerateLimit(60);
|
window.setFramerateLimit(60);
|
||||||
@ -69,11 +75,11 @@ void spineboy () {
|
|||||||
float delta = deltaClock.getElapsedTime().asSeconds();
|
float delta = deltaClock.getElapsedTime().asSeconds();
|
||||||
deltaClock.restart();
|
deltaClock.restart();
|
||||||
|
|
||||||
if (drawable->state->loop) {
|
/*if (drawable->state->loop) {
|
||||||
if (drawable->state->time > 2) AnimationState_setAnimationByName(drawable->state, "jump", false);
|
if (drawable->state->time > 2) AnimationState_setAnimationByName(drawable->state, "jump", false);
|
||||||
} else {
|
} else {
|
||||||
if (drawable->state->time > 1) AnimationState_setAnimationByName(drawable->state, "walk", true);
|
if (drawable->state->time > 1) AnimationState_setAnimationByName(drawable->state, "walk", true);
|
||||||
}
|
}*/
|
||||||
|
|
||||||
drawable->update(delta);
|
drawable->update(delta);
|
||||||
|
|
||||||
|
|||||||
@ -40,8 +40,7 @@ namespace Spine {
|
|||||||
GraphicsDeviceManager graphics;
|
GraphicsDeviceManager graphics;
|
||||||
SkeletonRenderer skeletonRenderer;
|
SkeletonRenderer skeletonRenderer;
|
||||||
Skeleton skeleton;
|
Skeleton skeleton;
|
||||||
Animation animation;
|
AnimationState state;
|
||||||
float time;
|
|
||||||
|
|
||||||
public Example () {
|
public Example () {
|
||||||
graphics = new GraphicsDeviceManager(this);
|
graphics = new GraphicsDeviceManager(this);
|
||||||
@ -59,13 +58,25 @@ namespace Spine {
|
|||||||
protected override void LoadContent () {
|
protected override void LoadContent () {
|
||||||
skeletonRenderer = new SkeletonRenderer(GraphicsDevice);
|
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);
|
SkeletonJson json = new SkeletonJson(atlas);
|
||||||
skeleton = new Skeleton(json.ReadSkeletonData("data/goblins.json"));
|
skeleton = new Skeleton(json.ReadSkeletonData("data/" + name + ".json"));
|
||||||
skeleton.SetSkin("goblingirl");
|
if (name == "goblins") skeleton.SetSkin("goblingirl");
|
||||||
skeleton.SetSlotsToBindPose(); // Without this the skin attachments won't be attached. See SetSkin.
|
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.X = 320;
|
||||||
skeleton.RootBone.Y = 440;
|
skeleton.RootBone.Y = 440;
|
||||||
@ -89,8 +100,8 @@ namespace Spine {
|
|||||||
protected override void Draw (GameTime gameTime) {
|
protected override void Draw (GameTime gameTime) {
|
||||||
GraphicsDevice.Clear(Color.Black);
|
GraphicsDevice.Clear(Color.Black);
|
||||||
|
|
||||||
time += gameTime.ElapsedGameTime.Milliseconds / 1000f;
|
state.Update(gameTime.ElapsedGameTime.Milliseconds / 1000f);
|
||||||
animation.Apply(skeleton, time, true);
|
state.Apply(skeleton);
|
||||||
skeleton.UpdateWorldTransform();
|
skeleton.UpdateWorldTransform();
|
||||||
skeletonRenderer.Begin();
|
skeletonRenderer.Begin();
|
||||||
skeletonRenderer.Draw(skeleton);
|
skeletonRenderer.Draw(skeleton);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user