首页 文章

Unity 3D animation.MatchTarget,根运动仅影响位置或仅旋转

提问于
浏览
1

Background

在Unity 3D引擎版本2017.3.1f1中,我是'm working on a 3D character driven by mecanim root motion. I need to rotate the character exactly 180° around the Y-axis (up) so it then walks left or right (it'的2D游戏) . 因为它是mecanim,角度并不总是精确的,角色转弯有时是177°,有时是181° . 这是不必要的错误,导致角色在行走时在Z轴上移动 . 所以我决定使用内置的animator.MatchTarget()函数(https://docs.unity3d.com/ScriptReference/Animator.MatchTarget.html)来修正最终角度 .

Problem

Animator.MatchTarget没有重载函数来匹配旋转,因此我需要输入转动动画片段的最终位置(旋转动画具有根运动和位置变化) . 我假设成员变量Animator.TargetPositon完成这项工作:

// following script is attached to the character rotation Animator state
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GenericState : StateMachineBehaviour {

     public float targetAngle_ = 180.0f;

     override public void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex){

         Quaternion targetRotation_ = Quaternion.Euler(0, targetAngle_, 0); 
         animator.MatchTarget(animator.targetPosition, 
                              targetRotation_, 
                              AvatarTarget.Root, 
                              new MatchTargetWeightMask(Vector3.one, 1f),
                              0f,
                              1f);
     }
}

但它不涉及根运动,因此角色在开始转动动画时结束于确切的位置 . 还有另一个变量Animator.RootPosition(https://docs.unity3d.com/ScriptReference/Animator-rootPosition.html),但它只保存当前的字符位置 .

Workaround

我能想到的唯一解决方案是在编辑器模式下读取动画数据,存储根运动偏移,然后在运行时应用每个动画 . 这个解决方案过于复杂,我正在寻找一种简单的替代方案,“只匹配旋转,并从动画中的根运动中读取目标位置” .

谢谢

1 回答

  • 0

    以下解决方法有效(无需在编辑模式下访问动画曲线) . 请注意使用OnStateMove而不是OnStateUpdate . 重写OnStateMove时,将在状态中忽略根运动,并且必须在此方法中手动应用(https://docs.unity3d.com/ScriptReference/Animator.ApplyBuiltinRootMotion.html) . 不知何故,它比Animator.MatchTarget()做得更好 .

    // following script is attached to the character rotation Animator state
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class GenericState : StateMachineBehaviour {
    
        public bool _SnapEnabled;
    
        Quaternion _enterRotation;
        public float targetAngle_ = 180.0f;
    
        override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex){
            _enterRotation = animator.rootRotation;
        }
    
        override public void OnStateMove(Animator animator, AnimatorStateInfo stateInfo, int layerIndex){
            float normalizedTime_ = Mathf.Clamp(
                                   (stateInfo.normalizedTime - _matchMin) /
                                   (_matchMax - _matchMin), 0f, 1f);
    
            if ( _SnapEnabled && normalizedTime_ > 0 ){
                Quaternion snappedRotation_ = Quaternion.Euler(0, targetAngle_, 0);
                Quaternion targetRotation_ = Quaternion.Lerp(_enterRotation,
                                                  snappedRotation_,
                                                  normalizedTime_);
    
                animator.transform.position = animator.rootPosition;
                animator.transform.rotation = targetRotation_;
            } else {
                animator.ApplyBuiltinRootMotion();
            }
        }
    }
    

相关问题