Problem: It's too many steps to import .fbx files with animation clips into Unity.
When importing .fbx files with animation clips into Unity, an animator controller must be made, then the animation state must be created, then the transition from the entry (or whichever) state to the animation clip must be applied--this all needs to be done manually.
This process wastes a lot of time when done repetitively, can be optimized.
Solution: Automize creation of animator controller, animation state, transition & motion assignment with C# scripting.
Skills: Unity, Blender, C#
The code
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditor.Animations;
using System.IO;
public class AnimationImporter : AssetPostprocessor
{
// Purpose: Turn on looping for animation clips
// Note: This function can also be used to automize other import settings
// in the future if desired
void OnPreprocessAnimation()
{
ModelImporter importer = assetImporter as ModelImporter;
var animations = importer.defaultClipAnimations; // Source animations
if (animations.Length == 0)
{
return;
}
// Turn looping on for animation clips
// -Comment this out if you don't want looping on
foreach (var clip in animations)
{
clip.loopTime = true;
}
// Assign clip animations back source animations, finalize changes
importer.clipAnimations = animations;
}
// Purpose: Automatically create animator controller, state, and transition
// for imported animation clip
void OnPostprocessAnimation(GameObject root, AnimationClip clip)
{
if (clip == null || root == null) return;
// Clean up naming convention from Blender (remove illegals)
string clipName = clip.name;
string safeName = SanitizeFileName(clipName);
string clipPath = assetPath;
// The folder/filepath that controllers will be saved to
string controllerPath = $"Assets/Mecanim/{safeName}_Controller.controller";
// Delay controller creation until after import is fully finished
EditorApplication.delayCall += () =>
{
// Uncomment the line below if you are having trouble finding the controllers
// Debug.Log($"Creating controller at: {controllerPath}");
// Ensure Mecanim folder exists
if (!AssetDatabase.IsValidFolder("Assets/Mecanim"))
AssetDatabase.CreateFolder("Assets", "Mecanim");
// Avoid overwriting if controller already exists
if (AssetDatabase.LoadAssetAtPath<AnimatorController>(controllerPath) != null)
{
Debug.LogWarning($"Animator controller already exists: {controllerPath} — skipping creation.");
return;
}
// Create controller
var controller = AnimatorController.CreateAnimatorControllerAtPath(controllerPath);
var stateMachine = controller.layers[0].stateMachine;
// Re-access animation clip
AnimationClip importedClip = null;
Object[] importedAssets = AssetDatabase.LoadAllAssetsAtPath(clipPath);
foreach (var a in importedAssets)
{
if (a is AnimationClip && a.name == clipName)
{
importedClip = a as AnimationClip;
break;
}
}
// Assign motion as anim clip
if (importedClip != null)
{
// Add state for the clip
var state = stateMachine.AddState(safeName);
state.motion = importedClip;
// Add entry transition
// - Change this if you want a different transition from/to your animation clip
stateMachine.AddEntryTransition(state);
}
else
{
Debug.LogWarning($"Could not find imported clip {clipName} at {clipPath}");
}
};
}
// Purpose: This function removes illegal characters from filenames
private static string SanitizeFileName(string name)
{
char[] invalidChars = Path.GetInvalidFileNameChars();
string sanitized = "";
foreach (char c in name)
{
if (System.Array.Exists(invalidChars, invalidChar => invalidChar == c) || c == '|')
{
Debug.Log($"Illegal character found: '{c}' (ASCII {(int)c}), replacing illegal with _");
sanitized += '_'; // Replace illegals with underscore
}
else
{
sanitized += c;
}
}
Debug.Log($"Final sanitized name: {sanitized}");
return sanitized;
}
}
Note:
If you are getting a warning or warnings “Assets/Editor/CrossPlatformInput/CrossPlatformInputInitialize.cs(96,17): warning CS0618: 'BuildTargetGroup.PSM' is obsolete: 'PSM has been removed in >= 5.3'”
You may delete the “CrossPlatformInput” folder [Assets > Editor > CrossPlatformInput], of course if you are not using it for anything else in your game
Upon import, in OnPreprocessAnimation, looping is turned on for all animations -- this part may be deleted/commented out of desired - it was useful for Gutterborne as most of our animations looped
In OnPostprocessAnimation, the Animator Controller is created on a delay to avoid Unity's rule against creating new assets during the importing of other assets.
Animator Controller with the same name as the imported animation clip is created: "[name of animation clip]_Controller" in Mecanim folder
Note: Blender's naming convention for naming armature animations uses the "|" character to separate words which is not a valid character for name Animator Controllers in Unity. So, the filename of the animation clip is first 'sanitized'. Any illegal characters are replaced with an underscore; "_".
Animation clip is fetched
A state is created for the animation clip
The motion of the new state is set to the animation clip
A state transition is created between the animated state and the Entry state
When creating Gutterborne, I was annoyed with the extra steps needed to import an FBX from Blender into Unity and thought there must be a way to optimize this.
I found a few Unity Discussions about using AssetPostprocessor to hook into Unity's import pipeline -- Bingo!
Then I got to coding...and came up with the script above.
What if a .blend file is imported instead?
In most cases, it is always best practice to export as an FBX when going from Blender -> Unity, as Unity's Blender importer is somewhat unstable.
Behind the scenes, Unity just invokes Blender's FBX exporter anyways.
And if you or others are going to be editing this Blender file or edit the Blender file by accident, it may cause unwanted issues.
[Sources: (Unity Documentation) (bringing in .blend file into Unity - Stack Exchange) (using .blend vs .fbx file in - Unity discussion)]
So, a .blend file should rarely, if ever, be used directly in a Unity project
FBX files that have more than one animation in them
For Gutterborne, we only had one animation clip per model
Coming soon..
Additional credit to: TheKiwiCoder on Youtube [https://www.youtube.com/watch?v=KGp8y1rsKag] and some debugging help from ChatGPT.