动画控制器中的混合树如何在动画之间平滑切换?

Posted

技术标签:

【中文标题】动画控制器中的混合树如何在动画之间平滑切换?【英文标题】:Blend tree in animator controller how can I change between animations smooth? 【发布时间】:2021-08-15 04:03:33 【问题描述】:

这是混合树的屏幕截图。顶部的第一个动画是空闲的,在左侧底部有一个 Forward,我还添加了一个 Forward 参数。这个前锋控制玩家的移动速度。

通过将此脚本附加到播放器,我正在控制 Forward 参数并减慢播放器的移动速度。

using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;

public class PlayerSpaceshipAreaColliding : MonoBehaviour

    public float rotationSpeed =250;
    public float movingSpeed;
    public float secondsToRotate;
    public GameObject uiSceneText;
    public TextMeshProUGUI textMeshProUGUI;

    private float timeElapsed = 0;
    private float lerpDuration = 3;
    private float startValue = 1;
    private float endValue = 0;
    private float valueToLerp = 0;
    private Animator playerAnimator;
    private bool exitSpaceShipSurroundingArea = false;
    private bool slowd = true;
    private bool startRotatingBack = false;
    private bool displayText = true;

    private float desiredRot;
    public float damping = 10;
    private Rigidbody playerRigidbody;

    // Start is called before the first frame update
    void Start()
    
        playerAnimator = GetComponent<Animator>();
        playerRigidbody = GetComponent<Rigidbody>();

        desiredRot = transform.eulerAngles.y;
    

    // Update is called once per frame
    void Update()
    
        if (exitSpaceShipSurroundingArea)
        
            if (slowd)
                SlowDown();

            if (playerAnimator.GetFloat("Forward") == 0)
            
                slowd = false;

                LockController.PlayerLockState(false);

                if (displayText)
                
                    uiSceneText.SetActive(true);

                    if (textMeshProUGUI.text != "")
                        textMeshProUGUI.text = "";

                    textMeshProUGUI.text = "I can see something very far in the distance, but it's too long to walk by foot.";

                    StartCoroutine(UITextWait());

                    displayText = false;
                

            
        
    

    IEnumerator UITextWait()
    
        yield return new WaitForSeconds(5f);

        textMeshProUGUI.text = "";
        uiSceneText.SetActive(false);
        startRotatingBack = true;
    

    private void OnTriggerEnter(Collider other)
    
        if (other.name == "CrashLandedShipUpDown")
        
            exitSpaceShipSurroundingArea = false;
            Debug.Log("Entered Spaceship Area !");
        
    

    private void OnTriggerExit(Collider other)
    
        if (other.name == "CrashLandedShipUpDown")
        
            exitSpaceShipSurroundingArea = true;
            Debug.Log("Exited Spaceship Area !");
        
    

    private void SlowDown()
    
        if (timeElapsed < lerpDuration)
        
            valueToLerp = Mathf.Lerp(startValue, endValue, timeElapsed / lerpDuration);
            playerAnimator.SetFloat("Forward", valueToLerp);
            timeElapsed += Time.deltaTime;
        

        playerAnimator.SetFloat("Forward", valueToLerp);
        valueToLerp = 0;
    

问题是当 Forward 的值变为 0 并且玩家停止跳到混合树中的空闲动画并且不平滑地切换到它时。

我用红色圆圈标记的检查器中的空闲动画(更改动画速度)的值与其余部分一样为 1 但因为它有点跳到空闲我将其更改为 0.5 但现在整个空闲动画是播放太慢,就像慢动作一样。

我不知道为什么它从减速到空闲的变化看起来像是在切割跳跃而不是平滑地切换到空闲动画?

【问题讨论】:

我找到了解决方案。相反,在脚本 ThirdPersonUserControl 中,像在 LockController.PlayerLockState(false); 行中那样禁用它我刚刚向 ThirdPersonUserControl 脚本添加了一个公共静态布尔值,当它为真时,它不允许使用 FixedUpdate 中的输入。现在效果很好。 如果您认为您找到了解决此类问题的方法,请不要发表评论,但请正确回答您的问题,以便 a)人们可以看到这已经有了答案,并且 b)有类似问题的人可以找到更好的解决方案 【参考方案1】:

对我有用的解决方案。

在脚本 ThirdPersonUserControl 内部,而不是像我在 LockController.PlayerLockState(false); 行中那样禁用整个脚本我刚刚添加了一个公共静态布尔值,通过将布尔值设置为 true,它将避免在脚本中使用输入:

新的 bool 变量是名称 stop :

using System;
using UnityEngine;
using UnityStandardAssets.CrossPlatformInput;

