mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-04 22:34:53 +08:00
[unity] SkeletonRootMotion components now support rotation root motion.
This commit is contained in:
parent
94c42ab33a
commit
3688bdb25e
1255
CHANGELOG.md.orig
Normal file
1255
CHANGELOG.md.orig
Normal file
File diff suppressed because it is too large
Load Diff
@ -37,6 +37,7 @@ namespace Spine.Unity.Editor {
|
||||
protected SerializedProperty rootMotionBoneName;
|
||||
protected SerializedProperty transformPositionX;
|
||||
protected SerializedProperty transformPositionY;
|
||||
protected SerializedProperty transformRotation;
|
||||
protected SerializedProperty rootMotionScaleX;
|
||||
protected SerializedProperty rootMotionScaleY;
|
||||
protected SerializedProperty rootMotionTranslateXPerY;
|
||||
@ -48,6 +49,7 @@ namespace Spine.Unity.Editor {
|
||||
protected GUIContent rootMotionBoneNameLabel;
|
||||
protected GUIContent transformPositionXLabel;
|
||||
protected GUIContent transformPositionYLabel;
|
||||
protected GUIContent transformRotationLabel;
|
||||
protected GUIContent rootMotionScaleXLabel;
|
||||
protected GUIContent rootMotionScaleYLabel;
|
||||
protected GUIContent rootMotionTranslateXPerYLabel;
|
||||
@ -61,6 +63,7 @@ namespace Spine.Unity.Editor {
|
||||
rootMotionBoneName = serializedObject.FindProperty("rootMotionBoneName");
|
||||
transformPositionX = serializedObject.FindProperty("transformPositionX");
|
||||
transformPositionY = serializedObject.FindProperty("transformPositionY");
|
||||
transformRotation = serializedObject.FindProperty("transformRotation");
|
||||
rootMotionScaleX = serializedObject.FindProperty("rootMotionScaleX");
|
||||
rootMotionScaleY = serializedObject.FindProperty("rootMotionScaleY");
|
||||
rootMotionTranslateXPerY = serializedObject.FindProperty("rootMotionTranslateXPerY");
|
||||
@ -72,6 +75,7 @@ namespace Spine.Unity.Editor {
|
||||
rootMotionBoneNameLabel = new UnityEngine.GUIContent("Root Motion Bone", "The bone to take the motion from.");
|
||||
transformPositionXLabel = new UnityEngine.GUIContent("X", "Root transform position (X)");
|
||||
transformPositionYLabel = new UnityEngine.GUIContent("Y", "Use the Y-movement of the bone.");
|
||||
transformRotationLabel = new UnityEngine.GUIContent("Rotation", "Use the rotation of the bone.");
|
||||
rootMotionScaleXLabel = new UnityEngine.GUIContent("Root Motion Scale (X)", "Scale applied to the horizontal root motion delta. Can be used for delta compensation to e.g. stretch a jump to the desired distance.");
|
||||
rootMotionScaleYLabel = new UnityEngine.GUIContent("Root Motion Scale (Y)", "Scale applied to the vertical root motion delta. Can be used for delta compensation to e.g. stretch a jump to the desired distance.");
|
||||
rootMotionTranslateXPerYLabel = new UnityEngine.GUIContent("Root Motion Translate (X)", "Added X translation per root motion Y delta. Can be used for delta compensation when scaling is not enough, to e.g. offset a horizontal jump to a vertically different goal.");
|
||||
@ -102,6 +106,7 @@ namespace Spine.Unity.Editor {
|
||||
EditorGUILayout.PropertyField(rootMotionBoneName, rootMotionBoneNameLabel);
|
||||
EditorGUILayout.PropertyField(transformPositionX, transformPositionXLabel);
|
||||
EditorGUILayout.PropertyField(transformPositionY, transformPositionYLabel);
|
||||
EditorGUILayout.PropertyField(transformRotation, transformRotationLabel);
|
||||
|
||||
EditorGUILayout.PropertyField(rootMotionScaleX, rootMotionScaleXLabel);
|
||||
EditorGUILayout.PropertyField(rootMotionScaleY, rootMotionScaleYLabel);
|
||||
|
||||
@ -53,6 +53,7 @@ namespace Spine.Unity {
|
||||
#endregion
|
||||
|
||||
protected Vector2 movementDelta;
|
||||
protected float rotationDelta;
|
||||
|
||||
SkeletonMecanim skeletonMecanim;
|
||||
public SkeletonMecanim SkeletonMecanim {
|
||||
@ -107,14 +108,29 @@ namespace Spine.Unity {
|
||||
} else {
|
||||
movementDelta -= weight * GetAnimationRootMotion(time, lastTime, animation);
|
||||
}
|
||||
if (transformRotation) {
|
||||
if (!playsBackward) {
|
||||
rotationDelta += weight * GetAnimationRootMotionRotation(lastTime, time, animation);
|
||||
} else {
|
||||
rotationDelta -= weight * GetAnimationRootMotionRotation(time, lastTime, animation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override Vector2 CalculateAnimationsMovementDelta () {
|
||||
// Note: movement delta is not gather after animation but
|
||||
// Note: movement delta is not gathered after animation but
|
||||
// in OnClipApplied after every applied animation.
|
||||
Vector2 result = movementDelta;
|
||||
movementDelta = Vector2.zero;
|
||||
return result;
|
||||
}
|
||||
|
||||
protected override float CalculateAnimationsRotationDelta () {
|
||||
// Note: movement delta is not gathered after animation but
|
||||
// in OnClipApplied after every applied animation.
|
||||
float result = rotationDelta;
|
||||
rotationDelta = 0;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -128,8 +128,50 @@ namespace Spine.Unity {
|
||||
return localDelta;
|
||||
}
|
||||
|
||||
protected override float CalculateAnimationsRotationDelta () {
|
||||
float localDelta = 0;
|
||||
int trackCount = animationState.Tracks.Count;
|
||||
|
||||
for (int trackIndex = 0; trackIndex < trackCount; ++trackIndex) {
|
||||
// note: animationTrackFlags != -1 below covers trackIndex >= 32,
|
||||
// with -1 corresponding to entry "everything" of the dropdown list.
|
||||
if (animationTrackFlags != -1 && (animationTrackFlags & 1 << trackIndex) == 0)
|
||||
continue;
|
||||
|
||||
TrackEntry track = animationState.GetCurrent(trackIndex);
|
||||
TrackEntry next = null;
|
||||
while (track != null) {
|
||||
var animation = track.Animation;
|
||||
float start = track.AnimationLast;
|
||||
float end = track.AnimationTime;
|
||||
var currentDelta = GetAnimationRootMotionRotation(start, end, animation);
|
||||
if (currentDelta != 0) {
|
||||
ApplyMixAlphaToDelta(ref currentDelta, next, track);
|
||||
localDelta += currentDelta;
|
||||
}
|
||||
|
||||
// Traverse mixingFrom chain.
|
||||
next = track;
|
||||
track = track.MixingFrom;
|
||||
}
|
||||
}
|
||||
return localDelta;
|
||||
}
|
||||
|
||||
void ApplyMixAlphaToDelta (ref Vector2 currentDelta, TrackEntry next, TrackEntry track) {
|
||||
// Apply mix alpha to the delta position (based on AnimationState.cs).
|
||||
float mixAlpha = 1;
|
||||
GetMixAlpha(ref mixAlpha, next, track);
|
||||
currentDelta *= mixAlpha;
|
||||
}
|
||||
|
||||
void ApplyMixAlphaToDelta (ref float currentDelta, TrackEntry next, TrackEntry track) {
|
||||
float mixAlpha = 1;
|
||||
GetMixAlpha(ref mixAlpha, next, track);
|
||||
currentDelta *= mixAlpha;
|
||||
}
|
||||
|
||||
void GetMixAlpha (ref float cumulatedMixAlpha, TrackEntry next, TrackEntry track) {
|
||||
// code below based on AnimationState.cs
|
||||
float mix;
|
||||
if (next != null) {
|
||||
if (next.MixDuration == 0) { // Single frame mix to undo mixingFrom changes.
|
||||
@ -139,7 +181,7 @@ namespace Spine.Unity {
|
||||
if (mix > 1) mix = 1;
|
||||
}
|
||||
float mixAndAlpha = track.Alpha * next.InterruptAlpha * (1 - mix);
|
||||
currentDelta *= mixAndAlpha;
|
||||
cumulatedMixAlpha *= mixAndAlpha;
|
||||
} else {
|
||||
if (track.MixDuration == 0) {
|
||||
mix = 1;
|
||||
@ -147,7 +189,7 @@ namespace Spine.Unity {
|
||||
mix = track.Alpha * (track.MixTime / track.MixDuration);
|
||||
if (mix > 1) mix = 1;
|
||||
}
|
||||
currentDelta *= mix;
|
||||
cumulatedMixAlpha *= mix;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,6 +45,7 @@ namespace Spine.Unity {
|
||||
protected string rootMotionBoneName = "root";
|
||||
public bool transformPositionX = true;
|
||||
public bool transformPositionY = true;
|
||||
public bool transformRotation = false;
|
||||
|
||||
public float rootMotionScaleX = 1;
|
||||
public float rootMotionScaleY = 1;
|
||||
@ -67,7 +68,14 @@ namespace Spine.Unity {
|
||||
/// Returns <c>Vector2.zero</c> when <c>rigidbody</c> and <c>rigidbody2D</c> are null.
|
||||
/// This can be necessary when multiple scripts call <c>Rigidbody2D.MovePosition</c>,
|
||||
/// where the last call overwrites the effect of preceding ones.</summary>
|
||||
public Vector2 PreviousRigidbodyRootMotion {
|
||||
public Vector2 PreviousRigidbodyRootMotion2D {
|
||||
get { return new Vector2(previousRigidbodyRootMotion.x, previousRigidbodyRootMotion.y); }
|
||||
}
|
||||
|
||||
/// <summary>Root motion translation that has been applied in the preceding <c>FixedUpdate</c> call
|
||||
/// if a rigidbody is assigned at either <c>rigidbody</c> or <c>rigidbody2D</c>.
|
||||
/// Returns <c>Vector3.zero</c> when <c>rigidbody</c> and <c>rigidbody2D</c> are null.</summary>
|
||||
public Vector3 PreviousRigidbodyRootMotion3D {
|
||||
get { return previousRigidbodyRootMotion; }
|
||||
}
|
||||
|
||||
@ -86,13 +94,19 @@ namespace Spine.Unity {
|
||||
protected int rootMotionBoneIndex;
|
||||
protected List<int> transformConstraintIndices = new List<int>();
|
||||
protected List<Vector2> transformConstraintLastPos = new List<Vector2>();
|
||||
protected List<float> transformConstraintLastRotation = new List<float>();
|
||||
protected List<Bone> topLevelBones = new List<Bone>();
|
||||
protected Vector2 initialOffset = Vector2.zero;
|
||||
protected Vector2 tempSkeletonDisplacement;
|
||||
protected Vector2 rigidbodyDisplacement;
|
||||
protected Vector2 previousRigidbodyRootMotion = Vector2.zero;
|
||||
protected Vector3 rigidbodyDisplacement;
|
||||
protected Vector3 previousRigidbodyRootMotion = Vector2.zero;
|
||||
protected Vector2 additionalRigidbody2DMovement = Vector2.zero;
|
||||
|
||||
protected Quaternion rigidbodyRotation = Quaternion.identity;
|
||||
protected float rigidbody2DRotation;
|
||||
protected float initialOffsetRotation;
|
||||
protected float tempSkeletonRotation;
|
||||
|
||||
protected virtual void Reset () {
|
||||
FindRigidbodyComponent();
|
||||
}
|
||||
@ -101,8 +115,10 @@ namespace Spine.Unity {
|
||||
skeletonComponent = GetComponent<ISkeletonComponent>();
|
||||
GatherTopLevelBones();
|
||||
SetRootMotionBone(rootMotionBoneName);
|
||||
if (rootMotionBone != null)
|
||||
if (rootMotionBone != null) {
|
||||
initialOffset = new Vector2(rootMotionBone.X, rootMotionBone.Y);
|
||||
initialOffsetRotation = rootMotionBone.Rotation;
|
||||
}
|
||||
|
||||
var skeletonAnimation = skeletonComponent as ISkeletonAnimation;
|
||||
if (skeletonAnimation != null) {
|
||||
@ -126,24 +142,26 @@ namespace Spine.Unity {
|
||||
rigidBody2D.velocity * deltaTime;
|
||||
}
|
||||
|
||||
Vector2 rigidbodyDisplacement2D = new Vector2(rigidbodyDisplacement.x, rigidbodyDisplacement.y);
|
||||
rigidBody2D.MovePosition(gravityAndVelocityMovement + new Vector2(transform.position.x, transform.position.y)
|
||||
+ rigidbodyDisplacement + additionalRigidbody2DMovement);
|
||||
+ rigidbodyDisplacement2D + additionalRigidbody2DMovement);
|
||||
rigidBody2D.MoveRotation(rigidbody2DRotation + rigidBody2D.rotation);
|
||||
} else if (rigidBody != null) {
|
||||
rigidBody.MovePosition(transform.position
|
||||
+ new Vector3(rigidbodyDisplacement.x, rigidbodyDisplacement.y, 0));
|
||||
rigidBody.MoveRotation(rigidBody.rotation * rigidbodyRotation);
|
||||
} else return;
|
||||
|
||||
Vector2 parentBoneScale;
|
||||
GetScaleAffectingRootMotion(out parentBoneScale);
|
||||
ClearEffectiveBoneOffsets(parentBoneScale);
|
||||
previousRigidbodyRootMotion = rigidbodyDisplacement;
|
||||
rigidbodyDisplacement = Vector2.zero;
|
||||
tempSkeletonDisplacement = Vector2.zero;
|
||||
|
||||
ClearRigidbodyTempMovement();
|
||||
}
|
||||
|
||||
protected virtual void OnDisable () {
|
||||
rigidbodyDisplacement = Vector2.zero;
|
||||
tempSkeletonDisplacement = Vector2.zero;
|
||||
ClearRigidbodyTempMovement();
|
||||
}
|
||||
|
||||
protected void FindRigidbodyComponent () {
|
||||
@ -160,6 +178,7 @@ namespace Spine.Unity {
|
||||
|
||||
protected virtual float AdditionalScale { get { return 1.0f; } }
|
||||
abstract protected Vector2 CalculateAnimationsMovementDelta ();
|
||||
protected virtual float CalculateAnimationsRotationDelta () { return 0; }
|
||||
abstract public Vector2 GetRemainingRootMotion (int trackIndex = 0);
|
||||
|
||||
public struct RootMotionInfo {
|
||||
@ -278,6 +297,52 @@ namespace Spine.Unity {
|
||||
return currentDelta;
|
||||
}
|
||||
|
||||
public float GetAnimationRootMotionRotation (Animation animation) {
|
||||
return GetAnimationRootMotionRotation(0, animation.Duration, animation);
|
||||
}
|
||||
|
||||
public float GetAnimationRootMotionRotation (float startTime, float endTime,
|
||||
Animation animation) {
|
||||
|
||||
if (startTime == endTime)
|
||||
return 0;
|
||||
|
||||
RotateTimeline rotateTimeline = animation.FindTimelineForBone<RotateTimeline>(rootMotionBoneIndex);
|
||||
|
||||
// Non-looped base
|
||||
float endRotation = 0;
|
||||
float startRotation = 0;
|
||||
if (rotateTimeline != null) {
|
||||
endRotation = rotateTimeline.Evaluate(endTime);
|
||||
startRotation = rotateTimeline.Evaluate(startTime);
|
||||
}
|
||||
var transformConstraintsItems = skeletonComponent.Skeleton.TransformConstraints.Items;
|
||||
foreach (int constraintIndex in this.transformConstraintIndices) {
|
||||
TransformConstraint constraint = transformConstraintsItems[constraintIndex];
|
||||
ApplyConstraintToRotation(animation, constraint, constraintIndex, endTime, false, ref endRotation);
|
||||
ApplyConstraintToRotation(animation, constraint, constraintIndex, startTime, true, ref startRotation);
|
||||
}
|
||||
float currentDelta = endRotation - startRotation;
|
||||
|
||||
// Looped additions
|
||||
if (startTime > endTime) {
|
||||
float loopRotation = 0;
|
||||
float zeroPos = 0;
|
||||
if (rotateTimeline != null) {
|
||||
loopRotation = rotateTimeline.Evaluate(animation.Duration);
|
||||
zeroPos = rotateTimeline.Evaluate(0);
|
||||
}
|
||||
foreach (int constraintIndex in this.transformConstraintIndices) {
|
||||
TransformConstraint constraint = transformConstraintsItems[constraintIndex];
|
||||
ApplyConstraintToRotation(animation, constraint, constraintIndex, animation.Duration, false, ref loopRotation);
|
||||
ApplyConstraintToRotation(animation, constraint, constraintIndex, 0, false, ref zeroPos);
|
||||
}
|
||||
currentDelta += loopRotation - zeroPos;
|
||||
}
|
||||
UpdateLastConstraintRotation(transformConstraintsItems);
|
||||
return currentDelta;
|
||||
}
|
||||
|
||||
void ApplyConstraintToPos (Animation animation, TransformConstraint constraint,
|
||||
int constraintIndex, float time, bool useLastConstraintPos, ref Vector2 pos) {
|
||||
TransformConstraintTimeline timeline = animation.FindTransformConstraintTimeline(constraintIndex);
|
||||
@ -297,6 +362,23 @@ namespace Spine.Unity {
|
||||
pos.y * invMixXY.y + constraintPos.y * mixXY.y);
|
||||
}
|
||||
|
||||
void ApplyConstraintToRotation (Animation animation, TransformConstraint constraint,
|
||||
int constraintIndex, float time, bool useLastConstraintRotation, ref float rotation) {
|
||||
TransformConstraintTimeline timeline = animation.FindTransformConstraintTimeline(constraintIndex);
|
||||
if (timeline == null)
|
||||
return;
|
||||
float mixRotate = timeline.EvaluateRotateMix(time);
|
||||
float invMixRotate = timeline.EvaluateRotateMix(time);
|
||||
float constraintRotation;
|
||||
if (useLastConstraintRotation)
|
||||
constraintRotation = transformConstraintLastRotation[constraintIndex];
|
||||
else {
|
||||
Bone targetBone = constraint.Target;
|
||||
constraintRotation = targetBone.Rotation;
|
||||
}
|
||||
rotation = rotation * invMixRotate + constraintRotation * mixRotate;
|
||||
}
|
||||
|
||||
void UpdateLastConstraintPos (TransformConstraint[] transformConstraintsItems) {
|
||||
foreach (int constraintIndex in this.transformConstraintIndices) {
|
||||
TransformConstraint constraint = transformConstraintsItems[constraintIndex];
|
||||
@ -305,6 +387,14 @@ namespace Spine.Unity {
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateLastConstraintRotation (TransformConstraint[] transformConstraintsItems) {
|
||||
foreach (int constraintIndex in this.transformConstraintIndices) {
|
||||
TransformConstraint constraint = transformConstraintsItems[constraintIndex];
|
||||
Bone targetBone = constraint.Target;
|
||||
transformConstraintLastRotation[constraintIndex] = targetBone.Rotation;
|
||||
}
|
||||
}
|
||||
|
||||
public RootMotionInfo GetAnimationRootMotionInfo (Animation animation, float currentTime) {
|
||||
RootMotionInfo rootMotion = new RootMotionInfo();
|
||||
float duration = animation.Duration;
|
||||
@ -340,6 +430,7 @@ namespace Spine.Unity {
|
||||
Bone targetBone = constraint.Target;
|
||||
Vector2 constraintPos = new Vector2(targetBone.X, targetBone.Y);
|
||||
transformConstraintLastPos.Add(constraintPos);
|
||||
transformConstraintLastRotation.Add(targetBone.Rotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -375,24 +466,49 @@ namespace Spine.Unity {
|
||||
if (!this.isActiveAndEnabled)
|
||||
return; // Root motion is only applied when component is enabled.
|
||||
|
||||
var boneLocalDelta = CalculateAnimationsMovementDelta();
|
||||
Vector2 boneLocalDelta = CalculateAnimationsMovementDelta();
|
||||
Vector2 parentBoneScale;
|
||||
Vector2 skeletonDelta = GetSkeletonSpaceMovementDelta(boneLocalDelta, out parentBoneScale);
|
||||
ApplyRootMotion(skeletonDelta, parentBoneScale);
|
||||
Vector2 totalScale;
|
||||
Vector2 skeletonTranslationDelta = GetSkeletonSpaceMovementDelta(boneLocalDelta, out parentBoneScale, out totalScale);
|
||||
float skeletonRotationDelta = 0;
|
||||
if (transformRotation) {
|
||||
float boneLocalDeltaRotation = CalculateAnimationsRotationDelta();
|
||||
skeletonRotationDelta = GetSkeletonSpaceRotationDelta(boneLocalDeltaRotation, totalScale);
|
||||
}
|
||||
|
||||
ApplyRootMotion(skeletonTranslationDelta, skeletonRotationDelta, parentBoneScale);
|
||||
}
|
||||
|
||||
void ApplyRootMotion (Vector2 skeletonDelta, Vector2 parentBoneScale) {
|
||||
void ApplyRootMotion (Vector2 skeletonTranslationDelta, float skeletonRotationDelta, Vector2 parentBoneScale) {
|
||||
// Apply root motion to Transform or RigidBody;
|
||||
if (UsesRigidbody) {
|
||||
rigidbodyDisplacement += (Vector2)transform.TransformVector(skeletonDelta);
|
||||
rigidbodyDisplacement += transform.TransformVector(skeletonTranslationDelta);
|
||||
|
||||
// Accumulated displacement is applied on the next Physics update in FixedUpdate.
|
||||
// Until the next Physics update, tempBoneDisplacement is offsetting bone locations
|
||||
// to prevent stutter which would otherwise occur if we don't move every Update.
|
||||
tempSkeletonDisplacement += skeletonDelta;
|
||||
SetEffectiveBoneOffsetsTo(tempSkeletonDisplacement, parentBoneScale);
|
||||
tempSkeletonDisplacement += skeletonTranslationDelta;
|
||||
|
||||
if (skeletonRotationDelta != 0.0f) {
|
||||
if (rigidBody != null) {
|
||||
Quaternion addedWorldRotation = Quaternion.AngleAxis(skeletonRotationDelta, transform.forward);
|
||||
rigidbodyRotation = rigidbodyRotation * addedWorldRotation;
|
||||
} else if (rigidBody2D != null) {
|
||||
Vector3 lossyScale = transform.lossyScale;
|
||||
float rotationSign = lossyScale.x * lossyScale.y > 0 ? 1 : -1;
|
||||
rigidbody2DRotation += rotationSign * skeletonRotationDelta;
|
||||
}
|
||||
tempSkeletonRotation += skeletonRotationDelta;
|
||||
}
|
||||
|
||||
SetEffectiveBoneOffsetsTo(tempSkeletonDisplacement, tempSkeletonRotation, parentBoneScale);
|
||||
} else {
|
||||
transform.position += transform.TransformVector(skeletonDelta);
|
||||
transform.position += transform.TransformVector(skeletonTranslationDelta);
|
||||
if (skeletonRotationDelta != 0.0f) {
|
||||
Vector3 lossyScale = transform.lossyScale;
|
||||
float rotationSign = lossyScale.x * lossyScale.y > 0 ? 1 : -1;
|
||||
transform.Rotate(0, 0, rotationSign * skeletonRotationDelta);
|
||||
}
|
||||
ClearEffectiveBoneOffsets(parentBoneScale);
|
||||
}
|
||||
}
|
||||
@ -400,11 +516,12 @@ namespace Spine.Unity {
|
||||
void ApplyTransformConstraints () {
|
||||
rootMotionBone.AX = rootMotionBone.X;
|
||||
rootMotionBone.AY = rootMotionBone.Y;
|
||||
rootMotionBone.AppliedRotation = rootMotionBone.Rotation;
|
||||
var transformConstraintsItems = skeletonComponent.Skeleton.TransformConstraints.Items;
|
||||
foreach (int constraintIndex in this.transformConstraintIndices) {
|
||||
TransformConstraint constraint = transformConstraintsItems[constraintIndex];
|
||||
// apply the constraint and sets Bone.ax and Bone.ay values.
|
||||
/// Update is based on Bone.x and Bone.y, so skeleton.UpdateWorldTransform()
|
||||
// apply the constraint and sets Bone.ax, Bone.ay and Bone.arotation values.
|
||||
/// Update is based on Bone.x, Bone.y and Bone.rotation, so skeleton.UpdateWorldTransform()
|
||||
/// can be called afterwards without having a different starting point.
|
||||
constraint.Update();
|
||||
}
|
||||
@ -432,9 +549,9 @@ namespace Spine.Unity {
|
||||
return totalScale;
|
||||
}
|
||||
|
||||
Vector2 GetSkeletonSpaceMovementDelta (Vector2 boneLocalDelta, out Vector2 parentBoneScale) {
|
||||
Vector2 GetSkeletonSpaceMovementDelta (Vector2 boneLocalDelta, out Vector2 parentBoneScale, out Vector2 totalScale) {
|
||||
Vector2 skeletonDelta = boneLocalDelta;
|
||||
Vector2 totalScale = GetScaleAffectingRootMotion(out parentBoneScale);
|
||||
totalScale = GetScaleAffectingRootMotion(out parentBoneScale);
|
||||
skeletonDelta.Scale(totalScale);
|
||||
|
||||
Vector2 rootMotionTranslation = new Vector2(
|
||||
@ -451,7 +568,12 @@ namespace Spine.Unity {
|
||||
return skeletonDelta;
|
||||
}
|
||||
|
||||
void SetEffectiveBoneOffsetsTo (Vector2 displacementSkeletonSpace, Vector2 parentBoneScale) {
|
||||
float GetSkeletonSpaceRotationDelta (float boneLocalDelta, Vector2 totalScaleAffectingRootMotion) {
|
||||
float rotationSign = totalScaleAffectingRootMotion.x * totalScaleAffectingRootMotion.y > 0 ? 1 : -1;
|
||||
return rotationSign * boneLocalDelta;
|
||||
}
|
||||
|
||||
void SetEffectiveBoneOffsetsTo (Vector2 displacementSkeletonSpace, float rotationSkeletonSpace, Vector2 parentBoneScale) {
|
||||
|
||||
ApplyTransformConstraints();
|
||||
|
||||
@ -461,22 +583,44 @@ namespace Spine.Unity {
|
||||
if (topLevelBone == rootMotionBone) {
|
||||
if (transformPositionX) topLevelBone.X = displacementSkeletonSpace.x / skeleton.ScaleX;
|
||||
if (transformPositionY) topLevelBone.Y = displacementSkeletonSpace.y / skeleton.ScaleY;
|
||||
if (transformRotation) {
|
||||
float rotationSign = skeleton.ScaleX * skeleton.ScaleY > 0 ? 1 : -1;
|
||||
topLevelBone.Rotation = rotationSign * rotationSkeletonSpace;
|
||||
}
|
||||
} else {
|
||||
bool useAppliedPosition = transformConstraintIndices.Count > 0;
|
||||
float rootMotionBoneX = useAppliedPosition ? rootMotionBone.AX : rootMotionBone.X;
|
||||
float rootMotionBoneY = useAppliedPosition ? rootMotionBone.AY : rootMotionBone.Y;
|
||||
bool useAppliedTransform = transformConstraintIndices.Count > 0;
|
||||
float rootMotionBoneX = useAppliedTransform ? rootMotionBone.AX : rootMotionBone.X;
|
||||
float rootMotionBoneY = useAppliedTransform ? rootMotionBone.AY : rootMotionBone.Y;
|
||||
|
||||
float offsetX = (initialOffset.x - rootMotionBoneX) * parentBoneScale.x;
|
||||
float offsetY = (initialOffset.y - rootMotionBoneY) * parentBoneScale.y;
|
||||
|
||||
if (transformPositionX) topLevelBone.X = (displacementSkeletonSpace.x / skeleton.ScaleX) + offsetX;
|
||||
if (transformPositionY) topLevelBone.Y = (displacementSkeletonSpace.y / skeleton.ScaleY) + offsetY;
|
||||
|
||||
if (transformRotation) {
|
||||
float rootMotionBoneRotation = useAppliedTransform ? rootMotionBone.AppliedRotation : rootMotionBone.Rotation;
|
||||
|
||||
float parentBoneRotationSign = (parentBoneScale.x * parentBoneScale.y > 0 ? 1 : -1);
|
||||
float offsetRotation = (initialOffsetRotation - rootMotionBoneRotation) * parentBoneRotationSign;
|
||||
|
||||
float skeletonRotationSign = skeleton.ScaleX * skeleton.ScaleY > 0 ? 1 : -1;
|
||||
topLevelBone.Rotation = (rotationSkeletonSpace * skeletonRotationSign) + offsetRotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClearEffectiveBoneOffsets (Vector2 parentBoneScale) {
|
||||
SetEffectiveBoneOffsetsTo(Vector2.zero, parentBoneScale);
|
||||
SetEffectiveBoneOffsetsTo(Vector2.zero, 0, parentBoneScale);
|
||||
}
|
||||
|
||||
void ClearRigidbodyTempMovement () {
|
||||
rigidbodyDisplacement = Vector2.zero;
|
||||
tempSkeletonDisplacement = Vector2.zero;
|
||||
rigidbodyRotation = Quaternion.identity;
|
||||
rigidbody2DRotation = 0;
|
||||
tempSkeletonRotation = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,6 +73,22 @@ namespace Spine.Unity.AnimationTools {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Evaluates the resulting value of a RotateTimeline at a given time.
|
||||
/// SkeletonData can be accessed from Skeleton.Data or from SkeletonDataAsset.GetSkeletonData.
|
||||
/// If no SkeletonData is given, values are returned as difference to setup pose
|
||||
/// instead of absolute values.</summary>
|
||||
public static float Evaluate (this RotateTimeline timeline, float time, SkeletonData skeletonData = null) {
|
||||
if (time < timeline.Frames[0]) return 0f;
|
||||
|
||||
float rotation = timeline.GetCurveValue(time);
|
||||
if (skeletonData == null) {
|
||||
return rotation;
|
||||
} else {
|
||||
BoneData boneData = skeletonData.Bones.Items[timeline.BoneIndex];
|
||||
return (boneData.Rotation + rotation);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Evaluates the resulting X and Y translate mix values of a
|
||||
/// TransformConstraintTimeline at a given time.</summary>
|
||||
public static Vector2 EvaluateTranslateXYMix (this TransformConstraintTimeline timeline, float time) {
|
||||
@ -83,6 +99,16 @@ namespace Spine.Unity.AnimationTools {
|
||||
return new Vector2(mixX, mixY);
|
||||
}
|
||||
|
||||
/// <summary>Evaluates the resulting rotate mix values of a
|
||||
/// TransformConstraintTimeline at a given time.</summary>
|
||||
public static float EvaluateRotateMix (this TransformConstraintTimeline timeline, float time) {
|
||||
if (time < timeline.Frames[0]) return 0;
|
||||
|
||||
float rotate, mixX, mixY, scaleX, scaleY, shearY;
|
||||
timeline.GetCurveValue(out rotate, out mixX, out mixY, out scaleX, out scaleY, out shearY, time);
|
||||
return rotate;
|
||||
}
|
||||
|
||||
/// <summary>Gets the translate timeline for a given boneIndex.
|
||||
/// You can get the boneIndex using SkeletonData.FindBone().Index.
|
||||
/// The root bone is always boneIndex 0.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user