RootMotionComputer 根运动计算机

using UnityEngine;
using System.Collections;

/*
 * ----------------------------------------------------------------------------
 * Creation Info
 * ----------------------------------------------------------------------------
 * Root Motion Computer
 * Version: 1.2
 * Date: 2010.12.08
 * Author: Adam Mechtley (http://adammechtley.com)
 * Created for Mixamo, Inc. (http://mixamo.com)
 *
 * ----------------------------------------------------------------------------
 * Description:
 * ----------------------------------------------------------------------------
 * Like many other game engines, Unity was developed with the intent that
 * character animations be created in-place, as though the characters were
 * moving and acting on a treadmill, and the characters‘ root nodes would then
 * be moved programmatically using physics, a character controller, or another
 * procedural mechanism. Unfortunately, many character movements, such as a
 * zombie lurching forward, do not move forward with a constant velocity.
 * Consequently, such motions can introduce foot sliding or dramatic bobbing
 * back and forth when moved procedurally.
 *
 * The Root Motion Computer was designed to solve this problem, particularly by
 * leveraging the fact that motion-capture animations actually capture a moving
 * actor rather than an actor performing in place. Using all of the default
 * settings, the root motion computer uses the movement of the character‘s
 * pelvis to move move the root node. Thus, in-place motions will play back in-
 * place, while motions captured with forward or sideways movement will
 * actually move the character when looping, rather than snapping back to the
 * starting location. Users can also configure the computer‘s various settings
 * to instead pipe its output to another script to drive the velocity of a
 * character controller or another movement tool.
 *
 * ----------------------------------------------------------------------------
 * Usage:
 * ----------------------------------------------------------------------------
 * You can place this script anywhere in your project folder. Because it is
 * written in C#, however, you must put it in your Plugins folder in your
 * project if you are coding in UnityScript.
 *
 * In many cases, you can simply add the component to your character and it
 * should "just work." Otherwise you can manually specify various properties:
 *
 * isManagedExternally: Specifies that another script will invoke Initialize()
 *     and ComputeRootMotion(). This is used if you need to manage the
 *     execution order to prevent the computer from interfering with animation
 *     requests you may make in your own Start(), Awake(), or LateUpdate()
 *     functions.
 * rootNode: The transform that is actually moved, whether by the computer or
 *     by another mechanism like a character controller.
 * anim: The animation component from which to process AnimationStates.
 * pelvis: The character‘s pelvis transform. This object is used to determine
 *     changes in the character‘s overall position or rotation.
 * pelvisForwardAxis: The axis on the pelvis that points to the character‘s
 *     front in the bind pose.
 * pelvisRightAxis: The axis on the pelvis that points to the character‘s right
 *     in the bind pose.
 * computationMode: Specifies whether the computer should compute only forward
 *     translation, all translation (forward-back and side-to-side), or all
 *     translation as well as turning rotation.
 * applyMotion: Specifies whether the computation results should be applied to
 *     rootNode. Set to false if output is going to be read and processed by
 *     another script to move a character controller, for example.
 * deltaPosition: Represents the character‘s change in translation since the
 *     last frame, given in the space of rootNode.
 * deltaRotation: Represents the character‘s change in orientation since the
 *     last frame, given in the space of rootNode.
 * deltaEulerAngles: Same as deltaRotation but converted into Euler angles.
 * isDebugMode: Renders pelvis axes in the scene view when the character is
 *     selected. Renders axis tripods to illustrate the position and
 *     orientation of the pelvis and root node when the game is playing.
 * debugGizmoSize: A scalar for the debug gizmos.
 *
 * ----------------------------------------------------------------------------
 * Notes and Limitations:
 * ----------------------------------------------------------------------------
 * 1. The computer is currently only designed to handle movement of characters
 * forward-back and side-to-side with rotation about their up-axes. As such, it
 * offers no generalized mechanism for adjusting the height of a character
 * (e.g. jumping, going up stairs). Because the computer operates using delta
 * values in LateUpdate(), however, you can implement your own custom logic for
 * adjusting a character‘s height in your own movement code and the computer
 * will simply work on top if it.
 *
 * 2. As of Unity 2.x, there is no way to query the post-normalized weights of
 * AnimationStates. The computer attempts to work around this by rebuilding
 * normalized weights for each state using the same process that Unity uses
 * (applying weights to top-most layers first, and then working down).
 *
 * 3. The computer currently assumes that the clip for any particular
 * AnimationState will not change. (Generally speaking, it should not once it
 * has been added to the animation component anyway.)
 *
 * 4. The computer should support adding new clips at run-time, though the
 * feature has been tested only briefly.
 *
 * 5. Because of how rotation computation works, if a character is on his
 * stomach and then rolls onto his back (or vice-versa), then it is inadvisable
 * to blend in other motions at the time the actual roll occurs (unless it is
 * another synchronized rolling motion).
 * */

