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\$
0
\$\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\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.