[unity] Fixed RootMotion scripts in combination with Rigidbody components. Now works smoothly. Closes #1880.

This commit is contained in:
Harald Csaszar 2021-04-21 20:27:52 +02:00
parent 692eed3a75
commit e425d64d2c

View File

@ -48,7 +48,9 @@ namespace Spine.Unity {
public float rootMotionScaleX = 1; public float rootMotionScaleX = 1;
public float rootMotionScaleY = 1; public float rootMotionScaleY = 1;
/// <summary>Skeleton space X translation per skeleton space Y translation root motion.</summary>
public float rootMotionTranslateXPerY = 0; public float rootMotionTranslateXPerY = 0;
/// <summary>Skeleton space Y translation per skeleton space X translation root motion.</summary>
public float rootMotionTranslateYPerX = 0; public float rootMotionTranslateYPerX = 0;
[Header("Optional")] [Header("Optional")]
@ -65,6 +67,7 @@ namespace Spine.Unity {
protected int rootMotionBoneIndex; protected int rootMotionBoneIndex;
protected List<Bone> topLevelBones = new List<Bone>(); protected List<Bone> topLevelBones = new List<Bone>();
protected Vector2 initialOffset = Vector2.zero; protected Vector2 initialOffset = Vector2.zero;
protected Vector2 tempSkeletonDisplacement;
protected Vector2 rigidbodyDisplacement; protected Vector2 rigidbodyDisplacement;
protected virtual void Reset () { protected virtual void Reset () {
@ -97,11 +100,16 @@ namespace Spine.Unity {
rigidBody.MovePosition(transform.position rigidBody.MovePosition(transform.position
+ new Vector3(rigidbodyDisplacement.x, rigidbodyDisplacement.y, 0)); + new Vector3(rigidbodyDisplacement.x, rigidbodyDisplacement.y, 0));
} }
Vector2 parentBoneScale;
GetScaleAffectingRootMotion(out parentBoneScale);
ClearEffectiveBoneOffsets(parentBoneScale);
rigidbodyDisplacement = Vector2.zero; rigidbodyDisplacement = Vector2.zero;
tempSkeletonDisplacement = Vector2.zero;
} }
protected virtual void OnDisable () { protected virtual void OnDisable () {
rigidbodyDisplacement = Vector2.zero; rigidbodyDisplacement = Vector2.zero;
tempSkeletonDisplacement = Vector2.zero;
} }
protected void FindRigidbodyComponent () { protected void FindRigidbodyComponent () {
@ -147,26 +155,27 @@ namespace Spine.Unity {
float minX = 0, float maxX = float.MaxValue, float minY = 0, float maxY = float.MaxValue, float minX = 0, float maxX = float.MaxValue, float minY = 0, float maxY = float.MaxValue,
bool allowXTranslation = false, bool allowYTranslation = false) { bool allowXTranslation = false, bool allowYTranslation = false) {
distanceToTarget = (Vector2)transform.InverseTransformVector(distanceToTarget); Vector2 distanceToTargetSkeletonSpace = (Vector2)transform.InverseTransformVector(distanceToTarget);
Vector2 scaleAffectingRootMotion = GetScaleAffectingRootMotion(); Vector2 scaleAffectingRootMotion = GetScaleAffectingRootMotion();
distanceToTarget.Scale(new Vector2(1f / scaleAffectingRootMotion.x, 1f / scaleAffectingRootMotion.y)); if (UsesRigidbody)
distanceToTargetSkeletonSpace -= tempSkeletonDisplacement;
Vector2 remainingRootMotion = GetRemainingRootMotion(trackIndex); Vector2 remainingRootMotionSkeletonSpace = GetRemainingRootMotion(trackIndex);
if (remainingRootMotion.x == 0) remainingRootMotionSkeletonSpace.Scale(scaleAffectingRootMotion);
remainingRootMotion.x = 0.0001f; if (remainingRootMotionSkeletonSpace.x == 0)
if (remainingRootMotion.y == 0) remainingRootMotionSkeletonSpace.x = 0.0001f;
remainingRootMotion.y = 0.0001f; if (remainingRootMotionSkeletonSpace.y == 0)
remainingRootMotionSkeletonSpace.y = 0.0001f;
if (allowXTranslation)
rootMotionTranslateXPerY = (distanceToTarget.x - remainingRootMotion.x) / Math.Abs(remainingRootMotion.y);
if (allowYTranslation)
rootMotionTranslateYPerX = (distanceToTarget.y - remainingRootMotion.y) / Math.Abs(remainingRootMotion.x);
if (adjustX) if (adjustX)
rootMotionScaleX = Math.Min(maxX, Math.Max(minX, distanceToTarget.x / remainingRootMotion.x)); rootMotionScaleX = Math.Min(maxX, Math.Max(minX, distanceToTargetSkeletonSpace.x / remainingRootMotionSkeletonSpace.x));
if (adjustY) if (adjustY)
rootMotionScaleY = Math.Min(maxY, Math.Max(minY, distanceToTarget.y / remainingRootMotion.y)); rootMotionScaleY = Math.Min(maxY, Math.Max(minY, distanceToTargetSkeletonSpace.y / remainingRootMotionSkeletonSpace.y));
if (allowXTranslation)
rootMotionTranslateXPerY = (distanceToTargetSkeletonSpace.x - remainingRootMotionSkeletonSpace.x * rootMotionScaleX) / remainingRootMotionSkeletonSpace.y;
if (allowYTranslation)
rootMotionTranslateYPerX = (distanceToTargetSkeletonSpace.y - remainingRootMotionSkeletonSpace.y * rootMotionScaleY) / remainingRootMotionSkeletonSpace.x;
} }
public Vector2 GetAnimationRootMotion (Animation animation) { public Vector2 GetAnimationRootMotion (Animation animation) {
@ -225,10 +234,27 @@ namespace Spine.Unity {
if (!this.isActiveAndEnabled) if (!this.isActiveAndEnabled)
return; // Root motion is only applied when component is enabled. return; // Root motion is only applied when component is enabled.
var movementDelta = CalculateAnimationsMovementDelta(); var boneLocalDelta = CalculateAnimationsMovementDelta();
Vector2 parentBoneScale; Vector2 parentBoneScale;
AdjustMovementDeltaToConfiguration(ref movementDelta, out parentBoneScale, animatedSkeletonComponent.Skeleton); Vector2 skeletonDelta = GetSkeletonSpaceMovementDelta(boneLocalDelta, out parentBoneScale);
ApplyRootMotion(movementDelta, parentBoneScale); ApplyRootMotion(skeletonDelta, parentBoneScale);
}
void ApplyRootMotion (Vector2 skeletonDelta, Vector2 parentBoneScale) {
// Apply root motion to Transform or RigidBody;
if (UsesRigidbody) {
rigidbodyDisplacement += (Vector2)transform.TransformVector(skeletonDelta);
// 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);
}
else {
transform.position += transform.TransformVector(skeletonDelta);
ClearEffectiveBoneOffsets(parentBoneScale);
}
} }
Vector2 GetScaleAffectingRootMotion () { Vector2 GetScaleAffectingRootMotion () {
@ -253,44 +279,44 @@ namespace Spine.Unity {
return totalScale; return totalScale;
} }
void AdjustMovementDeltaToConfiguration (ref Vector2 localDelta, out Vector2 parentBoneScale, Skeleton skeleton) { Vector2 GetSkeletonSpaceMovementDelta (Vector2 boneLocalDelta, out Vector2 parentBoneScale) {
Vector2 skeletonDelta = boneLocalDelta;
Vector2 totalScale = GetScaleAffectingRootMotion(out parentBoneScale); Vector2 totalScale = GetScaleAffectingRootMotion(out parentBoneScale);
localDelta.Scale(totalScale); skeletonDelta.Scale(totalScale);
Vector2 rootMotionTranslation = new Vector2( Vector2 rootMotionTranslation = new Vector2(
rootMotionTranslateXPerY * Math.Abs(localDelta.y), rootMotionTranslateXPerY * skeletonDelta.y,
rootMotionTranslateYPerX * Math.Abs(localDelta.x)); rootMotionTranslateYPerX * skeletonDelta.x);
localDelta.x *= rootMotionScaleX; skeletonDelta.x *= rootMotionScaleX;
localDelta.y *= rootMotionScaleY; skeletonDelta.y *= rootMotionScaleY;
localDelta.x += rootMotionTranslation.x; skeletonDelta.x += rootMotionTranslation.x;
localDelta.y += rootMotionTranslation.y; skeletonDelta.y += rootMotionTranslation.y;
if (!transformPositionX) localDelta.x = 0f; if (!transformPositionX) skeletonDelta.x = 0f;
if (!transformPositionY) localDelta.y = 0f; if (!transformPositionY) skeletonDelta.y = 0f;
return skeletonDelta;
} }
void ApplyRootMotion (Vector2 localDelta, Vector2 parentBoneScale) { void SetEffectiveBoneOffsetsTo (Vector2 displacementSkeletonSpace, Vector2 parentBoneScale) {
// Apply root motion to Transform or RigidBody;
if (UsesRigidbody) {
rigidbodyDisplacement += (Vector2)transform.TransformVector(localDelta);
// Accumulated displacement is applied on the next Physics update (FixedUpdate)
}
else {
transform.position += transform.TransformVector(localDelta);
}
// Move top level bones in opposite direction of the root motion bone // Move top level bones in opposite direction of the root motion bone
var skeleton = skeletonComponent.Skeleton;
foreach (var topLevelBone in topLevelBones) { foreach (var topLevelBone in topLevelBones) {
if (topLevelBone == rootMotionBone) { if (topLevelBone == rootMotionBone) {
if (transformPositionX) topLevelBone.x = 0; if (transformPositionX) topLevelBone.x = displacementSkeletonSpace.x / skeleton.ScaleX;
if (transformPositionY) topLevelBone.y = 0; if (transformPositionY) topLevelBone.y = displacementSkeletonSpace.y / skeleton.ScaleY;
} }
else { else {
if (transformPositionX) topLevelBone.x = -(rootMotionBone.x - initialOffset.x) * parentBoneScale.x; float offsetX = (initialOffset.x - rootMotionBone.x) * parentBoneScale.x;
if (transformPositionY) topLevelBone.y = -(rootMotionBone.y - initialOffset.y) * parentBoneScale.y; float offsetY = (initialOffset.y - rootMotionBone.y) * parentBoneScale.y;
if (transformPositionX) topLevelBone.x = (displacementSkeletonSpace.x / skeleton.ScaleX) + offsetX;
if (transformPositionY) topLevelBone.y = (displacementSkeletonSpace.y / skeleton.ScaleY) + offsetY;
} }
} }
} }
void ClearEffectiveBoneOffsets (Vector2 parentBoneScale) {
SetEffectiveBoneOffsetsTo(Vector2.zero, parentBoneScale);
}
} }
} }