// a struct to store information about all of the animation states
/// <summary>
/// 存放动画状态的结构体
/// </summary>
public struct AnimInfo
{

    /// <summary>
    /// 当前动作的规范化时间,直接指向对应动画的规范化时间
    /// </summary>
    public float currentNormalizedTime;
    /// <summary>
    /// 之前动作的规范化时间
    /// </summary>
    public float previousNormalizedTime;
    /// <summary>
    /// 当前动作的权重,直接指向对应动画的权重
    /// </summary>
    public float currentWeight; // the actual weight value queried from the AnimationState
    /// <summary>
    /// 实际播放出来的权重
    /// </summary>
    public float contributingWeight; // the weight the AnimationState is actually contributing to the final result based on layers
    /// <summary>
    /// 当前位置
    /// </summary>
    public Vector3 currentPosition;
    /// <summary>
    /// 之前的位置
    /// </summary>
    public Vector3 previousPosition;
    /// <summary>
    /// 开始位置
    /// </summary>
    public Vector3 startPosition;
    /// <summary>
    /// 结束位置
    /// </summary>
    public Vector3 endPosition;
    /// <summary>
    /// 当前的轴
    /// </summary>
    public Vector3 currentAxis;
    /// <summary>
    /// 之前的轴
    /// </summary>
    public Vector3 previousAxis;
    /// <summary>
    /// 开始的轴
    /// </summary>
    public Vector3 startAxis;
    /// <summary>
    /// 结束的轴
    /// </summary>
    public Vector3 endAxis;
    /// <summary>
    /// 全部旋转
    /// </summary>
    public Quaternion totalRotation;
}

// an enum to describe how delta values should be computed
/// <summary>
/// 增量值计算枚举
/// </summary>
public enum RootMotionComputationMode
{
    ZTranslation,
    XZTranslation,
    TranslationAndRotation
}

/// <summary>
/// 管理所有动画,权重,混合
/// </summary>
[AddComponentMenu("Mixamo/Root Motion Computer")]
public class RootMotionComputer : MonoBehaviour
{
    // the transform to have root motion applied
    /// <summary>
    /// 脚本所在对象
    /// </summary>
    public Transform rootNode;

    // the animation component where all of the clips for this model exist
    /// <summary>
    /// 当前所有的动画数组
    /// </summary>
    public Animation anim;

    // the pelvis joint from which the script obtains x-z motion and y-rotation for the root
    /// <summary>
    /// 模型所在的对象,设置X向Z移动、Y旋转
    /// </summary>
    public Transform pelvis;
    /// <summary>
    /// 骨盆局部的轴,右方向 也是X轴
    /// </summary>
    public Vector3 pelvisRightAxis = Vector3.right; // its local axis specifying the right direction
    /// <summary>
    /// 暂时存储骨盆的局部位置
    /// </summary>
    private Vector3 pLocalPosition; // a variable to temporarily store and set the pelvis local position after computation

    // parameters for computation and application of result
    /// <summary>
    /// 是否是外部管理
    /// </summary>
    public bool isManagedExternally = false; // if the computer is managed externally, then its calls are invoked manually
    /// <summary>
    /// 增量结构
    /// </summary>
    public RootMotionComputationMode computationMode = RootMotionComputationMode.TranslationAndRotation;
    /// <summary>
    /// 应用运动
    /// </summary>
    public bool applyMotion = true;

    // information about the computed root position
    /// <summary>
    /// 来自上一帧的三角州位置
    /// </summary>
    private Vector3 dPosition = Vector3.zero; // local-space delta position since previous frame
    /// <summary>
    /// 来自上一帧的三角州位置
    /// </summary>
    public Vector3 deltaPosition { get { return dPosition; } }
    /// <summary>
    /// 减少分配的一个容器
    /// </summary>
    private Vector3 p; // a simple container to minimize allocations

