diff --git a/CHANGELOG.md b/CHANGELOG.md
index d11382035..7605c991b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -247,6 +247,7 @@
* Added `BoundingBoxFollowerGraphic` component. This class is a counterpart of `BoundingBoxFollower` that can be used with `SkeletonGraphic`.
* Added Inspector context menu functions `SkeletonRenderer - Add all BoundingBoxFollower GameObjects` and `SkeletonGraphic - Add all BoundingBoxFollowerGraphic GameObjects` that automatically generate bounding box follower GameObjects for every `BoundingBoxAttachment` for all skins of a skeleton.
* `GetRemappedClone()` now provides an additional parameter `pivotShiftsMeshUVCoords` for `MeshAttachment` to prevent uv shifts at a non-central Sprite pivot. This parameter defaults to `true` to maintain previous behaviour.
+ * `SkeletonRenderer` components now provide an additional update mode `Only Event Timelines` at the `Update When Invisible` property. This mode saves additional timeline updates compared to update mode `Everything Except Mesh`.
* **Changes of default values**
* `SkeletonMecanim`'s `Layer Mix Mode` now defaults to `MixMode.MixNext` instead of `MixMode.MixAlways`.
diff --git a/spine-csharp/src/AnimationState.cs b/spine-csharp/src/AnimationState.cs
index e1c132a11..2ded456e1 100644
--- a/spine-csharp/src/AnimationState.cs
+++ b/spine-csharp/src/AnimationState.cs
@@ -300,6 +300,43 @@ namespace Spine {
return applied;
}
+ /// Version of only applying EventTimelines for lightweight off-screen updates.
+ // Note: This method is not part of the libgdx reference implementation.
+ public bool ApplyEventTimelinesOnly (Skeleton skeleton) {
+ if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null.");
+
+ var events = this.events;
+ bool applied = false;
+ var tracksItems = tracks.Items;
+ for (int i = 0, n = tracks.Count; i < n; i++) {
+ TrackEntry current = tracksItems[i];
+ if (current == null || current.delay > 0) continue;
+ applied = true;
+
+ // Apply mixing from entries first.
+ if (current.mixingFrom != null)
+ ApplyMixingFromEventTimelinesOnly(current, skeleton);
+
+ // Apply current entry.
+ float animationLast = current.animationLast, animationTime = current.AnimationTime;
+ int timelineCount = current.animation.timelines.Count;
+ var timelines = current.animation.timelines;
+ var timelinesItems = timelines.Items;
+ for (int ii = 0; ii < timelineCount; ii++) {
+ Timeline timeline = timelinesItems[ii];
+ if (timeline is EventTimeline)
+ timeline.Apply(skeleton, animationLast, animationTime, events, 1.0f, MixBlend.Setup, MixDirection.In);
+ }
+ QueueEvents(current, animationTime);
+ events.Clear(false);
+ current.nextAnimationLast = animationTime;
+ current.nextTrackLast = current.trackTime;
+ }
+
+ queue.Drain();
+ return applied;
+ }
+
private float ApplyMixingFrom (TrackEntry to, Skeleton skeleton, MixBlend blend) {
TrackEntry from = to.mixingFrom;
if (from.mixingFrom != null) ApplyMixingFrom(from, skeleton, blend);
@@ -386,6 +423,43 @@ namespace Spine {
return mix;
}
+ /// Version of only applying EventTimelines for lightweight off-screen updates.
+ // Note: This method is not part of the libgdx reference implementation.
+ private float ApplyMixingFromEventTimelinesOnly (TrackEntry to, Skeleton skeleton) {
+ TrackEntry from = to.mixingFrom;
+ if (from.mixingFrom != null) ApplyMixingFromEventTimelinesOnly(from, skeleton);
+
+ float mix;
+ if (to.mixDuration == 0) { // Single frame mix to undo mixingFrom changes.
+ mix = 1;
+ }
+ else {
+ mix = to.mixTime / to.mixDuration;
+ if (mix > 1) mix = 1;
+ }
+
+ var eventBuffer = mix < from.eventThreshold ? this.events : null;
+ if (eventBuffer == null)
+ return mix;
+
+ float animationLast = from.animationLast, animationTime = from.AnimationTime;
+ var timelines = from.animation.timelines;
+ int timelineCount = timelines.Count;
+ var timelinesItems = timelines.Items;
+ for (int i = 0; i < timelineCount; i++) {
+ var timeline = timelinesItems[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);
+ from.nextAnimationLast = animationTime;
+ from.nextTrackLast = from.trackTime;
+
+ return mix;
+ }
+
/// Applies the attachment timeline and sets .
/// False when: 1) the attachment timeline is mixing out, 2) mix < attachmentThreshold, and 3) the timeline
/// is not the last timeline to set the slot's attachment. In that case the timeline is applied only so subsequent
diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonAnimation.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonAnimation.cs
index 514eaa341..cfc5ddf29 100644
--- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonAnimation.cs
+++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonAnimation.cs
@@ -213,7 +213,10 @@ namespace Spine.Unity {
if (_BeforeApply != null)
_BeforeApply(this);
- state.Apply(skeleton);
+ if (updateMode != UpdateMode.OnlyEventTimelines)
+ state.Apply(skeleton);
+ else
+ state.ApplyEventTimelinesOnly(skeleton);
if (_UpdateLocal != null)
_UpdateLocal(this);
diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs
index 429266bf1..457ba3124 100644
--- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs
+++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs
@@ -62,7 +62,7 @@ namespace Spine.Unity {
/// Update mode to optionally limit updates to e.g. only apply animations but not update the mesh.
public UpdateMode UpdateMode { get { return updateMode; } set { updateMode = value; } }
- [SerializeField] protected UpdateMode updateMode = UpdateMode.FullUpdate;
+ protected UpdateMode updateMode = UpdateMode.FullUpdate;
/// Update mode used when the MeshRenderer becomes invisible
/// (when OnBecameInvisible() is called). Update mode is automatically
@@ -263,7 +263,10 @@ namespace Spine.Unity {
if (BeforeApply != null)
BeforeApply(this);
- state.Apply(skeleton);
+ if (updateMode != UpdateMode.OnlyEventTimelines)
+ state.Apply(skeleton);
+ else
+ state.ApplyEventTimelinesOnly(skeleton);
if (UpdateLocal != null)
UpdateLocal(this);
@@ -283,7 +286,7 @@ namespace Spine.Unity {
// instantiation can happen from Update() after this component, leading to a missing Update() call.
if (!wasUpdatedAfterInit) Update(0);
if (freeze) return;
- if (updateMode <= UpdateMode.EverythingExceptMesh) return;
+ if (updateMode != UpdateMode.FullUpdate) return;
UpdateMesh();
}
diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs
index 519bf334f..2fea2f7d4 100644
--- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs
+++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs
@@ -82,7 +82,7 @@ namespace Spine.Unity {
/// Update mode to optionally limit updates to e.g. only apply animations but not update the mesh.
public UpdateMode UpdateMode { get { return updateMode; } set { updateMode = value; } }
- [SerializeField] protected UpdateMode updateMode = UpdateMode.FullUpdate;
+ protected UpdateMode updateMode = UpdateMode.FullUpdate;
/// Update mode used when the MeshRenderer becomes invisible
/// (when OnBecameInvisible() is called). Update mode is automatically
@@ -381,7 +381,7 @@ namespace Spine.Unity {
}
#endif
- if (updateMode <= UpdateMode.EverythingExceptMesh) return;
+ if (updateMode != UpdateMode.FullUpdate) return;
#if SPINE_OPTIONAL_RENDEROVERRIDE
bool doMeshOverride = generateMeshOverride != null;
diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/ISkeletonAnimation.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/ISkeletonAnimation.cs
index 36e300ec9..90486e381 100644
--- a/spine-unity/Assets/Spine/Runtime/spine-unity/ISkeletonAnimation.cs
+++ b/spine-unity/Assets/Spine/Runtime/spine-unity/ISkeletonAnimation.cs
@@ -31,8 +31,10 @@ namespace Spine.Unity {
public enum UpdateMode {
Nothing = 0,
OnlyAnimationStatus,
- EverythingExceptMesh,
- FullUpdate
+ OnlyEventTimelines = 4, // added as index 4 to keep scene behavior unchanged.
+ EverythingExceptMesh = 2,
+ FullUpdate,
+ //Reserved 4 for OnlyEventTimelines
};
public delegate void UpdateBonesDelegate (ISkeletonAnimation animated);