namespace UnityStandardAssets.Characters.ThirdPerson

    [RequireComponent(typeof (ThirdPersonCharacter))]
    public class ThirdPersonUserControl : MonoBehaviour
    
        private ThirdPersonCharacter m_Character; // A reference to the ThirdPersonCharacter on the object
        private Transform m_Cam;                  // A reference to the main camera in the scenes transform
        private Vector3 m_CamForward;             // The current forward direction of the camera
        private Vector3 m_Move;
        private bool m_Jump;                      // the world-relative desired move direction, calculated from the camForward and user input.

        public static bool stop = false;

        
        private void Start()
        
            // get the transform of the main camera
            if (Camera.main != null)
            
                m_Cam = Camera.main.transform;
            
            else
            
                Debug.LogWarning(
                    "Warning: no main camera found. Third person character needs a Camera tagged \"MainCamera\", for camera-relative controls.", gameObject);
                // we use self-relative controls in this case, which probably isn't what the user wants, but hey, we warned them!
            

            // get the third person character ( this should never be null due to require component )
            m_Character = GetComponent<ThirdPersonCharacter>();
        


        private void Update()
        
            if (!m_Jump)
            
                m_Jump = CrossPlatformInputManager.GetButtonDown("Jump");
            
        


        // Fixed update is called in sync with physics
        private void FixedUpdate()
        
            if (stop == false)
            
                // read inputs
                float h = CrossPlatformInputManager.GetAxis("Horizontal");
                float v = CrossPlatformInputManager.GetAxis("Vertical");
                bool crouch = Input.GetKey(KeyCode.C);

                // calculate move direction to pass to character
                if (m_Cam != null)
                
                    // calculate camera relative direction to move:
                    m_CamForward = Vector3.Scale(m_Cam.forward, new Vector3(1, 0, 1)).normalized;
                    m_Move = v * m_CamForward + h * m_Cam.right;
                
                else
                
                    // we use world-relative directions in the case of no main camera
                    m_Move = v * Vector3.forward + h * Vector3.right;
                
#if !MOBILE_INPUT
                // walk speed multiplier
                if (Input.GetKey(KeyCode.LeftShift)) m_Move *= 0.5f;
#endif

                // pass all parameters to the character control script
                m_Character.Move(m_Move, crouch, m_Jump);
                m_Jump = false;
            
        
    

然后在我的脚本中,播放器减速停止然后不动,它平滑地变为空闲动画,因为它已经在混合树中这样做了问题不在于混合树,而在于我尝试过的方式锁定玩家移动。

using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityStandardAssets.Characters.ThirdPerson;

public class PlayerSpaceshipAreaColliding : MonoBehaviour

    public float rotationSpeed =250;
    public float movingSpeed;
    public float secondsToRotate;
    public GameObject uiSceneText;
    public TextMeshProUGUI textMeshProUGUI;

    private float timeElapsed = 0;
    private float lerpDuration = 3;
    private float startValue = 1;
    private float endValue = 0;
    private float valueToLerp = 0;
    private Animator playerAnimator;
    private bool exitSpaceShipSurroundingArea = false;
    private bool slowd = true;
    private bool startRotatingBack = false;
    private bool displayText = true;

    public float damping = 10;
    private Quaternion playerRotation;

    // Start is called before the first frame update
    void Start()
    
        playerAnimator = GetComponent<Animator>();
    

    // Update is called once per frame
    void Update()
    
        if (exitSpaceShipSurroundingArea)
        
            if (slowd)
                SlowDown();

            if (playerAnimator.GetFloat("Forward") == 0)
            
                slowd = false;

                ThirdPersonUserControl.stop = true;

                if (displayText)
                
                    uiSceneText.SetActive(true);

                    if (textMeshProUGUI.text != "")
                        textMeshProUGUI.text = "";

                    textMeshProUGUI.text = "I can see something very far in the distance, but it's too long to walk by foot.";
                    StartCoroutine(UITextWait());
                    displayText = false;
                
            

            if (startRotatingBack)
            
                transform.rotation = Quaternion.RotateTowards(transform.rotation, playerRotation, rotationSpeed * Time.deltaTime);
            
        
    

    IEnumerator UITextWait()
    
        yield return new WaitForSeconds(5f);

        textMeshProUGUI.text = "";
        uiSceneText.SetActive(false);
        startRotatingBack = true;
    

    private void OnTriggerEnter(Collider other)
    
        if (other.name == "CrashLandedShipUpDown")
        
            exitSpaceShipSurroundingArea = false;
            Debug.Log("Entered Spaceship Area !");
        
    

    private void OnTriggerExit(Collider other)
    
        if (other.name == "CrashLandedShipUpDown")
        
            playerRotation = transform.rotation;

            exitSpaceShipSurroundingArea = true;
            Debug.Log("Exited Spaceship Area !");
        
    

    private void SlowDown()
    
        if (timeElapsed < lerpDuration)
        
            valueToLerp = Mathf.Lerp(startValue, endValue, timeElapsed / lerpDuration);
            playerAnimator.SetFloat("Forward", valueToLerp);
            timeElapsed += Time.deltaTime;
        

        playerAnimator.SetFloat("Forward", valueToLerp);
        valueToLerp = 0;
    

【讨论】:

以上是关于动画控制器中的混合树如何在动画之间平滑切换?的主要内容,如果未能解决你的问题,请参考以下文章

在选项卡之间切换时如何创建交叉溶解动画

[OpenGL] 骨骼动画混合效果

如何在带有动画的图像之间切换?

如何在不同设备的动画文本之间切换? html/css

unity-动画状态机Animator

CSS关键帧动画中的平滑旋转