    // information about the computed root rotation
    /// <summary>
    /// 来自上一帧的三角州旋转
    /// </summary>
    private Quaternion dRotation = Quaternion.identity; // local-space delta rotation since previous frame
    /// <summary>
    /// 来自上一帧的三角州旋转
    /// </summary>
    public Quaternion deltaRotation { get { return dRotation; } }
    /// <summary>
    /// 来自上一帧的三角州旋转的欧拉角
    /// </summary>
    public Vector3 deltaEulerAngles { get { return dRotation.eulerAngles; } }

    // a hashtable storing information about each AnimationState
    /// <summary>
    /// 存放所有动画的哈希表
    /// </summary>
    private Hashtable animInfoTable;
    /// <summary>
    /// 动画状态信息结构体,为了减少分配
    /// </summary>
    private AnimInfo info; // a simple container to minimize allocations

    // specify whether the component should be running in debug mode
    /// <summary>
    /// 是否是调试模式下运行
    /// </summary>
    public bool isDebugMode = true;
    /// <summary>
    /// 调试线框的大小
    /// </summary>
    public float debugGizmoSize = 0.25f;

    // is the computation occuring on the first frame of execution?
    /// <summary>
    /// 计算上的第一帧执行?
    /// </summary>
    private bool isFirstFrame = true;

    // the highest and lowest layers on which there is an AnimationState
    /// <summary>
    /// 最高的层
    /// </summary>
    private int highestLayer = 0;
    /// <summary>
    /// 最低的层
    /// </summary>
    private int lowestLayer = 0;

    /*
     * Initialize the component if it is not managed externally
     * */
    void Start()
    {
        //如果不是外部管理
        if (!isManagedExternally) Initialize();
    }

    /*
     * Initialize all necessary variables and warn user as needed
     * */
    /// <summary>
    /// 必须的初始化
    /// </summary>
    public void Initialize()
    {
        // validate component references
        if (anim == null)
        {
            //取子对象的所有动画
            anim = gameObject.GetComponentInChildren(typeof(Animation)) as Animation;
            if (anim == null) Debug.LogError("No animation component has been specified.", this);
            else if (isDebugMode) Debug.LogWarning(string.Format("No animation component has been specified. Using the animation component on {0}.", gameObject.name), this);
        }
        if (rootNode == null)
        {
            //等于自身
            rootNode = transform;
            if (isDebugMode) Debug.LogWarning(string.Format("No root object has been manually specified. Assuming that {0} is the root object to be moved.", gameObject.name), this);
        }
        if (pelvis == null)
        {
            //取自身的所有组件
            Component[] hierarchy = GetComponentsInChildren(typeof(Transform));
            // first try to figure out the pelvis based on name
            //给骨盆赋值
            foreach (Transform joint in hierarchy)
                if (pelvis == null && (joint.name.ToLower() == "hips" || joint.name.ToLower().Contains("pelvis"))) pelvis = joint;
            // if no named pelvis was found, then try to find the first skinned mesh renderer with children
            if (pelvis == null)
            {
                foreach (Transform joint in hierarchy)
                {
                    if (joint.GetComponent(typeof(SkinnedMeshRenderer)) == null) continue;
                    Component[] children = joint.GetComponentsInChildren(typeof(Transform));
                    if (children.Length > 1) pelvis = joint;
                }
            }
            if (pelvis == null) Debug.LogError("No pelvis transform has been specified.", this);
            else if (isDebugMode) Debug.LogWarning(string.Format("No pelvis object as been manually specified. Assuming that {0} is the pelvis object to track.", pelvis.name));
        }

        // store whether or not the animation component is playing
        bool isAnimationPlaying = anim.isPlaying;

        // store information about each AnimationState in a hashtable for easy lookup later
        animInfoTable = new Hashtable();
        // first, figure out what all AnimationStates are currently doing
        //默认设置给每一个动画
        foreach (AnimationState aState in anim)
        {
            AddAnimInfoToTable(aState);
        }
        //动画采样
        anim.Sample(); // BUG: need to call Sample() once up front or AnimationStates in Animation component may reorder during iteration
        //停止所有动画,以确保所有的动画权重为0
        anim.Stop(); // call Stop() to ensure that all weights go to 0
        //重新启动,以确保采样时的值是正确的
        anim.enabled = true; // reenable the animation component to ensure that values will be correct when sampling
        // store properties for each state one at a time
        foreach (AnimationState aState in anim)
        {
            SetupNewAnimInfo(aState);
        }

        // revert the animation component to whatever it was doing beforehand
        //权重,规范化时间 再次赋值为结构体里面的!当时为何不在设置结构体的时候就直接做了呢?
        foreach (AnimationState aState in anim)
        {
            info = (AnimInfo)animInfoTable[aState];
            //尼玛。这里不是重赋值么
            aState.weight = info.currentWeight;
            aState.normalizedTime = info.currentNormalizedTime;
        }
        if (isAnimationPlaying) anim.Play();
        else anim.Stop();
    }

