Here's the code i modified from the SkeletonRootMotion which provided by Mitch, but it's not working!
I figure out the Time and EndTime under Timelines are always be the same, isit a bug?
using UnityEngine;
using System.Collections;
using Spine;
using Spine.Unity;
[RequireComponent(typeof(SkeletonAnimation))]
public class SkeletonRootMotion : MonoBehaviour {
SkeletonAnimation skeletonAnimation;
int rootBoneIndex = -1;
public AnimationCurve rootMotionCurve;
void OnEnable() {
if (skeletonAnimation == null)
skeletonAnimation = GetComponent<SkeletonAnimation>();
skeletonAnimation.UpdateLocal += ApplyRootMotion;
skeletonAnimation.UpdateWorld += UpdateBones;
}
void OnDisable() {
skeletonAnimation.UpdateLocal -= ApplyRootMotion;
skeletonAnimation.UpdateWorld -= UpdateBones;
}
void Start() {
rootBoneIndex = skeletonAnimation.skeleton.FindBoneIndex(skeletonAnimation.skeleton.RootBone.Data.Name);
skeletonAnimation.state.Start += HandleStart;
}
void HandleStart(Spine.AnimationState state, int trackIndex) {
//must use first track for now
if (trackIndex != 0)
return;
rootMotionCurve = null;
Spine.Animation anim = state.GetCurrent(trackIndex).Animation;
//find the root bone's translate curve
foreach (Timeline t in anim.Timelines) {
if (t.GetType() != typeof(TranslateTimeline))
continue;
TranslateTimeline tt = (TranslateTimeline)t;
if (tt.boneIndex == rootBoneIndex) {
//sample the root curve's X value
//TODO: cache this data? Maybe implement RootMotionTimeline instead and keep it in SkeletonData
rootMotionCurve = new AnimationCurve();
float time = 0;
float increment = 1f / 30f;
int frameCount = Mathf.FloorToInt(anim.Duration / increment);
for (int i = 0; i <= frameCount; i++) {
float x = GetXAtTime(tt, time);
rootMotionCurve.AddKey(time, x);
time += increment;
}
break;
}
}
}
//borrowed from TranslateTimeline.Apply method
float GetXAtTime(TranslateTimeline timeline, float time) {
float[] frames = timeline.frames;
if (time < frames[0]) return frames[1]; // Time is before first frame.
Bone bone = skeletonAnimation.skeleton.RootBone;
if (time >= frames[frames.Length - 3]) { // Time is after last frame.
return (bone.data.x + frames[frames.Length - 2] - bone.x);
}
// Interpolate between the last frame and the current frame.
int frameIndex = Spine.Animation.binarySearch(frames, time, 3);
float lastFrameX = frames[frameIndex - 2];
float frameTime = frames[frameIndex];
float percent = 1 - (time - frameTime) / (frames[frameIndex + -3] - frameTime);
percent = timeline.GetCurvePercent(frameIndex / 3 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent));
return (bone.data.x + lastFrameX + (frames[frameIndex + 1] - lastFrameX) * percent - bone.x);
}
void ApplyRootMotion(ISkeletonAnimation skelAnim) {
if (rootMotionCurve == null)
return;
var tmpSkelAnim = skelAnim as SkeletonAnimation;
TrackEntry t = tmpSkelAnim.state.GetCurrent(0);
if (t == null)
return;
int loopCount = (int)(t.Time / t.EndTime);
int lastLoopCount = (int)(t.LastTime / t.EndTime);
//disregard the unwanted
if (lastLoopCount < 0) lastLoopCount = 0;
print( t.Time );
print( t.LastTime );
//They're always the same value!!!
float currentTime = t.Time - (t.EndTime * loopCount);
float lastTime = t.LastTime - (t.EndTime * lastLoopCount);
float delta = 0;
float a = rootMotionCurve.Evaluate(lastTime);
float b = rootMotionCurve.Evaluate(currentTime);
//detect if loop occurred and offset
if (loopCount > lastLoopCount) {
float e = rootMotionCurve.Evaluate(t.EndTime);
float s = rootMotionCurve.Evaluate(0);
delta = (e - a) + (b - s);
}
else {
delta = b - a;
}
if (tmpSkelAnim.skeleton.FlipX)
delta *= -1;
//TODO: implement Rigidbody2D and Rigidbody hooks here
transform.Translate(delta, 0, 0);
}
void UpdateBones(ISkeletonAnimation skelAnim) {
//reset the root bone's x component to stick to the origin
var tmpSkelAnim = skelAnim as SkeletonAnimation;
tmpSkelAnim.skeleton.RootBone.X = 0;
}
}