Эх сурвалжийг харах

Merge branch '4.0' into 4.1-beta

Mario Zechner 3 жил өмнө
parent
commit
e6aefc1065

+ 40 - 25
spine-csharp/src/AnimationState.cs

@@ -40,7 +40,7 @@ namespace Spine {
 	/// See <a href='http://esotericsoftware.com/spine-applying-animations/'>Applying Animations</a> in the Spine Runtimes Guide.</para>
 	/// </summary>
 	public class AnimationState {
-		static readonly Animation EmptyAnimation = new Animation("<empty>", new ExposedList<Timeline>(), 0);
+		internal static readonly Animation EmptyAnimation = new Animation("<empty>", new ExposedList<Timeline>(), 0);
 
 		/// 1) A previously applied timeline has set this property.<para />
 		/// Result: Mix from the current pose to the timeline pose.
@@ -309,9 +309,11 @@ namespace Spine {
 			return applied;
 		}
 
-		/// <summary>Version of <see cref="Apply"/> only applying EventTimelines for lightweight off-screen updates.</summary>
+		/// <summary>Version of <see cref="Apply"/> only applying and updating time at
+		/// EventTimelines for lightweight off-screen updates.</summary>
+		/// <param name="issueEvents">When set to false, only animation times of TrackEntries are updated.</param>
 		// Note: This method is not part of the libgdx reference implementation.
-		public bool ApplyEventTimelinesOnly (Skeleton skeleton) {
+		public bool ApplyEventTimelinesOnly (Skeleton skeleton, bool issueEvents = true) {
 			if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null.");
 
 			ExposedList<Event> events = this.events;
@@ -323,24 +325,28 @@ namespace Spine {
 				applied = true;
 
 				// Apply mixing from entries first.
-				if (current.mixingFrom != null) ApplyMixingFromEventTimelinesOnly(current, skeleton);
+				if (current.mixingFrom != null) ApplyMixingFromEventTimelinesOnly(current, skeleton, issueEvents);
 
 				// Apply current entry.
 				float animationLast = current.animationLast, animationTime = current.AnimationTime;
-				int timelineCount = current.animation.timelines.Count;
-				Timeline[] timelines = current.animation.timelines.Items;
-				for (int ii = 0; ii < timelineCount; ii++) {
-					Timeline timeline = timelines[ii];
-					if (timeline is EventTimeline)
-						timeline.Apply(skeleton, animationLast, animationTime, events, 1.0f, MixBlend.Setup, MixDirection.In);
+
+				if (issueEvents) {
+					int timelineCount = current.animation.timelines.Count;
+					Timeline[] timelines = current.animation.timelines.Items;
+					for (int ii = 0; ii < timelineCount; ii++) {
+						Timeline timeline = timelines[ii];
+						if (timeline is EventTimeline)
+							timeline.Apply(skeleton, animationLast, animationTime, events, 1.0f, MixBlend.Setup, MixDirection.In);
+					}
+					QueueEvents(current, animationTime);
+					events.Clear(false);
 				}
-				QueueEvents(current, animationTime);
-				events.Clear(false);
 				current.nextAnimationLast = animationTime;
 				current.nextTrackLast = current.trackTime;
 			}
 
-			queue.Drain();
+			if (issueEvents)
+				queue.Drain();
 			return applied;
 		}
 
@@ -434,11 +440,14 @@ namespace Spine {
 			return mix;
 		}
 
-		/// <summary>Version of <see cref="ApplyMixingFrom"/> only applying EventTimelines for lightweight off-screen updates.</summary>
+		/// <summary>Version of <see cref="ApplyMixingFrom"/> only applying and updating time at
+		/// EventTimelines for lightweight off-screen updates.</summary>
+		/// <param name="issueEvents">When set to false, only animation times of TrackEntries are updated.</param>
 		// Note: This method is not part of the libgdx reference implementation.
-		private float ApplyMixingFromEventTimelinesOnly (TrackEntry to, Skeleton skeleton) {
+		private float ApplyMixingFromEventTimelinesOnly (TrackEntry to, Skeleton skeleton, bool issueEvents) {
 			TrackEntry from = to.mixingFrom;
-			if (from.mixingFrom != null) ApplyMixingFromEventTimelinesOnly(from, skeleton);
+			if (from.mixingFrom != null) ApplyMixingFromEventTimelinesOnly(from, skeleton, issueEvents);
+
 
 			float mix;
 			if (to.mixDuration == 0) { // Single frame mix to undo mixingFrom changes.
@@ -452,16 +461,18 @@ namespace Spine {
 			if (eventBuffer == null) return mix;
 
 			float animationLast = from.animationLast, animationTime = from.AnimationTime;
-			int timelineCount = from.animation.timelines.Count;
-			Timeline[] timelines = from.animation.timelines.Items;
-			for (int i = 0; i < timelineCount; i++) {
-				Timeline timeline = timelines[i];
-				if (timeline is EventTimeline)
-					timeline.Apply(skeleton, animationLast, animationTime, eventBuffer, 0, MixBlend.Setup, MixDirection.Out);
-			}
+			if (issueEvents) {
+				int timelineCount = from.animation.timelines.Count;
+				Timeline[] timelines = from.animation.timelines.Items;
+				for (int i = 0; i < timelineCount; i++) {
+					Timeline timeline = timelines[i];
+					if (timeline is EventTimeline)
+						timeline.Apply(skeleton, animationLast, animationTime, eventBuffer, 0, MixBlend.Setup, MixDirection.Out);
+				}
 
-			if (to.mixDuration > 0) QueueEvents(from, animationTime);
-			this.events.Clear(false);
+				if (to.mixDuration > 0) QueueEvents(from, animationTime);
+				this.events.Clear(false);
+			}
 			from.nextAnimationLast = animationTime;
 			from.nextTrackLast = from.trackTime;
 
@@ -1242,6 +1253,10 @@ namespace Spine {
 		/// If true, the animation will be applied in reverse. Events are not fired when an animation is applied in reverse.</summary>
 		public bool Reverse { get { return reverse; } set { reverse = value; } }
 
+		/// <summary>Returns true if this entry is for the empty animation. See <see cref="AnimationState.SetEmptyAnimation(int, float)"/>,
+		/// <see cref="AnimationState.AddEmptyAnimation(int, float, float)"/>, and <see cref="AnimationState.SetEmptyAnimations(float)"/>.
+		public bool IsEmptyAnimation { get { return animation == AnimationState.EmptyAnimation; } }
+
 		/// <summary>
 		/// <para>
 		/// Resets the rotation directions for mixing this entry's rotate timelines. This can be useful to avoid bones rotating the

+ 1 - 1
spine-csharp/src/Bone.cs

@@ -274,7 +274,7 @@ namespace Spine {
 		///  Some information is ambiguous in the world transform, such as -1,-1 scale versus 180 rotation. The applied transform after
 		/// calling this method is equivalent to the local transform used to compute the world transform, but may not be identical.
 		/// </para></summary>
-		internal void UpdateAppliedTransform () {
+		public void UpdateAppliedTransform () {
 			Bone parent = this.parent;
 			if (parent == null) {
 				ax = worldX - skeleton.x;

+ 6 - 1
spine-unity/Assets/Spine/Runtime/spine-unity/Asset Types/SkeletonDataCompatibility.cs

@@ -173,11 +173,16 @@ namespace Spine.Unity {
 			int i = 0;
 			if (content.Length >= 3 && content[0] == 0xEF && content[1] == 0xBB && content[2] == 0xBF) // skip potential BOM
 				i = 3;
+			bool openingBraceFound = false;
 			for (; i < numCharsToCheck; ++i) {
 				char c = (char)content[i];
 				if (char.IsWhiteSpace(c))
 					continue;
-				return c == '{';
+				if (!openingBraceFound) {
+					if (c == '{') openingBraceFound = true;
+					else return false;
+				} else
+					return c == '"';
 			}
 			return true;
 		}

+ 16 - 2
spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonAnimation.cs

@@ -206,8 +206,10 @@ namespace Spine.Unity {
 				return;
 			UpdateAnimationStatus(deltaTime);
 
-			if (updateMode == UpdateMode.OnlyAnimationStatus)
+			if (updateMode == UpdateMode.OnlyAnimationStatus) {
+				state.ApplyEventTimelinesOnly(skeleton, issueEvents: false);
 				return;
+			}
 			ApplyAnimation();
 		}
 
@@ -224,7 +226,7 @@ namespace Spine.Unity {
 			if (updateMode != UpdateMode.OnlyEventTimelines)
 				state.Apply(skeleton);
 			else
-				state.ApplyEventTimelinesOnly(skeleton);
+				state.ApplyEventTimelinesOnly(skeleton, issueEvents: true);
 
 			if (_UpdateLocal != null)
 				_UpdateLocal(this);
@@ -246,6 +248,18 @@ namespace Spine.Unity {
 			if (!wasUpdatedAfterInit) Update(0);
 			base.LateUpdate();
 		}
+
+		public override void OnBecameVisible () {
+			UpdateMode previousUpdateMode = updateMode;
+			updateMode = UpdateMode.FullUpdate;
+
+			// OnBecameVisible is called after LateUpdate()
+			if (previousUpdateMode != UpdateMode.FullUpdate &&
+				previousUpdateMode != UpdateMode.EverythingExceptMesh)
+				Update(0);
+			if (previousUpdateMode != UpdateMode.FullUpdate)
+				LateUpdate();
+		}
 	}
 
 }

+ 5 - 2
spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs

@@ -265,10 +265,13 @@ namespace Spine.Unity {
 			wasUpdatedAfterInit = true;
 			if (updateMode < UpdateMode.OnlyAnimationStatus)
 				return;
+
 			UpdateAnimationStatus(deltaTime);
 
-			if (updateMode == UpdateMode.OnlyAnimationStatus)
+			if (updateMode == UpdateMode.OnlyAnimationStatus) {
+				state.ApplyEventTimelinesOnly(skeleton, issueEvents: false);
 				return;
+			}
 			ApplyAnimation();
 		}
 
@@ -303,7 +306,7 @@ namespace Spine.Unity {
 			if (updateMode != UpdateMode.OnlyEventTimelines)
 				state.Apply(skeleton);
 			else
-				state.ApplyEventTimelinesOnly(skeleton);
+				state.ApplyEventTimelinesOnly(skeleton, issueEvents: true);
 
 			if (UpdateLocal != null)
 				UpdateLocal(this);

+ 12 - 0
spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonMecanim.cs

@@ -140,6 +140,18 @@ namespace Spine.Unity {
 			base.LateUpdate();
 		}
 
+		public override void OnBecameVisible () {
+			UpdateMode previousUpdateMode = updateMode;
+			updateMode = UpdateMode.FullUpdate;
+
+			// OnBecameVisible is called after LateUpdate()
+			if (previousUpdateMode != UpdateMode.FullUpdate &&
+				previousUpdateMode != UpdateMode.EverythingExceptMesh)
+				Update();
+			if (previousUpdateMode != UpdateMode.FullUpdate)
+				LateUpdate();
+		}
+
 		[System.Serializable]
 		public class MecanimTranslator {
 

+ 4 - 2
spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs

@@ -554,11 +554,13 @@ namespace Spine.Unity {
 				OnMeshAndMaterialsUpdated(this);
 		}
 
-		public void OnBecameVisible () {
+		public virtual void OnBecameVisible () {
 			UpdateMode previousUpdateMode = updateMode;
 			updateMode = UpdateMode.FullUpdate;
+
+			// OnBecameVisible is called after LateUpdate()
 			if (previousUpdateMode != UpdateMode.FullUpdate)
-				LateUpdate(); // OnBecameVisible is called after LateUpdate()
+				LateUpdate();
 		}
 
 		public void OnBecameInvisible () {

+ 1 - 1
spine-unity/Modules/com.esotericsoftware.spine.timeline/Editor/SpineAnimationStateClipInspector.cs

@@ -79,7 +79,7 @@ namespace Spine.Unity.Editor {
 			if (timelineClip == null)
 				return;
 
-			float blendInDur = (float)timelineClip.blendInDuration;
+			float blendInDur = System.Math.Max((float)timelineClip.blendInDuration, (float)timelineClip.easeInDuration);
 			bool isBlendingNow = blendInDur > 0;
 			bool wasBlendingBefore = timelineClipInfo.previousBlendInDuration > 0;
 

+ 74 - 51
spine-unity/Modules/com.esotericsoftware.spine.timeline/Runtime/SpineAnimationState/SpineAnimationStateMixerBehaviour.cs

@@ -38,8 +38,10 @@ namespace Spine.Unity.Playables {
 	public class SpineAnimationStateMixerBehaviour : PlayableBehaviour {
 
 		float[] lastInputWeights;
-		bool lastAnyTrackPlaying = false;
+		bool lastAnyClipPlaying = false;
 		public int trackIndex;
+		ScriptPlayable<SpineAnimationStateBehaviour>[] startingClips
+			= new ScriptPlayable<SpineAnimationStateBehaviour>[2];
 
 		IAnimationStateComponent animationStateComponent;
 		bool pauseWithDirector = true;
@@ -135,60 +137,81 @@ namespace Spine.Unity.Playables {
 					this.lastInputWeights[i] = default(float);
 			}
 			var lastInputWeights = this.lastInputWeights;
-			bool anyTrackPlaying = false;
+			int numStartingClips = 0;
+			bool anyClipPlaying = false;
 
 			// Check all clips. If a clip that was weight 0 turned into weight 1, call SetAnimation.
 			for (int i = 0; i < inputCount; i++) {
 				float lastInputWeight = lastInputWeights[i];
 				float inputWeight = playable.GetInputWeight(i);
-				bool trackStarted = lastInputWeight == 0 && inputWeight > 0;
+				bool clipStarted = lastInputWeight == 0 && inputWeight > 0;
 				if (inputWeight > 0)
-					anyTrackPlaying = true;
+					anyClipPlaying = true;
 				lastInputWeights[i] = inputWeight;
 
-				if (trackStarted) {
-					ScriptPlayable<SpineAnimationStateBehaviour> inputPlayable = (ScriptPlayable<SpineAnimationStateBehaviour>)playable.GetInput(i);
-					SpineAnimationStateBehaviour clipData = inputPlayable.GetBehaviour();
-
-					pauseWithDirector = !clipData.dontPauseWithDirector;
-					endAtClipEnd = !clipData.dontEndWithClip;
-					endMixOutDuration = clipData.endMixOutDuration;
-
-					if (clipData.animationReference == null) {
-						float mixDuration = clipData.customDuration ? clipData.mixDuration : state.Data.DefaultMix;
-						state.SetEmptyAnimation(trackIndex, mixDuration);
-					} else {
-						if (clipData.animationReference.Animation != null) {
-							Spine.TrackEntry trackEntry = state.SetAnimation(trackIndex, clipData.animationReference.Animation, clipData.loop);
-
-							trackEntry.EventThreshold = clipData.eventThreshold;
-							trackEntry.DrawOrderThreshold = clipData.drawOrderThreshold;
-							trackEntry.TrackTime = (float)inputPlayable.GetTime() * (float)inputPlayable.GetSpeed();
-							trackEntry.TimeScale = (float)inputPlayable.GetSpeed();
-							trackEntry.AttachmentThreshold = clipData.attachmentThreshold;
-							trackEntry.HoldPrevious = clipData.holdPrevious;
+				if (clipStarted && numStartingClips < 2) {
+					ScriptPlayable<SpineAnimationStateBehaviour> clipPlayable = (ScriptPlayable<SpineAnimationStateBehaviour>)playable.GetInput(i);
+					startingClips[numStartingClips++] = clipPlayable;
+				}
+			}
+			// unfortunately order of clips can be wrong when two start at the same time, we have to sort clips
+			if (numStartingClips == 2) {
+				ScriptPlayable<SpineAnimationStateBehaviour> clipPlayable0 = startingClips[0];
+				ScriptPlayable<SpineAnimationStateBehaviour> clipPlayable1 = startingClips[1];
+				if (clipPlayable0.GetDuration() > clipPlayable1.GetDuration()) { // swap, clip 0 ends after clip 1
+					startingClips[0] = clipPlayable1;
+					startingClips[1] = clipPlayable0;
+				}
+			}
 
-							if (clipData.customDuration)
-								trackEntry.MixDuration = clipData.mixDuration;
+			for (int j = 0; j < numStartingClips; ++j) {
+				ScriptPlayable<SpineAnimationStateBehaviour> clipPlayable = startingClips[j];
+				SpineAnimationStateBehaviour clipData = clipPlayable.GetBehaviour();
+				pauseWithDirector = !clipData.dontPauseWithDirector;
+				endAtClipEnd = !clipData.dontEndWithClip;
+				endMixOutDuration = clipData.endMixOutDuration;
 
-							timelineStartedTrackEntry = trackEntry;
-						}
-						//else Debug.LogWarningFormat("Animation named '{0}' not found", clipData.animationName);
+				if (clipData.animationReference == null) {
+					float mixDuration = clipData.customDuration ? clipData.mixDuration : state.Data.DefaultMix;
+					state.SetEmptyAnimation(trackIndex, mixDuration);
+				} else {
+					if (clipData.animationReference.Animation != null) {
+						TrackEntry currentEntry = state.GetCurrent(trackIndex);
+						Spine.TrackEntry trackEntry;
+						if (currentEntry == null && (clipData.customDuration && clipData.mixDuration > 0)) {
+							state.SetEmptyAnimation(trackIndex, 0); // ease in requires empty animation
+							trackEntry = state.AddAnimation(trackIndex, clipData.animationReference.Animation, clipData.loop, 0);
+						} else
+							trackEntry = state.SetAnimation(trackIndex, clipData.animationReference.Animation, clipData.loop);
+
+						trackEntry.EventThreshold = clipData.eventThreshold;
+						trackEntry.DrawOrderThreshold = clipData.drawOrderThreshold;
+						trackEntry.TrackTime = (float)clipPlayable.GetTime() * (float)clipPlayable.GetSpeed();
+						trackEntry.TimeScale = (float)clipPlayable.GetSpeed();
+						trackEntry.AttachmentThreshold = clipData.attachmentThreshold;
+						trackEntry.HoldPrevious = clipData.holdPrevious;
+
+						if (clipData.customDuration)
+							trackEntry.MixDuration = clipData.mixDuration;
+
+						timelineStartedTrackEntry = trackEntry;
 					}
+					//else Debug.LogWarningFormat("Animation named '{0}' not found", clipData.animationName);
+				}
 
-					// Ensure that the first frame ends with an updated mesh.
-					if (skeletonAnimation) {
-						skeletonAnimation.Update(0);
-						skeletonAnimation.LateUpdate();
-					} else if (skeletonGraphic) {
-						skeletonGraphic.Update(0);
-						skeletonGraphic.LateUpdate();
-					}
+				// Ensure that the first frame ends with an updated mesh.
+				if (skeletonAnimation) {
+					skeletonAnimation.Update(0);
+					skeletonAnimation.LateUpdate();
+				} else if (skeletonGraphic) {
+					skeletonGraphic.Update(0);
+					skeletonGraphic.LateUpdate();
 				}
 			}
-			if (lastAnyTrackPlaying && !anyTrackPlaying)
+			startingClips[0] = startingClips[1] = ScriptPlayable<SpineAnimationStateBehaviour>.Null;
+			if (lastAnyClipPlaying && !anyClipPlaying)
 				HandleClipEnd();
-			this.lastAnyTrackPlaying = anyTrackPlaying;
+			this.lastAnyClipPlaying = anyClipPlaying;
 		}
 
 #if SPINE_EDITMODEPOSE
@@ -251,25 +274,25 @@ namespace Spine.Unity.Playables {
 				if (fromAnimation != null && mixDuration > 0 && toClipTime < mixDuration) {
 					dummyAnimationState = dummyAnimationState ?? new AnimationState(skeletonComponent.SkeletonDataAsset.GetAnimationStateData());
 
-					var toTrack = dummyAnimationState.GetCurrent(0);
-					var fromTrack = toTrack != null ? toTrack.MixingFrom : null;
-					bool isAnimationTransitionMatch = (toTrack != null && toTrack.Animation == toAnimation && fromTrack != null && fromTrack.Animation == fromAnimation);
+					var toEntry = dummyAnimationState.GetCurrent(0);
+					var fromEntry = toEntry != null ? toEntry.MixingFrom : null;
+					bool isAnimationTransitionMatch = (toEntry != null && toEntry.Animation == toAnimation && fromEntry != null && fromEntry.Animation == fromAnimation);
 
 					if (!isAnimationTransitionMatch) {
 						dummyAnimationState.ClearTracks();
-						fromTrack = dummyAnimationState.SetAnimation(0, fromAnimation, fromClipLoop);
-						fromTrack.AllowImmediateQueue();
+						fromEntry = dummyAnimationState.SetAnimation(0, fromAnimation, fromClipLoop);
+						fromEntry.AllowImmediateQueue();
 						if (toAnimation != null) {
-							toTrack = dummyAnimationState.SetAnimation(0, toAnimation, clipData.loop);
-							toTrack.HoldPrevious = clipData.holdPrevious;
+							toEntry = dummyAnimationState.SetAnimation(0, toAnimation, clipData.loop);
+							toEntry.HoldPrevious = clipData.holdPrevious;
 						}
 					}
 
 					// Update track times.
-					fromTrack.TrackTime = fromClipTime;
-					if (toTrack != null) {
-						toTrack.TrackTime = toClipTime;
-						toTrack.MixTime = toClipTime;
+					fromEntry.TrackTime = fromClipTime;
+					if (toEntry != null) {
+						toEntry.TrackTime = toClipTime;
+						toEntry.MixTime = toClipTime;
 					}
 
 					// Apply Pose

+ 1 - 1
spine-unity/Modules/com.esotericsoftware.spine.timeline/package-no-spine-unity-dependency.json

@@ -2,7 +2,7 @@
 	"name": "com.esotericsoftware.spine.timeline",
 	"displayName": "Spine Timeline Extensions",
 	"description": "This plugin provides integration of spine-unity for the Unity Timeline.\n\nPrerequisites:\nIt requires a working installation of the spine-unity runtime (via the spine-unity unitypackage), version 4.0.\n(See http://esotericsoftware.com/git/spine-runtimes/spine-unity)",
-	"version": "4.0.5",
+	"version": "4.0.6",
 	"unity": "2018.3",
 	"author": {
 		"name": "Esoteric Software",

+ 1 - 1
spine-unity/Modules/com.esotericsoftware.spine.timeline/package.json

@@ -2,7 +2,7 @@
 	"name": "com.esotericsoftware.spine.timeline",
 	"displayName": "Spine Timeline Extensions",
 	"description": "This plugin provides integration of spine-unity for the Unity Timeline.\n\nPrerequisites:\nIt requires a working installation of the spine-unity and spine-csharp runtimes as UPM packages (not as spine-unity unitypackage), version 4.0.\n(See http://esotericsoftware.com/git/spine-runtimes/spine-unity)",
-	"version": "4.0.5",
+	"version": "4.0.6",
 	"unity": "2018.3",
 	"author": {
 		"name": "Esoteric Software",