    /*
     * Add information about the provided state to the hashtable
     * */
    /// <summary>
    /// 设置一个动画的详细信息为结构体,并将之 动画名,结构体 的形式存入哈希表
    /// </summary>
    /// <param name="aState">动画</param>
    public void AddAnimInfoToTable(AnimationState aState)
    {
        // create the new info object
        AnimInfo newInfo = new AnimInfo();

        // store the current properties
        //设置规范化时间
        newInfo.currentNormalizedTime = aState.normalizedTime;
        //设置权重
        newInfo.currentWeight = aState.weight;

        // add a new hashtable entry for the AnimInfo
        //存入动画哈希表中
        animInfoTable.Add(aState, newInfo);
    }

    /*
     * Set up further properties for a newly-created info object after calling AddAnimInfoToTable()
     * */
    /// <summary>
    /// 设置哈希表里动画对应的结构体,重置这个动画
    /// </summary>
    /// <param name="aState">动画名</param>
    public void SetupNewAnimInfo(AnimationState aState)
    {
        //取这个动画的结构体
        AnimInfo newInfo = (AnimInfo)animInfoTable[aState];

        // store information about the animation state up front
        //当前的状态,应当是false 。
        bool isEnabled = aState.enabled;
        WrapMode wrapMode = aState.wrapMode;

        // activate the animation state 激活动画状态
        aState.weight = 1f;
        aState.enabled = true;

        //循环模式 确保该值在normalizedTime=1f是不一定相同normalizedTime= 0F ,官方没有 Clamp 的解释 操
        aState.wrapMode = WrapMode.Clamp; // ensures the value at normalizedTime = 1f is not necessarily the same as normalizedTime = 0f

        // scrub to the beginning of the animation state and store initial position and rotation values
        //净化开始动画状态的初始位置的和旋转值
        aState.normalizedTime = 0f;
        anim.Sample();
        newInfo.startPosition = GetProjectedPosition(pelvis);
        newInfo.previousPosition = GetProjectedPosition(pelvis);
        newInfo.startAxis = GetProjectedAxis(pelvis, pelvisRightAxis);
        newInfo.previousAxis = GetProjectedAxis(pelvis, pelvisRightAxis);

        // scrub to the end of the animation state and store final position and rotation values
        //净化结束的动画状态,并存储最后的位置和旋转值
        aState.normalizedTime = 1f;
        anim.Sample();
        newInfo.endPosition = GetProjectedPosition(pelvis);
        newInfo.endAxis = GetProjectedAxis(pelvis, pelvisRightAxis);

        // store the total rotation over the course of the animation
        //从开始到结束的一个旋转的四元数
        newInfo.totalRotation = Quaternion.FromToRotation(newInfo.startAxis, newInfo.endAxis);

        // reset the clip to its starting point and scrub it down to 0 weight
        //重置这个动画到开始的点到0的权重
        aState.normalizedTime = 0f;
        aState.weight = 0f;
        aState.enabled = isEnabled;
        aState.wrapMode = wrapMode;
        anim.Sample();

        //最后赋值
        animInfoTable[aState] = newInfo;
    }

    /*
     * All motion is applied in LateUpdate() since it is called after all animation states have been set
     * */
    //以保万一才在LatUpdate里运行,因为开头设置了。
    void LateUpdate()
    {
        if (!isManagedExternally) ComputeRootMotion();
    }

    /*
     * Compute the root motion variables
     * */
    /// <summary>
    /// 计算根运动变量
    /// </summary>
    public void ComputeRootMotion()
    {
        // early out if no animation is playing 没运动
        if (!anim.isPlaying) return;

        // store whether or not we should be bothering to compute rotation parameters存储是否我们应该费心去计算旋转参数
        bool isRotationMode = (computationMode == RootMotionComputationMode.TranslationAndRotation);

        #region 设置最高层低层,是否所有的动画都已加入哈希表?没有则添加并设置
        // an array to store any AnimationStates that have been added to the animation component since the last frame
        //一个数组来存储已被添加到自上一帧的动画组件任何AnimationStates,用于初始化没有添加进的动画
        ArrayList newlyAddedAnimationStates = null;

        // first store current actual weight and time information for all AnimationStates
        foreach (AnimationState aState in anim)
        {
            // store the highest and lowest layers for use in a later iteration
            //给最高层和最低层赋值 . 这里每次都要赋一下,让我感到很费解
            highestLayer = Mathf.Max(highestLayer, aState.layer);
            lowestLayer = Mathf.Min(lowestLayer, aState.layer);

            // if any new animation states have been added, then deal with them in a following iteration
            // 这里判断有没有初始化没添加到哈希表的动画
            if (!animInfoTable.ContainsKey(aState))
            {
                AddAnimInfoToTable(aState);
                newlyAddedAnimationStates.Add(aState);
                continue;
            }

            info = (AnimInfo)animInfoTable[aState];
            //我擦。又一次分配
            info.currentNormalizedTime = aState.normalizedTime;
            info.currentWeight = aState.weight;

            animInfoTable[aState] = info;

            // scrub the weight down to 0 for the next iteration
            //为下一次做好准备??
            aState.weight = 0f;
        }

        // if any new AnimationStates have been added, add their info to the table
        //有新的动画添加,就设置
        if (newlyAddedAnimationStates != null && newlyAddedAnimationStates.Count > 0)
        {
            // first set all weights to 0, which will include newly added states
            foreach (AnimationState aState in anim) aState.weight = 0f;

            // store all the properties for the new states
            foreach (AnimationState aState in newlyAddedAnimationStates) SetupNewAnimInfo(aState);
        }

        #endregion

        #region 计算层的整合,权重的分配。这里有些不懂的地方
        // compute normalized AnimationState weights across layers since Unity does not expose them
        //计算归AnimationState的重量跨越层,因为Unity不暴露他们
        //总的权重,余下的权重
        float remainingWeight = 1f;
        //从最高层开始
        for (int i = highestLayer; i >= lowestLayer; i--)
        {
            //所有动画(指当前的层)权重的总和
            float weightOnThisLayer = 0f;
            //用循环取当前层。layer层中可能有多个动画
            foreach (AnimationState aState in anim)
            {

                if (aState.layer != i) continue;
                //取当前层的当前动画信息
                info = (AnimInfo)animInfoTable[aState];

                // find out how much weight the animation state is actually contributing this frame
                //没有启用动画,或者已分配完
                if (!aState.enabled || remainingWeight <= 0f)
                {
                    info.contributingWeight = 0f;
                }
                else
                {
                    //实际上播放出来的权重
                    info.contributingWeight = remainingWeight * info.currentWeight;
                    //print(aState.name + "-- layer: " + aState.layer  + "还余下的权重:" + remainingWeight + "这个动画当前的权重:"
                    //    + info.currentWeight + "实际播放出来的权重" + info.contributingWeight);
                }
                //将这个层上所有的动画的权重加起来
                weightOnThisLayer += info.contributingWeight;
                //print(weightOnThisLayer);
                animInfoTable[aState] = info;
            }
            // if the weight on this layer is > 1, then normalize it
            // using Blend() or setting weights manually will not affect other weights on the layer, so they must be manually renormalized
            //这个层上所有动画权重相加后的数大于1了
            if (weightOnThisLayer > 1f)
            {
                //设置一个百分数来减少实际播放出来的权重
                float oneOverWeightOnThisLayer = 1f / weightOnThisLayer;
                //循环这个层的动画,并做出一些播出的权重的减少
                foreach (AnimationState aState in anim)
                {
                    if (aState.layer != i) continue;
                    info = (AnimInfo)animInfoTable[aState];
                    info.contributingWeight = info.contributingWeight * oneOverWeightOnThisLayer;
                    animInfoTable[aState] = info;
                }
                //重置为1
                weightOnThisLayer = 1f;
            }
            //余下的权重 等于 减去分配后的权重
            remainingWeight -= weightOnThisLayer;
        }

        #endregion

        // reset the delta values for this frame
        //重设此帧的增量值
        dPosition = Vector3.zero;
        dRotation = Quaternion.identity;

        // compute each AnimationState‘s individual contribution to the current frame‘s delta values
        //计算每个动画的贡献,以当前帧的增量值
        foreach (AnimationState aState in anim)
        {
            info = (AnimInfo)animInfoTable[aState];

            // early out if this state was contributing nothing this frame
            //如果没有权重,下一位
            if (info.contributingWeight == 0f) continue;

            // early out if aState uses additive blending
            // NOTE: Not entirely sure if this is ideal or not, but it generally should be
            //如果混合模式为附加,则下一位
            if (aState.blendMode == AnimationBlendMode.Additive) continue;

            // scrub the weight up to 1 for sampling values
            //设置权重为1
            aState.weight = 1f;

            // sample the values for the projected root configuration back one frame
            // NOTE: cannot simply store these values from one frame to the next since user may manually change the time value at any point
            //采样数值为投影机的根目录配置一帧
            //注意:不能简单地存储这些值从一帧到下一个,因为用户可以在任何时候手动更改时间值
            aState.time = aState.time - Time.deltaTime * aState.speed;
            info.previousNormalizedTime = aState.normalizedTime;
            anim.Sample();
            info.previousAxis = GetProjectedAxis(pelvis, pelvisRightAxis);
            info.previousPosition = GetProjectedPosition(pelvis);

            // sample the values for the projected root configuration at the current frame
            aState.normalizedTime = info.currentNormalizedTime;
            anim.Sample();
            info.currentPosition = GetProjectedPosition(pelvis);
            info.currentAxis = GetProjectedAxis(pelvis, pelvisRightAxis);

            // ensure both normalizedTime values are positive
            info.previousNormalizedTime = 1f + info.previousNormalizedTime - (int)info.previousNormalizedTime;
            info.currentNormalizedTime = 1f + info.currentNormalizedTime - (int)info.currentNormalizedTime;

            // determine the contribution to the root‘s delta this frame based on whether the animation looped since the previous frame
            if (info.previousNormalizedTime - (int)info.previousNormalizedTime > info.currentNormalizedTime - (int)info.currentNormalizedTime)
            {
                // compute displacement with respect to identity
                p = info.contributingWeight * ((info.endPosition - info.previousPosition) + (info.currentPosition - info.startPosition));
                if (isRotationMode)
                {
                    // rotate displacement into current orientation
                    p = Quaternion.FromToRotation(info.currentAxis, info.totalRotation * Vector3.right) * p;
                    // compute angular displacement and append to result
                    dRotation *= Quaternion.Slerp(Quaternion.identity,
                        Quaternion.FromToRotation(info.previousAxis, info.endAxis) * Quaternion.FromToRotation(info.startAxis, info.currentAxis),
                        info.contributingWeight);
                }
                // append displacement to result
                dPosition += p;
            }
            else
            {
                // compute displacement with respect to identity
                p = info.contributingWeight * (info.currentPosition - info.previousPosition);
                if (isRotationMode)
                {
                    // rotate displacement into current orientation
                    p = Quaternion.FromToRotation(info.currentAxis, Vector3.right) * p;
                    // compute angular displacement and append to result
                    dRotation *= Quaternion.Slerp(Quaternion.identity, Quaternion.FromToRotation(info.previousAxis, info.currentAxis), info.contributingWeight);
                }
                // append displacement to result
                dPosition += p;
            }

            // scrub the weight back down to 0 so as to not affect sampling of other states
            aState.weight = 0f;
        }

        // reset weights to where they were before computation
        foreach (AnimationState aState in anim)
        {
            info = (AnimInfo)animInfoTable[aState];
            aState.weight = info.currentWeight;
        }

        // return the character to its current pose
        anim.Sample();

        // delta values for the first frame should simply move from the starting configuration into the current frame of animation
        if (isFirstFrame)
        {
            // simply translate and rotate to the current projected position and orientation
            dPosition = GetProjectedPosition(pelvis);
            dRotation = Quaternion.FromToRotation(Vector3.right, GetProjectedAxis(pelvis, pelvisRightAxis));

            // rotate displacement into current orientation
            if (isRotationMode) dPosition = Quaternion.FromToRotation(GetProjectedAxis(pelvis, pelvisRightAxis), Vector3.right) * dPosition;

            isFirstFrame = false;
        }

        // store the local position of the pelvis before returning it to hover over the root
        pLocalPosition = pelvis.localPosition;

        // zero out the local x-component of the position delta if root translation method is z-only
        if (computationMode == RootMotionComputationMode.ZTranslation) dPosition = Vector3.forward * Vector3.Dot(dPosition, Vector3.forward);
        // otherwise zero out the local x-position of the pelvis
        else pLocalPosition.x = 0f;

        // return the pelvis to a point hovering over the root
        pLocalPosition.z = 0f;
        pelvis.localPosition = pLocalPosition;

        // if computing rotation, then zero out local y-rotation of the pelvis
        if (isRotationMode) pelvis.localRotation = Quaternion.FromToRotation(GetProjectedAxis(pelvis, pelvisRightAxis), Vector3.right) * pelvis.localRotation;

        // draw debug lines if requested
        if (isDebugMode) DrawDebug();

        // return if root movement is not requested (e.g. a character controller will use delta values)
        if (!applyMotion) return;

        // apply rotation if requested
        if (isRotationMode) rootNode.localRotation *= dRotation;

        // apply translation
        rootNode.Translate(dPosition, Space.Self);
    }

