0
\$\begingroup\$

In my game, I use a Generic Rig model called "hero_generic".

I have a pistol-shooting animation. This animation was created for another rig, so I need to use Humanoid retargetting to use it on my model.

To try out how it looks, I copy my "hero_generic" character, name it "hero_humanoid" and create a Humanoid avatar for it. Then I set the animation to "Humanoid", put it on an animation controller and put the animation controller onto the "hero_humanoid".

It works, the animation is now playing on my Humanoid character, but I don't want my character to be Humanoid.

I want to bake the animation out for my hero_generic character. Unity doesn't provide any built-in option to do that.

How can I turn this retargetted animation to a generic .anim file?

\$\endgroup\$
1
\$\begingroup\$

I put a script on my "hero_generic" that copies the position and rotation of each bone of "hero_humanoid" to each bone of "hero_generic". This way, I have the animation playing on my generic rig. :-)

Now I want to save the generic animation from "hero_generic" to an anim file.

To do that, I use the following script:

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.Animations;
using UnityEngine;

public class TransferAnimation : MonoBehaviour
{
    public Transform HumanoidModelWhoseAnimationWillBeCopiedToThisModel;
    private Animator _OtherAnimator;

    public AnimationClip clip;

    private GameObjectRecorder _Recorder;

    void Start()
    {
        // Create recorder and record the script GameObject.
        _Recorder = new GameObjectRecorder(gameObject);

        // Bind all the Transforms on the GameObject and all its children.
        _Recorder.BindComponentsOfType<Transform>(gameObject, true);

        _OtherAnimator = HumanoidModelWhoseAnimationWillBeCopiedToThisModel.gameObject.GetComponent<Animator>();
    }

    private void LateUpdate()
    {
        //wir können keine animation aufnehmen, während ein animator das modell bewegt
        //also machen wir es so:
        //wir übertragen einfach die animation (siehe unten... über position und rotate) auf ein anderes modell
        //auf diese weise braucht das andere modell keine animation, und wir können die animation aufnehmen
        //wir machen sowohl die animation als auch dieses modell humanoid

        List<Transform> nMyTransforms = new List<Transform>();
        Helpers.GetAllChildren(this.transform, ref nMyTransforms);

        List<Transform> nHumanoidTransforms = new List<Transform>();
        Helpers.GetAllChildren(HumanoidModelWhoseAnimationWillBeCopiedToThisModel, ref nHumanoidTransforms);

        //we also need to process the main transform, not only its bones
        Undo.RecordObject(HumanoidModelWhoseAnimationWillBeCopiedToThisModel, "Inspector"); //Bone rotations done by a script are not recorded. Is there a trick to record these bone rotations via script anyways? For example using something like (pseudo-code) this? -> Undo.RecordObject(nTargetTransform, "Inspector");
        transform.localRotation = HumanoidModelWhoseAnimationWillBeCopiedToThisModel.localRotation;
        transform.position = HumanoidModelWhoseAnimationWillBeCopiedToThisModel.position;//auch die position ist wichtig!!! z. B. wenn sich jemand duckt beim zielen!!

        foreach (Transform nHumanoidTransform in nHumanoidTransforms)
        {
            foreach (Transform nMyGenericTransform in nMyTransforms)
            {
                if (nHumanoidTransform.name == nMyGenericTransform.name)
                {
                    Undo.RecordObject(nHumanoidTransform, "Inspector"); //Bone rotations done by a script are not recorded. Is there a trick to record these bone rotations via script anyways? For example using something like (pseudo-code) this? -> Undo.RecordObject(nTargetTransform, "Inspector");
                    nMyGenericTransform.localRotation = nHumanoidTransform.localRotation;
                    nMyGenericTransform.position = nHumanoidTransform.position;//auch die position ist wichtig!!! z. B. wenn sich jemand duckt beim zielen!!

                    break;
                }
            }
        }

        if (_OtherAnimator.GetCurrentAnimatorStateInfo(0).normalizedTime > 1 && !_OtherAnimator.IsInTransition(0))
        {
            //animation has finished playing
            if (_Recorder.isRecording)
            {
                // Save the recorded session to the clip.
                _Recorder.SaveToClip(clip);
                _Recorder.ResetRecording();

                // gameObject.SetActive(false);

            }
        }
        else
        {
            // Take a snapshot and record all the bindings values for this frame.
            _Recorder.TakeSnapshot(Time.deltaTime);
        }
    }

    public static void GetAllChildren(Transform parent, ref List<Transform> transforms)
    {
        foreach (Transform t in parent)
        {
            transforms.Add(t);
            GetAllChildren(t, ref transforms);
        }
    }
}

It is important to note that the .anim file will only be written after Unity has been closed. Before that, the .anim's file size will be like 2 KB only.

Also, it is of course necessary that the animation is played only once, so I should not connect 2 of the same animations like I sometimes to do create an endless animation.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .