Blender to Unity Animation - прыжки в кадрах

Я решил попробовать экспортировать модель Blender в FBX с анимацией в Unity. Есть две анимации, открытые и закрытые для верхней двери этой модели. Вот в чем дело - кажется, моя модель любит прыгать в начало ОБА анимаций, когда модель запущена, т.е. если я импортирую модель, и дверь открыта, и я решаю активировать, чтобы ЗАКРЫТЬ ее, она закроется должным образом - но затем, когда я пытаюсь ОТКРЫТЬ ее, она решает, что должна остаться в том же источнике, что и анимация закрывающейся двери во-первых, ТОГДА он открывается - в основном это приводит к тому, что дверь заходит слишком далеко за пределы модели.

Я также постарался опереться на ОДНУ анимацию в элементе управления аниматором Mecanim - но, кажется, что когда он движется в противоположном направлении, он возвращается в нужное место для двери, он не делает это с той же скоростью, но делает это беспорядочно. Наконец, когда я пытаюсь сделать анимацию одной двери на ту же анимацию двери с противоположной скоростью (т. Е. Дверь рядом с дверью закрыта, одна со скоростью 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 для Unity на их веб-сайте - без кубиков...

2 ответа

Решение

Я не знаю, как выглядят ваши анимационные клипы и как выглядят ваши переходы, но вот несколько советов:

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

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

Animator.Play непосредственно "прыгает" в целевое состояние без какого-либо перехода. Но анимация перезапускается каждый раз, когда вы нажимаете клавишу.

Triggers сложить так, если вы нажмете, например, несколько раз кнопку открытия, а затем нажмите кнопку закрытия, вы сделаете оба (или даже несколько) переходов между двумя состояниями, так как Trigger сбрасывается, только если он используется (или активно сбрасывается в скрипте, используя Animator.ResetTrigger).

И еще одна проблема, которую я вижу в том, что в настоящее время вы используете развязанный if заявления... так что теоретически можно нажать все 4 клавиши одновременно, что может привести к некоторым проблемам (Play вызов будет непосредственно переходить в целевое состояние, но триггеры из ранее Settrigger вызовы будут все еще там и сложены, так что возможно, что ваш аниматор делает различные переходы со следующего кадра.) Вы скорее должны использовать if-else операторы, позволяющие обрабатывать только одно нажатие клавиши одновременно.


Для простой двери с двумя состояниями и без сложных анимаций между ними я бы предложил другой подход:

  1. Не экспортируйте анимации из Blender только по модели

  2. Для вращающейся двери убедитесь, что шарнир находится на оси, вокруг которой вы вращаетесь

  3. В аниматоре есть только два состояния, например Opened | Closed

  4. Вместо триггеров используйте bool, например IsOpen

  5. Создайте свою "анимацию" так, чтобы обе 1 ключевой кадр и отключить Loop Time в инспекторе. Unity интерполирует между двумя анимациями автоматически, поэтому, если в анимации имеется только один ключевой кадр, вся интерполяция между значениями (позициями, цветами, поворотами и т. Д.) Автоматически обрабатывается самим Unity.

  6. Сделайте ваши переходы следующим образом

    • ExitTime -> 1
      На самом деле мы не будем использовать время выхода, но это исправляет небольшую ошибку, выдающую предупреждение

      Разница в эффективной длине между государствами слишком велика. Предварительный просмотр перехода будет отключен. UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr)

      если вы позволите это на 0

    • HasExittime -> false
      Это означает, что не нужно ждать завершения анимации или в определенном кадре, чтобы выполнить переход, но делайте это немедленно (анимация с одним ключевым кадром автоматически имеет длительность по умолчанию 1 секунда)
    • FixedDuration -> true (мы хотим настроить вещи в считанные секунды)
    • TransitionDuration(s) -> Здесь вы теперь настраиваете, как долго вы хотите, чтобы переход занимал (он также может быть различным для открытия и закрытия)
    • TransitionOffset -> 0 (анимация имеет только 1 ключевой кадр, поэтому мы не хотим запускать его в другом кадре)

    • и наконец ваши два условия

      • Открыть -> Закрыть: Условия: 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");
    }
}

Теперь используя значение Bool вместо триггеров, вам не нужно заботиться о сложенных вызовах. Я надеюсь, что этот подход с использованием 1 ключевого кадра соответствует вашим требованиям.


Если вам требуется несколько "шагов" в процессе открытия и закрытия, например, из-за наличия сложной анимации разблокировки до того, как дверь фактически сдвинется, я бы порекомендовал

  • использовать состояние для каждого шага
  • связать все в одном направлении с HasExitTime = true а также ExitTime = 1
  • Используйте вышеупомянутую настройку для интерполяции между этими состояниями
  • И, наконец, есть два условия перехода между каждой "парой шагов" на пути, поэтому каждая анимация либо переходит в "более открытую" на IsOpen = true или "более закрыто" на IsOpen = false

Для лучшего понимания здесь приведен пример того, как более сложные настройки могут выглядеть только при использовании такой анимации с 1 ключевым кадром.

У меня было много трудностей с приведением анимаций из блендера в единство. Здесь много подводных камней. Вот пара вещей, которые я бы попробовал. дайте мне знать, если что-то из этого работает для вас.

 При анимации модели в блендере часто случается, что происхождение вашей модели
 случайно перемещен или находится не в том же месте между вашими анимациями
 и когда это импортируется в единство, у него есть способ испортить ваши анимации.
 Так что просто дважды проверьте анимацию и убедитесь, что источник не имеет 
 переключается между двумя анимациями
  • Убедитесь, что вы не применяете RootMotion в аниматоре Unity.



Документы для применения корневого движения здесь

 в основном то, что он делает, если включен для любого движения, которое происходит в вашем
 анимация добавляет любое текущее движение анимации к его текущей позиции
 Поэтому, если корневой объект двери сдвинулся на 2 единицы, когда она открывается, и вы 
 применяя корневое движение, объект будет в 2 единицах от того места, где вы бы
 ожидать. И это будет просто продолжать складываться все дальше и дальше влево.

  • Убедитесь, что вы удалили все ключи transform.position, которые есть в анимационном клипе.
 Я предполагаю, что вы на самом деле не хотите, чтобы положение двери двигалось, но 
 вместо этого просто сделайте так, чтобы анимация выглядела так, будто дверь движется. Если так в этом случае
 Предполагая, что ваш объект настроен правильно. Что на мой взгляд, если бы это была раздвижная дверь, это было бы
 есть дерево объектов, подобное этому RootObject>Frame>Door. В таком случае единственным 
 объект, который должен иметь какие-либо ключи анимации, должен быть объектом двери. другой родитель
 объекты должны быть стационарными.


  • Незначительное предложение. Я бы использовал bool вместо триггера. Таким образом, было бы легче отслеживать состояние двери. (IE основывает вашу анимацию на "isClosed" bool)

Это всего лишь несколько полезных советов по устранению неполадок, которые я нашел полезными в прошлом.

Другие вопросы по тегам