Forráskód Böngészése

AnimationState queued animation for more runtimes, plus fixes.

NathanSweet 12 éve
szülő
commit
ed0bae85c3

+ 3 - 2
spine-c/src/spine/AnimationState.c

@@ -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;
 	}

+ 61 - 7
spine-csharp/src/AnimationState.cs

@@ -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) {
+		public void AddAnimation (String animationName, bool loop) {
+			AddAnimation(animationName, loop, 0);
+		}
+
+		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);
-			SetAnimation(animation, loop);
+			AddAnimation(animation, loop, delay);
 		}
 
-		public void SetAnimation (Animation animation, bool loop) {
+		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;
+}

+ 78 - 13
spine-libgdx/src/com/esotericsoftware/spine/AnimationState.java

@@ -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;
+	}
 }

+ 1 - 1
spine-libgdx/test/com/esotericsoftware/spine/AnimationStateTest.java

@@ -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);

+ 13 - 7
spine-sfml/example/main.cpp

@@ -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->time > 2) AnimationState_setAnimationByName(drawable->state, "jump", false);
-		} else {
-			if (drawable->state->time > 1) AnimationState_setAnimationByName(drawable->state, "walk", true);
-		}
+		/*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);
 

+ 20 - 9
spine-xna/example/src/ExampleGame.cs

@@ -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);