简体   繁体   中英

Blender to Unity Animation - Jumps In Frames

I decided to try exporting a Blender model in an FBX with animations to Unity. There are two animations, open and close for the top door of this model. Here's the thing - it seems my model likes to jump into the origin of BOTH animations when the model started, ie. if I import the model and the door is open, and I decide to trigger to CLOSE it, it will close properly - but then, when I try to OPEN it, it decides it needs to stay at the SAME origin as the closing door animation first, THEN it opens - basically causing the door to move too far OUTSIDE of the model.

I've also made a point to try leaning on ONE animation in the Mecanim animator control - but it seems while it gets back to the correct place for the door when moving in the opposite direction, it doesn't do it at the same speed, but does so erratically. Finally, when I try to have one door animation transition to the SAME door animation with the opposite speed (ie. door close to door close, one with speed of 1 and one with -1) it still jumps erratically.

Here's the last Mecanim animator setup I attempted:

在此处输入图片说明

This was the approaching of trying to use the same animation in the other state, but negative speed instead of positive. It doesn't work like you would expect.

I'm using state triggers to transition between these states in the code below:

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");
        }
    }
}

Can someone let me know if there's a better way to approach this and how? I've tried jumping between several articles on this, including a demo tutorial on Mecanim for Unity on their website - no dice...

I've had many difficulties with bringing animations from blender into unity. There are a lot of pitfalls. Here are a couple things i would try. let me know if any of these work for you.

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
  • Make sure you are not Applying RootMotion in the unity animator

在此处输入图片说明

The docs for apply root motion 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.
  • Make sure you remove any transform.position keys that are in the animation clip.
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.
  • Minor suggestion. I would use a bool instead of a trigger. That way it would be easier to keep track of the state of the door. (IE base your animation on a "isClosed" bool)

These are just some useful troubleshooting tips i found helpful in the past.

I don't know how your animation clips look like and how your transitions look like but here are a few tips:

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

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

Animator.Play directly "jumps" into the target state without any transition. But the animation is restarted everytime you hit the key.

Triggers get stacked so if you press eg the open button mutliple times and than press the close button, you will make both (or even multiple) transitions between the two states since a Trigger is only reset, if it is used (or reset actively in a script using Animator.ResetTrigger ).

And another issue I see is that currently you use decoupled if statements ... so it is theoretically possible to press all 4 keys at the same time what might leed to some issues (The Play call would directly jump to the target state but the triggers from the before Settrigger calls would be still there and stacked so it is possible that your animator makes various transitions from the next frame on.) You rather shouls use if-else statements to allow only processing one key press at a time.


For a simple door with only two states and no complex animations between those I would suggest a different approach:

  1. Dont export animations from Blender only the model

  2. For a rotating door make sure the pivot is on the axis you ant to rotate around

  3. In the animator have only two states eg Opened | Closed

  4. Instead of Triggers use a bool eg IsOpen

  5. Create your "animations" in the way that both have only exactly 1 keyframe and disable Loop Time in the inspector. Unity interpolates between the two animations automatically so if there is only one keyframe per animation all the interpolation between the values (positions, colors, rotations etc) is handled automatically by Unity itself.

  6. Make your transitions the following way

    • ExitTime -> 1
      We won't use the exit time actually but this fixes a little bug throwing a warning

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

      if you let it on 0

    • HasExittime -> false
      This means don't wait until an animation is finished or at a certain frame to make the transition but do it immeditely (an animation with only keyframe has automatcally a default length of 1 second)
    • FixedDuration -> true (we want to configure stuff in seconds)
    • TransitionDuration(s) -> Here you configure now how long you want the transition to take (it also can be different fo opening and closing)
    • TransitionOffset -> 0 (the animation has only 1 keyframe so we don't want to start it in another frame)

    • and finally your two conditions

      • Open -> Close: Conditions: IsOpen == false
      • Close -> Open: conditions: IsOpen == true

Than later in the code you instead would use

// 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");
    }
}

Now using a Bool value instead of Triggers you don't have to care about stacked calls. I hope this 1 keyframe animations approach fits your requirements.


If you require having multiple "steps" in your opnening and closing process eg because of having some complex unlock animations before the door actually moves, I would recommend to

  • use a state for every step
  • link all of one direction with HasExitTime = true and ExitTime = 1
  • Use the before mentioned setup to interpolate between those states
  • And finally have the two transition conditions between every "step pair" on the way so every animation either transitions to "more open" on IsOpen = true or to "more closed" on IsOpen = false

For better understanding here is an example how a more complex setup might look like only using such 1 keyframe animations

在此处输入图片说明

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM