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 = ;
/// <summary>
/// 最低的层
/// </summary>
private int lowestLayer = ; /*
* 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 > ) 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 > )
{
// 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);
}
}

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

  1. Animator根运动清除刚体速率问题测试

    在根运动打开时,施加AddForce,速率在后面几帧被清0了: 没有打开根运动AddForce的情况: unity论坛看了下,似乎有人遇到这个问题,而且无解.只能受力状态下关闭根运动 Q:Im wor ...

  2. 根运动 (Root Motion) – 工作原理

    http://blog.csdn.net/myarrow/article/details/45505085 1. 基本概念 在Unity3D动画中,模型的位置.角度经常会发生变化,我们需要决定是否将模 ...

  3. 说说用C语言求根的那些事儿

    C语言--求根:计算机只识别0和1,那么问题来了,作为计算工具如何解决数学问题?其实,计算机是死东西,都是程序员用计算机的的思维去加数学公式计算数学题的.听起来好高端的样子,其实啊,也就那么回事儿, ...

  4. Unity编辑器下获取动画的根运动状态并修改

    我最初想直接修改.anim文件 但通过后来得到的信息,其实根运动状态储存在FBX.meta文件里,转出的.anim文件虽然也有根运动的信息但是算是塌陷过的,无法进行开关操作. 这是我针对有根运动.an ...

  5. python应用-21根火柴游戏

    """ 21跟火柴 """ from random import randint def main(): total=21 while to ...

  6. Unity-Animator深入系列---API详解

    回到 Animator深入系列总目录 测试Unity版本为5.2.1 人形动画的接口都有标注 本列表不包含所有标注为过时的方法 1.Vector3 angularVelocity { get; } [ ...

  7. Unity-Animator深入系列---StateMachineBehaviour状态机脚本学习

    回到 Animator深入系列总目录 首先这个脚本必须继承自StateMachineBehaviour public class MySMB : StateMachineBehaviour { pub ...

  8. Unity-Animator深入系列---deltaPosition&deltaRotation

    回到 Animator深入系列总目录 官方文档给出的信息非常含糊 Gets the avatar delta position for the last evaluated frame. 测试了一下, ...

  9. 官方 Animator 例子解析 Animator.MatchTarget

    一.官方的解释 Animator.MatchTargetSwitch to Manual ); Parameters matchPosition The position we want the bo ...

随机推荐

  1. codeforce 429D. Tricky Function (思维暴力过)

    题目描述 Iahub and Sorin are the best competitive programmers in their town. However, they can't both qu ...

  2. codevs 1226 倒水问题

    1226 倒水问题 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold   题目描述 Description 有两个无刻度标志的水壶,分别可装 x 升和 y 升 ( x, ...

  3. BZOJ2303 APIO2011方格染色

    这题太神了 首先我们可以发现只有当i和j都是偶数时a[1][1]^a[1][j]^a[i][1]^a[i][j]=1才满足情况,其它时都为0 所以我们可以先把i和j都为偶数的地方^1变为0 下面才是最 ...

  4. UVA11019 Martix Matcher --- AC自动机

    UVA11019 Martix Matcher 题目描述: 给定一个\(n*m\)的文本串 问一个\(x*y\)的模式串出现的次数 AC自动机的奇妙使用 将\(x*y\)的模式串拆分成x个串,当x个串 ...

  5. [BZOJ4554][TJOI2016&&HEOI2016]游戏(匈牙利)

    4554: [Tjoi2016&Heoi2016]游戏 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 857  Solved: 506[Sub ...

  6. scanf输入字符串相关

    http://blog.csdn.net/liuhui_8989/article/details/13398793   补充..输入s的时候不要把变量设置成string类型,设置成char数组类型.. ...

  7. Test20171009 考试总结 NOIP模拟赛

    题目难度合适,区分度适中,但是本人水平不佳,没有拿到满意的分数. T1(matrix) 一种比较容易想到的想法是枚举起点求出最长全1串做预处理,这是O(n^2)的. 接着枚举列起点,列终点,通过后缀和 ...

  8. mpdf与fpdf的使用比较

    php扩展 ---mpdf/fpdf 最近用到pdf扩展,需求是生成合同与简历的pdf,可供下载打印 mpdf 首先接触的是mpdf,从源码可以看出mpdf是基于fpdf与html2fpdf的成果. ...

  9. noip2010初赛提高组 试题详解

    转载自:https://blog.csdn.net/eirlys_north/article/details/52889970 一.单项选择题 1.与16进制数 A1.2等值的10进制数是 ( ) A ...

  10. iOS开发系列--通讯录、蓝牙、

    iOS开发过程中有时候难免会使用iOS内置的一些应用软件和服务,例如QQ通讯录.微信电话本会使用iOS的通讯录,一些第三方软件会在应用内发送短信等.今天将和大家一起学习如何使用系统应用.使用系统服务: ...