Blender 到 Unity 动画 - 跳帧

Blender to Unity Animation - Jumps In Frames

我决定尝试将带有动画的 FBX 中的 Blender 模型导出到 Unity。此模型的顶门有两个动画,打开和关闭。事情是这样的——我的模型似乎喜欢在模型启动时跳入两个动画的起源,即。如果我导入模型并且门是打开的,并且我决定触发关闭它,它会正常关闭 - 但是,当我尝试打开它时,它决定它需要保持在与关闭门动画相同的原点首先,然后它打开 - 基本上导致门移动到模型之外太远。

我还特意尝试依靠 Mecanim 动画师控件中的一个动画 - 但它似乎在向相反方向移动时回到门的正确位置时,它并没有做它以相同的速度,但这样做不规律。最后,当我尝试将一个门动画转换为具有相反速度的 SAME 门动画时(即门关闭到门关闭,一个速度为 1,一个速度为 -1)它仍然不规律地跳跃。

这是我尝试的最后一个 Mecanim 动画师设置:

这是尝试在其他状态下使用相同动画的方法,但负速度而不是正速度。它不像你期望的那样工作。

我在下面的代码中使用状态触发器在这些状态之间转换:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Playables;

public class TriggerAnimation : MonoBehaviour {

    public Transform cockPit;
    // Use this for initialization
    void Start () {


    }

    // Update is called once per frame
    void Update () {
        if (Input.GetKeyDown("t"))
        {
            //AnimationPlayableUtilities.PlayClip("CockPit_ExtDoor|Open");
            Debug.Log("I'm being pressed");
            //GetComponent<Animator>().SetTrigger("ExtDoorOpen");
            GetComponent<Animator>().SetTrigger("IntDoorOpen");
            //GetComponent<Animator>().SetTrigger("CockPit_Door|Open");

        }
        if (Input.GetKeyDown("u"))
        {
            //PlayableGraph graph = new PlayableGraph();
            //AnimationPlayableUtilities.PlayAnimatorController(GetComponent<Animator>(), GetComponent<RuntimeAnimatorController>(), out graph);
            Debug.Log("I'm being pressed");
            //GetComponent<Animator>().SetTrigger("ExtDoorClose");
            GetComponent<Animator>().SetTrigger("IntDoorClose");
            //GetComponent<Animator>().SetTrigger("CockPit_Door|Close");

        }
        if (Input.GetKeyDown("x"))
        {
            GetComponent<Animation>().Play("CockPit_IntDoor|Open");
        }
        if (Input.GetKeyDown("z"))
        {
            GetComponent<Animation>().Play("CockPit_IntDoor|Close");
        }
    }
}

有人可以告诉我是否有更好的方法来解决这个问题以及如何解决这个问题吗?我试过在几篇关于此的文章之间跳转,包括他们网站上关于 Mecanim for Unity 的演示教程 - 没有骰子...

我在将动画从 blender 整合到 unity 时遇到了很多困难。有很多陷阱。这是我会尝试的几件事。让我知道这些是否对您有用。

 When animating a model in blender it often happens that the origin of  your model
 has accidentally been moved or is not in the exact same place between your two animations
 and when that gets imported into unity it has a way of messing with your animations.
 So just double check between your animations and make sure that the origin has not 
 shifted between the two animations
  • 确保你没有在 unity 动画器中应用 RootMotion



应用根运动的文档 here

 basically what it does if enabled is for any movement that takes place in your
 animation it adds whatever movement transpired in the animation to its current position
 Therefore if the root object of the door moved left by 2units when it opens and your are 
 applying root motion the object would then be 2 units left of where you would
 expect. And it would just continue to stack getting further and further left.

  • 确保删除动画剪辑中的所有 transform.position 键。
 I am assuming that you do not want to actually have the position of the door move but 
 instead just have the animation make it appear as if the door is moving. If so in that case
 Assuming that your object is setup right. Which in my mind if it was a sliding door it would
 have an object tree like this RootObject>Frame>Door. With that being the case the only 
 object that should have any animation keys on it should be the door object. the other parent
 objects should be stationary.


  • 小建议。我会使用布尔值而不是触发器。这样就更容易跟踪门的状态。 (IE 将您的动画基于 "isClosed" 布尔值)

