using UnityEngine; using System.Collections; using RootMotion.FinalIK; namespace RootMotion.Demos { /// /// Warping an effector from animation space to world space. /// The weight curve of the warp is used to add the offset from "Warp From" to "Warp To" to the effector. /// "Warp From" should be a Transform parented to the root of the character, hence in animation space (virtual position where a soccer player's foot hits the ball in the animation). /// "Warp To" should be a Transform in the world space (the actual ball). /// public class AnimationWarping : OffsetModifier { /// /// Definition of a warp from 'warpFrom' to 'warpTo' by normalized time of the animation /// [System.Serializable] public struct Warp { [Tooltip("Layer of the 'Animation State' in the Animator.")] public int animationLayer; [Tooltip("Name of the state in the Animator to warp.")] public string animationState; [Tooltip("Warping weight by normalized time of the animation state.")] public AnimationCurve weightCurve; [Tooltip("Animated point to warp from. This should be in character space so keep this Transform parented to the root of the character.")] public Transform warpFrom; [Tooltip("World space point to warp to.")] public Transform warpTo; [Tooltip("Which FBBIK effector to use?")] public FullBodyBipedEffector effector; } /// /// Using effector.positionOffset or effector.position with effector.positionWeight? /// [System.Serializable] public enum EffectorMode { PositionOffset, Position, } [Tooltip("Reference to the Animator component to use")] public Animator animator; [Tooltip("Using effector.positionOffset or effector.position with effector.positionWeight? " + "The former will enable you to use effector.position for other things, the latter will weigh in the effectors, hence using Reach and Pull in the process.")] public EffectorMode effectorMode; [Space(10)] [Tooltip("The array of warps, can have multiple simultaneous warps.")] public Warp[] warps; private EffectorMode lastMode; protected override void Start() { base.Start(); lastMode = effectorMode; } /// /// Gets the current warping weight of the warp at the specified index. /// public float GetWarpWeight(int warpIndex) { if (warpIndex < 0) { Debug.LogError("Warp index out of range."); return 0f; } if (warpIndex >= warps.Length) { Debug.LogError("Warp index out of range."); return 0f; } if (animator == null) { Debug.LogError("Animator unassigned in AnimationWarping"); return 0f; } // Get the animator state info AnimatorStateInfo info = animator.GetCurrentAnimatorStateInfo(warps[warpIndex].animationLayer); // If not currently playing the animation state of the warp, return if (!info.IsName(warps[warpIndex].animationState)) return 0f; // Evaluate the weight of the warp by the current normalized time of the state return warps[warpIndex].weightCurve.Evaluate(info.normalizedTime - (int)info.normalizedTime); } // Called each time before FBBIK solves protected override void OnModifyOffset() { // Go through all the warps... for (int i = 0; i < warps.Length; i++) { float warpWeight = GetWarpWeight(i); // Get the offset form warpFrom to warpTo Vector3 offset = warps[i].warpTo.position - warps[i].warpFrom.position; // Add that offset to the effector (using positionOffset additively, because it will be reset to Vector3.zero by FBBIK after each update) switch(effectorMode) { case EffectorMode.PositionOffset: ik.solver.GetEffector(warps[i].effector).positionOffset += offset * warpWeight * weight; break; case EffectorMode.Position: ik.solver.GetEffector(warps[i].effector).position = ik.solver.GetEffector(warps[i].effector).bone.position + offset; ik.solver.GetEffector(warps[i].effector).positionWeight = weight * warpWeight; break; } } // Switching modes safely, weighing out effector positionWeights if (lastMode == EffectorMode.Position && effectorMode == EffectorMode.PositionOffset) { foreach (Warp warp in warps) { ik.solver.GetEffector(warp.effector).positionWeight = 0f; } } lastMode = effectorMode; } // Set effector positionWeights to 0 if in "Position" effector mode void OnDisable() { if (effectorMode != EffectorMode.Position) return; foreach (Warp warp in warps) { ik.solver.GetEffector(warp.effector).positionWeight = 0f; } } } }