    /*
     * Obtain the position of t projected onto rootNode‘s zx plane
     * */
    /// <summary>
    /// 获取传入位置投射在根结点的ZX轴坐标的一个点 ,y = 0
    /// </summary>
    /// <param name="t">传入的位置</param>
    /// <returns></returns>
    private Vector3 GetProjectedPosition(Transform t)
    {
        Vector3 p = rootNode.InverseTransformPoint(t.position);
        p.y = 0f;
        return p;
    }

    /*
     * Obtain the projection of axis on t onto rootNode‘s zx plane
     * */
    /// <summary>
    /// 获取根节点的zx平面上的投影轴
    /// </summary>
    /// <param name="t">传入的位置</param>
    /// <param name="axis">方向</param>
    /// <returns></returns>
    private Vector3 GetProjectedAxis(Transform t, Vector3 axis)
    {
        Vector3 p = rootNode.InverseTransformDirection(t.TransformDirection(axis));
        p.y = 0f;
        return p;
    }

    /*
     * Draw axis tripods to show how root motion is being determined and applied
     * */
    private void DrawDebug()
    {
        // draw pelvis right axis
        Debug.DrawRay(pelvis.position, pelvis.TransformDirection(pelvisRightAxis) * debugGizmoSize, Color.red);

        // draw root node axes
        Debug.DrawRay(rootNode.position, rootNode.rotation * Vector3.forward * debugGizmoSize, Color.blue);
        Debug.DrawRay(rootNode.position, rootNode.rotation * Vector3.right * debugGizmoSize, Color.red);
        Debug.DrawRay(rootNode.position, rootNode.rotation * Vector3.up * debugGizmoSize, Color.green);
    }
}
时间: 2024-12-17 16:36:29