这些只是我过去发现有用的一些有用的故障排除技巧。

我不知道您的动画剪辑长什么样以及过渡效果如何,但这里有一些提示:

if (Input.GetKeyDown("x"))
{
    GetComponent<Animation>().Play("CockPit_IntDoor|Open");
}

if (Input.GetKeyDown("z"))
{
    GetComponent<Animation>().Play("CockPit_IntDoor|Close");
}

Animator.Play直接"jumps"进入目标状态,没有任何转换。但是每次按下按键时动画都会重新开始。

Triggers 会堆叠起来,所以如果您按例如打开按钮多次,而不是按下关闭按钮,您将在两种状态之间进行两种(甚至多次)转换,因为 Trigger 仅在使用时重置(或在脚本中使用 [ 主动重置) =15=]).

我看到的另一个问题是,目前您使用解耦的 if 语句...因此理论上可以同时按下所有 4 个键,这可能会导致一些问题(Play 调用会直接跳转到目标状态,但之前 Settrigger 调用的触发器仍会存在并堆叠,因此您的动画师可能会从下一帧开始进行各种转换。)您宁愿使用 if-else 语句一次只允许处理一个按键。


对于只有两种状态且两者之间没有复杂动画的简单门,我建议采用不同的方法:

  1. 不要仅从模型导出 Blender 动画

  2. 对于旋转门,确保枢轴位于您想要旋转的轴上

  3. 在动画器中只有两种状态,例如Opened | Closed

  4. 使用布尔值代替触发器,例如IsOpen

  5. 创建您的 "animations" 时,两者都只有 1 关键帧并在检查器中禁用 Loop Time。 Unity 会自动在两个动画之间进行插值,因此如果每个动画只有一个关键帧,则所有值(位置、颜色、旋转等)之间的插值都由 Unity 本身自动处理。

  6. 按以下方式进行转换

    • ExitTime -> 1
      我们实际上不会使用退出时间,但这修复了一个抛出警告的小错误

      Difference in effective length between states is too big. Transition preview will be disabled. UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr)

      如果你让它在 0

    • HasExittime -> false
      这意味着不要等到动画完成或在特定帧进行过渡,而是立即进行(只有关键帧的动画自动默认长度为 1 秒)
    • FixedDuration -> true(我们想在几秒钟内完成配置)
    • TransitionDuration(s) -> 在这里你现在配置你希望转换花费多长时间(打开和关闭也可以不同)
    • TransitionOffset -> 0(动画只有一个关键帧所以我们不想在另一帧开始)

    • 最后是你的两个条件

      • 开仓 -> 平仓:条件:IsOpen == false
      • 平仓->开仓:条件:IsOpen == true

稍后在代码中您将使用

// Store the component frerence instead of getting it everytime again
private Animator _animator;

private void Awake()
{
    _animator = GetComponent<Animator>();
}

private void Update()
{
    if (Input.GetKeyDown("t"))
    {
        Debug.Log("t is being pressed");
        _animator.SetBool("IsOpen", true);
    }
    // use if-else in order to process only one of the buttons at a time
    else if (Input.GetKeyDown("u"))
    {
        Debug.Log("u is being pressed");
        _animator.SetBool("IsOpen", false);
    }
    // you still can keep those for jumping to a state directly without the transition
    else if (Input.GetKeyDown("x"))
    {
        _animator.Play("Opened");
    }
    else if (Input.GetKeyDown("z"))
    {
        _animator.Play("Closed");
    }
}

现在使用布尔值而不是触发器,您不必关心堆叠调用。我希望这 1 个关键帧动画方法符合您的要求。


如果您需要在打开和关闭过程中有多个 "steps",例如因为在门实际移动之前有一些复杂的解锁动画,我建议

  • 为每一步使用一个状态
  • link 一个方向全部 HasExitTime = trueExitTime = 1
  • 使用前面提到的设置在这些状态之间进行插值
  • 最后在每个 "step pair" 之间有两个过渡条件,所以每个动画要么在 IsOpen = true 上过渡到 "more open",要么在 [=40 上过渡到 "more closed" =]

为了更好地理解这里有一个示例,一个更复杂的设置可能看起来只使用这样的 1 个关键帧动画