| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744 |
- /******************************************************************************
- * Spine Runtimes License Agreement
- * Last updated July 28, 2023. Replaces all prior versions.
- *
- * Copyright (c) 2013-2023, Esoteric Software LLC
- *
- * Integration of the Spine Runtimes into software or otherwise creating
- * derivative works of the Spine Runtimes is permitted under the terms and
- * conditions of Section 2 of the Spine Editor License Agreement:
- * http://esotericsoftware.com/spine-editor-license
- *
- * Otherwise, it is permitted to integrate the Spine Runtimes into software or
- * otherwise create derivative works of the Spine Runtimes (collectively,
- * "Products"), provided that each user of the Products must obtain their own
- * Spine Editor license and redistribution of the Products in any form must
- * include this license and copyright notice.
- *
- * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
- * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE
- * SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *****************************************************************************/
- // In order to respect TransformConstraints modifying the scale of parent bones,
- // GetScaleAffectingRootMotion() now uses parentBone.AScaleX and AScaleY instead
- // of previously used ScaleX and ScaleY. If you require the previous behaviour,
- // comment out the define below.
- #define USE_APPLIED_PARENT_SCALE
- using Spine.Unity.AnimationTools;
- using System;
- using System.Collections.Generic;
- using UnityEngine;
- namespace Spine.Unity {
- /// <summary>
- /// Base class for skeleton root motion components.
- /// </summary>
- [DefaultExecutionOrder(1)]
- abstract public class SkeletonRootMotionBase : MonoBehaviour {
- #region Inspector
- [SpineBone]
- public string rootMotionBoneName = "root";
- public bool transformPositionX = true;
- public bool transformPositionY = true;
- public bool transformRotation = false;
- public float rootMotionScaleX = 1;
- public float rootMotionScaleY = 1;
- public float rootMotionScaleRotation = 1;
- /// <summary>Skeleton space X translation per skeleton space Y translation root motion.</summary>
- public float rootMotionTranslateXPerY = 0;
- /// <summary>Skeleton space Y translation per skeleton space X translation root motion.</summary>
- public float rootMotionTranslateYPerX = 0;
- [Header("Optional")]
- public Rigidbody2D rigidBody2D;
- public bool applyRigidbody2DGravity = false;
- public Rigidbody rigidBody;
- /// <summary>Delegate type for customizing application of rootmotion.
- public delegate void RootMotionDelegate (SkeletonRootMotionBase component, Vector2 translation, float rotation);
- /// <summary>This callback can be used to apply root-motion in a custom way. It is raised after evaluating
- /// this animation frame's root-motion, before it is potentially applied (see <see cref="disableOnOverride"/>)
- /// to either Transform or Rigidbody.
- /// When <see cref="SkeletonAnimation.UpdateTiming"/> is set to <see cref="UpdateTiming.InUpdate"/>, multiple
- /// animation frames might take place before <c>FixedUpdate</c> is called once.
- /// The callback parameters <c>translation</c> and <c>rotation</c> are filled out with
- /// this animation frame's skeleton-space root-motion (not cumulated). You can use
- /// e.g. <c>transform.TransformVector()</c> to transform skeleton-space root-motion to world space.
- /// </summary>
- /// <seealso cref="PhysicsUpdateRootMotionOverride"/>
- public event RootMotionDelegate ProcessRootMotionOverride;
- /// <summary>This callback can be used to apply root-motion in a custom way. It is raised in FixedUpdate
- /// after (when <see cref="disableOnOverride"/> is set to false) or instead of when root-motion
- /// would be applied at the Rigidbody.
- /// When <see cref="SkeletonAnimation.UpdateTiming"/> is set to <see cref="UpdateTiming.InUpdate"/>, multiple
- /// animation frames might take place before before <c>FixedUpdate</c> is called once.
- /// The callback parameters <c>translation</c> and <c>rotation</c> are filled out with the
- /// (cumulated) skeleton-space root-motion since the the last <c>FixedUpdate</c> call. You can use
- /// e.g. <c>transform.TransformVector()</c> to transform skeleton-space root-motion to world space.
- /// </summary>
- /// <seealso cref="ProcessRootMotionOverride"/>
- public event RootMotionDelegate PhysicsUpdateRootMotionOverride;
- /// <summary>When true, root-motion is not applied to the Transform or Rigidbody.
- /// Otherwise the delegate callbacks are issued additionally.</summary>
- public bool disableOnOverride = true;
- public Bone RootMotionBone { get { return rootMotionBone; } }
- public bool UsesRigidbody {
- get { return rigidBody != null || rigidBody2D != null; }
- }
- /// <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>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 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; }
- }
- /// <summary>Additional translation to add to <c>Rigidbody2D.MovePosition</c>
- /// called in FixedUpdate. This can be necessary when multiple scripts call
- /// <c>MovePosition</c>, where the last call overwrites the effect of preceding ones.
- /// Has no effect if <c>rigidBody2D</c> is null.</summary>
- public Vector2 AdditionalRigidbody2DMovement {
- get { return additionalRigidbody2DMovement; }
- set { additionalRigidbody2DMovement = value; }
- }
- #endregion
- protected bool SkeletonAnimationUsesFixedUpdate {
- get {
- ISkeletonAnimation skeletonAnimation = skeletonComponent as ISkeletonAnimation;
- if (skeletonAnimation != null) {
- return skeletonAnimation.UpdateTiming == UpdateTiming.InFixedUpdate;
- }
- return false;
- }
- }
- protected ISkeletonComponent skeletonComponent;
- protected Bone rootMotionBone;
- 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 bool accumulatedUntilFixedUpdate = false;
- protected Vector2 tempSkeletonDisplacement;
- protected Vector3 rigidbodyDisplacement;
- protected Vector3 previousRigidbodyRootMotion = Vector2.zero;
- protected Vector2 additionalRigidbody2DMovement = Vector2.zero;
- protected Quaternion rigidbodyLocalRotation = Quaternion.identity;
- protected float rigidbody2DRotation;
- protected float initialOffsetRotation;
- protected float tempSkeletonRotation;
- protected virtual void Reset () {
- FindRigidbodyComponent();
- }
- protected virtual void Start () {
- Initialize();
- }
- protected void InitializeOnRebuild (ISkeletonAnimation animatedSkeletonComponent) {
- Initialize();
- }
- public virtual void Initialize () {
- skeletonComponent = GetComponent<ISkeletonComponent>();
- GatherTopLevelBones();
- SetRootMotionBone(rootMotionBoneName);
- if (rootMotionBone != null) {
- initialOffset = new Vector2(rootMotionBone.X, rootMotionBone.Y);
- initialOffsetRotation = rootMotionBone.Rotation;
- }
- ISkeletonAnimation skeletonAnimation = skeletonComponent as ISkeletonAnimation;
- if (skeletonAnimation != null) {
- skeletonAnimation.UpdateLocal -= HandleUpdateLocal;
- skeletonAnimation.UpdateLocal += HandleUpdateLocal;
- skeletonAnimation.OnAnimationRebuild -= InitializeOnRebuild;
- skeletonAnimation.OnAnimationRebuild += InitializeOnRebuild;
- SkeletonUtility skeletonUtility = GetComponent<SkeletonUtility>();
- if (skeletonUtility != null) {
- // SkeletonUtilityBone shall receive UpdateLocal callbacks for bone-following after root motion
- // clears the root-bone position.
- skeletonUtility.ResubscribeEvents();
- }
- }
- }
- protected virtual void FixedUpdate () {
- // Root motion is only applied when component is enabled.
- if (!this.isActiveAndEnabled)
- return;
- // When SkeletonAnimation component uses UpdateTiming.InFixedUpdate,
- // we directly call PhysicsUpdate in HandleUpdateLocal instead of here.
- if (!SkeletonAnimationUsesFixedUpdate)
- PhysicsUpdate(false);
- }
- protected virtual void PhysicsUpdate (bool skeletonAnimationUsesFixedUpdate) {
- Vector2 callbackDisplacement = tempSkeletonDisplacement;
- float callbackRotation = tempSkeletonRotation;
- bool isApplyAtRigidbodyAllowed = PhysicsUpdateRootMotionOverride == null || !disableOnOverride;
- if (isApplyAtRigidbodyAllowed) {
- if (rigidBody2D != null) {
- Vector2 gravityAndVelocityMovement = Vector2.zero;
- if (applyRigidbody2DGravity) {
- float deltaTime = Time.fixedDeltaTime;
- float deltaTimeSquared = (deltaTime * deltaTime);
- rigidBody2D.velocity += rigidBody2D.gravityScale * Physics2D.gravity * deltaTime;
- gravityAndVelocityMovement = 0.5f * rigidBody2D.gravityScale * Physics2D.gravity * deltaTimeSquared +
- rigidBody2D.velocity * deltaTime;
- }
- Vector2 rigidbodyDisplacement2D = new Vector2(rigidbodyDisplacement.x, rigidbodyDisplacement.y);
- // Note: MovePosition seems to be the only precise and reliable way to set movement delta,
- // for both 2D and 3D rigidbodies.
- // Setting velocity like "rigidBody2D.velocity = movement/deltaTime" works perfectly in mid-air
- // without gravity and ground collision, unfortunately when on the ground, friction causes severe
- // slowdown. Using a zero-friction PhysicsMaterial leads to sliding endlessly along the ground as
- // soon as forces are applied. Additionally, there is no rigidBody2D.isGrounded, requiring our own
- // checks.
- rigidBody2D.MovePosition(gravityAndVelocityMovement + new Vector2(rigidBody2D.position.x, rigidBody2D.position.y)
- + rigidbodyDisplacement2D + additionalRigidbody2DMovement);
- rigidBody2D.MoveRotation(rigidbody2DRotation + rigidBody2D.rotation);
- } else if (rigidBody != null) {
- rigidBody.MovePosition(rigidBody.position
- + new Vector3(rigidbodyDisplacement.x, rigidbodyDisplacement.y, rigidbodyDisplacement.z));
- rigidBody.MoveRotation(rigidBody.rotation * rigidbodyLocalRotation);
- }
- }
- previousRigidbodyRootMotion = rigidbodyDisplacement;
- if (accumulatedUntilFixedUpdate) {
- Vector2 parentBoneScale;
- GetScaleAffectingRootMotion(out parentBoneScale);
- ClearEffectiveBoneOffsets(parentBoneScale);
- skeletonComponent.Skeleton.UpdateWorldTransform(Skeleton.Physics.Pose);
- }
- ClearRigidbodyTempMovement();
- if (PhysicsUpdateRootMotionOverride != null)
- PhysicsUpdateRootMotionOverride(this, callbackDisplacement, callbackRotation);
- }
- protected virtual void OnDisable () {
- ClearRigidbodyTempMovement();
- }
- protected void FindRigidbodyComponent () {
- rigidBody2D = this.GetComponent<Rigidbody2D>();
- if (!rigidBody2D)
- rigidBody = this.GetComponent<Rigidbody>();
- if (!rigidBody2D && !rigidBody) {
- rigidBody2D = this.GetComponentInParent<Rigidbody2D>();
- if (!rigidBody2D)
- rigidBody = this.GetComponentInParent<Rigidbody>();
- }
- }
- 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 {
- public Vector2 start;
- public Vector2 current;
- public Vector2 mid;
- public Vector2 end;
- public bool timeIsPastMid;
- };
- abstract public RootMotionInfo GetRootMotionInfo (int trackIndex = 0);
- public ISkeletonComponent TargetSkeletonComponent {
- get {
- if (skeletonComponent == null)
- skeletonComponent = GetComponent<ISkeletonComponent>();
- return skeletonComponent;
- }
- }
- public ISkeletonAnimation TargetSkeletonAnimationComponent {
- get { return TargetSkeletonComponent as ISkeletonAnimation; }
- }
- public void SetRootMotionBone (string name) {
- Skeleton skeleton = skeletonComponent.Skeleton;
- Bone bone = skeleton.FindBone(name);
- if (bone != null) {
- this.rootMotionBoneIndex = bone.Data.Index;
- this.rootMotionBone = bone;
- FindTransformConstraintsAffectingBone();
- } else {
- Debug.Log("Bone named \"" + name + "\" could not be found. " +
- "Set 'skeletonRootMotion.rootMotionBoneName' before calling 'skeletonAnimation.Initialize(true)'.");
- this.rootMotionBoneIndex = 0;
- this.rootMotionBone = skeleton.RootBone;
- }
- }
- public void AdjustRootMotionToDistance (Vector2 distanceToTarget, int trackIndex = 0, bool adjustX = true, bool adjustY = true,
- float minX = 0, float maxX = float.MaxValue, float minY = 0, float maxY = float.MaxValue,
- bool allowXTranslation = false, bool allowYTranslation = false) {
- Vector2 distanceToTargetSkeletonSpace = (Vector2)transform.InverseTransformVector(distanceToTarget);
- Vector2 scaleAffectingRootMotion = GetScaleAffectingRootMotion();
- if (UsesRigidbody)
- distanceToTargetSkeletonSpace -= tempSkeletonDisplacement;
- Vector2 remainingRootMotionSkeletonSpace = GetRemainingRootMotion(trackIndex);
- remainingRootMotionSkeletonSpace.Scale(scaleAffectingRootMotion);
- if (remainingRootMotionSkeletonSpace.x == 0)
- remainingRootMotionSkeletonSpace.x = 0.0001f;
- if (remainingRootMotionSkeletonSpace.y == 0)
- remainingRootMotionSkeletonSpace.y = 0.0001f;
- if (adjustX)
- rootMotionScaleX = Math.Min(maxX, Math.Max(minX, distanceToTargetSkeletonSpace.x / remainingRootMotionSkeletonSpace.x));
- if (adjustY)
- 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) {
- return GetAnimationRootMotion(0, animation.Duration, animation);
- }
- public Vector2 GetAnimationRootMotion (float startTime, float endTime,
- Animation animation) {
- if (startTime == endTime)
- return Vector2.zero;
- TranslateTimeline translateTimeline = animation.FindTranslateTimelineForBone(rootMotionBoneIndex);
- TranslateXTimeline xTimeline = animation.FindTimelineForBone<TranslateXTimeline>(rootMotionBoneIndex);
- TranslateYTimeline yTimeline = animation.FindTimelineForBone<TranslateYTimeline>(rootMotionBoneIndex);
- // Non-looped base
- Vector2 endPos = Vector2.zero;
- Vector2 startPos = Vector2.zero;
- if (translateTimeline != null) {
- endPos = translateTimeline.Evaluate(endTime);
- startPos = translateTimeline.Evaluate(startTime);
- } else if (xTimeline != null || yTimeline != null) {
- endPos = TimelineExtensions.Evaluate(xTimeline, yTimeline, endTime);
- startPos = TimelineExtensions.Evaluate(xTimeline, yTimeline, startTime);
- }
- TransformConstraint[] transformConstraintsItems = skeletonComponent.Skeleton.TransformConstraints.Items;
- foreach (int constraintIndex in this.transformConstraintIndices) {
- TransformConstraint constraint = transformConstraintsItems[constraintIndex];
- ApplyConstraintToPos(animation, constraint, constraintIndex, endTime, false, ref endPos);
- ApplyConstraintToPos(animation, constraint, constraintIndex, startTime, true, ref startPos);
- }
- Vector2 currentDelta = endPos - startPos;
- // Looped additions
- if (startTime > endTime) {
- Vector2 loopPos = Vector2.zero;
- Vector2 zeroPos = Vector2.zero;
- if (translateTimeline != null) {
- loopPos = translateTimeline.Evaluate(animation.Duration);
- zeroPos = translateTimeline.Evaluate(0);
- } else if (xTimeline != null || yTimeline != null) {
- loopPos = TimelineExtensions.Evaluate(xTimeline, yTimeline, animation.Duration);
- zeroPos = TimelineExtensions.Evaluate(xTimeline, yTimeline, 0);
- }
- foreach (int constraintIndex in this.transformConstraintIndices) {
- TransformConstraint constraint = transformConstraintsItems[constraintIndex];
- ApplyConstraintToPos(animation, constraint, constraintIndex, animation.Duration, false, ref loopPos);
- ApplyConstraintToPos(animation, constraint, constraintIndex, 0, false, ref zeroPos);
- }
- currentDelta += loopPos - zeroPos;
- }
- UpdateLastConstraintPos(transformConstraintsItems);
- 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);
- }
- TransformConstraint[] 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);
- if (timeline == null)
- return;
- Vector2 mixXY = timeline.EvaluateTranslateXYMix(time);
- Vector2 invMixXY = timeline.EvaluateTranslateXYMix(time);
- Vector2 constraintPos;
- if (useLastConstraintPos)
- constraintPos = transformConstraintLastPos[GetConstraintLastPosIndex(constraintIndex)];
- else {
- Bone targetBone = constraint.Target;
- constraintPos = new Vector2(targetBone.X, targetBone.Y);
- }
- pos = new Vector2(
- pos.x * invMixXY.x + constraintPos.x * mixXY.x,
- 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[GetConstraintLastPosIndex(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];
- Bone targetBone = constraint.Target;
- transformConstraintLastPos[GetConstraintLastPosIndex(constraintIndex)] = new Vector2(targetBone.X, targetBone.Y);
- }
- }
- void UpdateLastConstraintRotation (TransformConstraint[] transformConstraintsItems) {
- foreach (int constraintIndex in this.transformConstraintIndices) {
- TransformConstraint constraint = transformConstraintsItems[constraintIndex];
- Bone targetBone = constraint.Target;
- transformConstraintLastRotation[GetConstraintLastPosIndex(constraintIndex)] = targetBone.Rotation;
- }
- }
- public RootMotionInfo GetAnimationRootMotionInfo (Animation animation, float currentTime) {
- RootMotionInfo rootMotion = new RootMotionInfo();
- float duration = animation.Duration;
- float mid = duration * 0.5f;
- rootMotion.timeIsPastMid = currentTime > mid;
- TranslateTimeline timeline = animation.FindTranslateTimelineForBone(rootMotionBoneIndex);
- if (timeline != null) {
- rootMotion.start = timeline.Evaluate(0);
- rootMotion.current = timeline.Evaluate(currentTime);
- rootMotion.mid = timeline.Evaluate(mid);
- rootMotion.end = timeline.Evaluate(duration);
- return rootMotion;
- }
- TranslateXTimeline xTimeline = animation.FindTimelineForBone<TranslateXTimeline>(rootMotionBoneIndex);
- TranslateYTimeline yTimeline = animation.FindTimelineForBone<TranslateYTimeline>(rootMotionBoneIndex);
- if (xTimeline != null || yTimeline != null) {
- rootMotion.start = TimelineExtensions.Evaluate(xTimeline, yTimeline, 0);
- rootMotion.current = TimelineExtensions.Evaluate(xTimeline, yTimeline, currentTime);
- rootMotion.mid = TimelineExtensions.Evaluate(xTimeline, yTimeline, mid);
- rootMotion.end = TimelineExtensions.Evaluate(xTimeline, yTimeline, duration);
- return rootMotion;
- }
- return rootMotion;
- }
- int GetConstraintLastPosIndex (int constraintIndex) {
- ExposedList<TransformConstraint> constraints = skeletonComponent.Skeleton.TransformConstraints;
- return transformConstraintIndices.FindIndex(addedIndex => addedIndex == constraintIndex);
- }
- void FindTransformConstraintsAffectingBone () {
- ExposedList<TransformConstraint> constraints = skeletonComponent.Skeleton.TransformConstraints;
- TransformConstraint[] constraintsItems = constraints.Items;
- for (int i = 0, n = constraints.Count; i < n; ++i) {
- TransformConstraint constraint = constraintsItems[i];
- if (constraint.Bones.Contains(rootMotionBone)) {
- transformConstraintIndices.Add(i);
- Bone targetBone = constraint.Target;
- Vector2 constraintPos = new Vector2(targetBone.X, targetBone.Y);
- transformConstraintLastPos.Add(constraintPos);
- transformConstraintLastRotation.Add(targetBone.Rotation);
- }
- }
- }
- Vector2 GetTimelineMovementDelta (float startTime, float endTime,
- TranslateXTimeline xTimeline, TranslateYTimeline yTimeline, Animation animation) {
- Vector2 currentDelta;
- if (startTime > endTime) // Looped
- currentDelta =
- (TimelineExtensions.Evaluate(xTimeline, yTimeline, animation.Duration)
- - TimelineExtensions.Evaluate(xTimeline, yTimeline, startTime))
- + (TimelineExtensions.Evaluate(xTimeline, yTimeline, endTime)
- - TimelineExtensions.Evaluate(xTimeline, yTimeline, 0));
- else if (startTime != endTime) // Non-looped
- currentDelta = TimelineExtensions.Evaluate(xTimeline, yTimeline, endTime)
- - TimelineExtensions.Evaluate(xTimeline, yTimeline, startTime);
- else
- currentDelta = Vector2.zero;
- return currentDelta;
- }
- void GatherTopLevelBones () {
- topLevelBones.Clear();
- Skeleton skeleton = skeletonComponent.Skeleton;
- foreach (Bone bone in skeleton.Bones) {
- if (bone.Parent == null)
- topLevelBones.Add(bone);
- }
- }
- void HandleUpdateLocal (ISkeletonAnimation animatedSkeletonComponent) {
- if (!this.isActiveAndEnabled)
- return; // Root motion is only applied when component is enabled.
- Vector2 boneLocalDelta = CalculateAnimationsMovementDelta();
- Vector2 parentBoneScale;
- Vector2 totalScale;
- Vector2 skeletonTranslationDelta = GetSkeletonSpaceMovementDelta(boneLocalDelta, out parentBoneScale, out totalScale);
- float skeletonRotationDelta = 0;
- if (transformRotation) {
- float boneLocalDeltaRotation = CalculateAnimationsRotationDelta();
- boneLocalDeltaRotation *= rootMotionScaleRotation;
- skeletonRotationDelta = GetSkeletonSpaceRotationDelta(boneLocalDeltaRotation, totalScale);
- }
- bool usesFixedUpdate = SkeletonAnimationUsesFixedUpdate;
- ApplyRootMotion(skeletonTranslationDelta, skeletonRotationDelta, parentBoneScale, usesFixedUpdate);
- if (usesFixedUpdate)
- PhysicsUpdate(usesFixedUpdate);
- }
- void ApplyRootMotion (Vector2 skeletonTranslationDelta, float skeletonRotationDelta, Vector2 parentBoneScale,
- bool skeletonAnimationUsesFixedUpdate) {
- // Accumulated displacement is applied on the next Physics update in FixedUpdate.
- // Until the next Physics update, tempSkeletonDisplacement and tempSkeletonRotation
- // are offsetting bone locations to prevent stutter which would otherwise occur if
- // we don't move every Update.
- bool usesRigidbody = this.UsesRigidbody;
- bool applyToTransform = !usesRigidbody && (ProcessRootMotionOverride == null || !disableOnOverride);
- accumulatedUntilFixedUpdate = !applyToTransform && !skeletonAnimationUsesFixedUpdate;
- if (ProcessRootMotionOverride != null)
- ProcessRootMotionOverride(this, skeletonTranslationDelta, skeletonRotationDelta);
- // Apply root motion to Transform or update values applied to RigidBody later (must happen in FixedUpdate).
- if (usesRigidbody) {
- rigidbodyDisplacement += transform.TransformVector(skeletonTranslationDelta);
- if (skeletonRotationDelta != 0.0f) {
- if (rigidBody != null) {
- Quaternion addedWorldRotation = Quaternion.Euler(0, 0, skeletonRotationDelta);
- rigidbodyLocalRotation = rigidbodyLocalRotation * addedWorldRotation;
- } else if (rigidBody2D != null) {
- Vector3 lossyScale = transform.lossyScale;
- float rotationSign = lossyScale.x * lossyScale.y > 0 ? 1 : -1;
- rigidbody2DRotation += rotationSign * skeletonRotationDelta;
- }
- }
- } else if (applyToTransform) {
- 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);
- }
- }
- tempSkeletonDisplacement += skeletonTranslationDelta;
- tempSkeletonRotation += skeletonRotationDelta;
- if (accumulatedUntilFixedUpdate) {
- SetEffectiveBoneOffsetsTo(tempSkeletonDisplacement, tempSkeletonRotation, parentBoneScale);
- } else {
- ClearEffectiveBoneOffsets(parentBoneScale);
- }
- }
- void ApplyTransformConstraints () {
- rootMotionBone.AX = rootMotionBone.X;
- rootMotionBone.AY = rootMotionBone.Y;
- rootMotionBone.AppliedRotation = rootMotionBone.Rotation;
- TransformConstraint[] transformConstraintsItems = skeletonComponent.Skeleton.TransformConstraints.Items;
- foreach (int constraintIndex in this.transformConstraintIndices) {
- TransformConstraint constraint = transformConstraintsItems[constraintIndex];
- // 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(Skeleton.Physics.None);
- }
- }
- Vector2 GetScaleAffectingRootMotion () {
- Vector2 parentBoneScale;
- return GetScaleAffectingRootMotion(out parentBoneScale);
- }
- Vector2 GetScaleAffectingRootMotion (out Vector2 parentBoneScale) {
- Skeleton skeleton = skeletonComponent.Skeleton;
- Vector2 totalScale = Vector2.one;
- totalScale.x *= skeleton.ScaleX;
- totalScale.y *= skeleton.ScaleY;
- parentBoneScale = Vector2.one;
- Bone scaleBone = rootMotionBone;
- while ((scaleBone = scaleBone.Parent) != null) {
- #if USE_APPLIED_PARENT_SCALE
- parentBoneScale.x *= scaleBone.AScaleX;
- parentBoneScale.y *= scaleBone.AScaleY;
- #else
- parentBoneScale.x *= scaleBone.ScaleX;
- parentBoneScale.y *= scaleBone.ScaleY;
- #endif
- }
- totalScale = Vector2.Scale(totalScale, parentBoneScale);
- totalScale *= AdditionalScale;
- return totalScale;
- }
- Vector2 GetSkeletonSpaceMovementDelta (Vector2 boneLocalDelta, out Vector2 parentBoneScale, out Vector2 totalScale) {
- Vector2 skeletonDelta = boneLocalDelta;
- totalScale = GetScaleAffectingRootMotion(out parentBoneScale);
- skeletonDelta.Scale(totalScale);
- Vector2 rootMotionTranslation = new Vector2(
- rootMotionTranslateXPerY * skeletonDelta.y,
- rootMotionTranslateYPerX * skeletonDelta.x);
- skeletonDelta.x *= rootMotionScaleX;
- skeletonDelta.y *= rootMotionScaleY;
- skeletonDelta.x += rootMotionTranslation.x;
- skeletonDelta.y += rootMotionTranslation.y;
- if (!transformPositionX) skeletonDelta.x = 0f;
- if (!transformPositionY) skeletonDelta.y = 0f;
- return skeletonDelta;
- }
- 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();
- // Move top level bones in opposite direction of the root motion bone
- Skeleton skeleton = skeletonComponent.Skeleton;
- foreach (Bone topLevelBone in topLevelBones) {
- 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 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, 0, parentBoneScale);
- }
- void ClearRigidbodyTempMovement () {
- rigidbodyDisplacement = Vector2.zero;
- tempSkeletonDisplacement = Vector2.zero;
- rigidbodyLocalRotation = Quaternion.identity;
- rigidbody2DRotation = 0;
- tempSkeletonRotation = 0;
- }
- }
- }
|