RootMotionComputer 根运动计算机的相关文章

世界十大黑客

凯文·米特尼克(Kevin Mitnick)         他是第一个在美国联邦调查局“悬赏捉拿”海报上露面的黑客.15岁的米特尼克闯入了“北美空中防务指挥系统”的计算机主机内,他和另外一些朋友翻遍了美国指向前苏联及其盟国的所有核弹头的数据资料,然后又悄无声息地溜了出来.这件事对美国军方来说已成为一大丑闻,五角大楼对此一直保持沉默.事后,美国著名的军事情报专家克赖顿曾 说:“如果当时米特尼克将这些情报卖给克格勃,那么他至少可以得到50万美元(大约310万人民币)的酬金.而美国则需花费数十亿美元

黑客故事

大事记 光影集锦 图册集锦 花絮视频 1凯文 凯文·米特尼克被称为世界上“头号电脑黑客”. 其实他的技术也许并不是黑客中最好的,甚至相当多的黑客们都反感他,认为他是只会用攻击.不懂技术的攻击狂,但是其黑客经历的传奇性足以让全世界为之震惊,也使得所有网络安全人员丢尽面子. 主要成就:他是第一个在美国联邦调查局“悬赏捉拿”海报上露面的黑客.15岁的米特尼克闯入了“北美空中防务指挥系统”的计算机主机内,他和另外一些朋友翻遍了美国指向前苏联及其盟国的所有核弹头的数据资料,然后又悄无声息地溜了出来. 这件

世界十大黑客简介

凯文·米特尼克(Kevin Mitnick)被称为世界上“头号电脑黑客”. 其实他的技术也许并不是黑客中最好的,甚至相当多的黑客们都反感他,认为他 凯文·米特尼克(Kevin Mitnick) 是只会用攻击.不懂技术的攻击狂,但是其黑客经历的传奇性足以让全世界为之震惊,也使得所有网络安全人员丢尽面子. 主要成就:他是第一个在美国联邦调查局“悬赏捉拿”海报上露面的黑客.15岁的米特尼克闯入了“北美空中防务指挥系统”的计算机主机内,他和另外一些朋友翻遍了美国指向前苏联及其盟国的所有核弹头的数据资料,

Unity-Animator零散记录4 API

1.public Vector3 angularVelocity { get; } 未测 2.public bool animatePhysics { get; set; } 未测 3.public bool applyRootMotion { get; set; } 是否打开根运动 4.public Avatar avatar { get; set; } [人形动画]人形动画Avatar 5.public Vector3 bodyPosition { get; set; } 未测 6.publ

官方 Animator 例子解析 Animator.MatchTarget

一.官方的解释 1 Animator.MatchTargetSwitch to Manual 2 void MatchTarget(Vector3 matchPosition, Quaternion matchRotation, AvatarTarget targetBodyPart, MatchTargetWeightMask weightMask, float startNormalizedTime, float targetNormalizedTime = 1); 3 Parameters

【Unity】10.4 类人动画角色的控制

分类:Unity.C#.VS2015 创建日期:2016-05-02 一.简介 导入角色网格和动画及设置 Avatar 之后,就可以准备开始在游戏中使用它们了.以下部分涵盖 Mecanim 提供的.用于控制及排序动画的主要功能. 二.循环动画片段 使用动画的一个常见操作是确保动画正确循环.例如,表示走路循环的动画片段以类似的姿势开始.结束非常重要(如开始时左脚在地面上,结束时右脚在地面上),这样才能确保不会出现脚滑动或奇怪的不稳定动作.Mecanim为解决此问题提供了方便的工具,让动画片段可基于

unity 对Animator动画系统的研究

unity的新动画系统叫Mecanim,使用Animator来取代旧系统Animation,按Unity文档的惯例:知识点主要分2部分:unity manual和unity script,读者可以边看文章边查阅文档,最好能动手测试. 文章的开始之前,先讲几个基本的知识的: 1.创建动画的一个基本步骤是设置一个unity3d可理解的简化后的骨骼到骨架中实际骨骼的映射:在Mecanim的术语中,这个映射称为Avatar,即avatar是骨骼到骨架的映射. (图片来自网) Avatar主要用于类人骨骼

(六)Unity5.0新特性------新动画功能

?? unity 5.0 中的新动画功能 这里是你可以期待的新动画功能快速概述 ! State Machine Behaviours状态机行为 在Unity 5 中,你会能够将StateMachineBehaviour 脚本添加到您的states,当played状态时能接收callbacks回调: ?OnStateEnter ?OnStateUpdate ?OnStateExit ?OnStateMove ?OnStateIK 在你的状态,您可以创建尽可能多的StateMachineBehavi

logo设计谈

以动物为素材构成的logo设计元素,特别注重动物的特征和动态的表现,这是十分显著的特点.如:鸟的外形特征和飞行动态通过两块黑色衬托出来,翅的两端处理极好,将鸟飞行时的感觉表现得很生动,你甚至于感觉到了鸟的翅膀的颤动!如果翅膀两头没有破开色块,那就显得呆板了.错开的有起伏动感的黑色块,外形独特,是这一枚图形logo设计特有的形象特征.“札幌西区”三角形的底边被两只鸟破开,使图形通气,显得很生动.两只鸟的基本形完全一样,只是大小重复,头部相对,非常有生活情趣.十字色块上的三只鸟也是鸟形的重复,却在简