Files
megakoop/Game/Editor/AutoAnimatorControllerBuilder.cs
Dominik G. 96d50bfad5 Animace
2025-10-26 14:26:09 +01:00

369 lines
16 KiB
C#

using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEditor.Animations;
using UnityEngine;
namespace MegaKoop.Game.EditorExtensions
{
public class AutoAnimatorControllerBuilder : EditorWindow
{
private const string ANIM_BASE_PATH = "Assets/Kevin Iglesias/Human Animations/Animations/Male";
private string controllerSavePath = "Assets/Game/ThirdPersonController.controller";
private bool assignToSelected = true;
private AnimationClip idleClip;
private AnimationClip walkForward, walkBackward, walkLeft, walkRight;
private AnimationClip walkForwardLeft, walkForwardRight, walkBackwardLeft, walkBackwardRight;
private AnimationClip crouchIdle;
private AnimationClip jumpBegin, jumpFall, jumpLand;
private AnimationClip deathClip;
[MenuItem("MegaKoop/Animator/Auto Build Controller")]
public static void ShowWindow()
{
var window = GetWindow<AutoAnimatorControllerBuilder>(true, "Auto Animator Builder");
window.minSize = new Vector2(500, 650);
window.Show();
}
private void OnEnable()
{
AutoDiscoverAnimations();
}
private void OnGUI()
{
EditorGUILayout.LabelField("Automatický Animator Controller Builder", EditorStyles.boldLabel);
EditorGUILayout.Space(6);
EditorGUILayout.HelpBox(
"Tento nástroj automaticky najde animace ve složce Kevin Iglesias a vytvoří kompletní Animator Controller.\n" +
"Stiskněte 'Najít Animace' pro aktualizaci nebo 'Vytvořit Controller' pro vygenerování.",
MessageType.Info);
EditorGUILayout.Space(8);
if (GUILayout.Button("Najít Animace", GUILayout.Height(30)))
{
AutoDiscoverAnimations();
}
EditorGUILayout.Space(8);
EditorGUILayout.LabelField("Nalezené Animace:", EditorStyles.boldLabel);
DrawClipField("Idle", idleClip);
DrawClipField("Walk Forward", walkForward);
DrawClipField("Walk Backward", walkBackward);
DrawClipField("Walk Left", walkLeft);
DrawClipField("Walk Right", walkRight);
DrawClipField("Walk Forward Left", walkForwardLeft);
DrawClipField("Walk Forward Right", walkForwardRight);
DrawClipField("Walk Backward Left", walkBackwardLeft);
DrawClipField("Walk Backward Right", walkBackwardRight);
DrawClipField("Crouch Idle", crouchIdle);
DrawClipField("Jump Begin", jumpBegin);
DrawClipField("Jump Fall", jumpFall);
DrawClipField("Jump Land", jumpLand);
DrawClipField("Death", deathClip);
EditorGUILayout.Space(10);
EditorGUILayout.LabelField("Nastavení:", EditorStyles.boldLabel);
assignToSelected = EditorGUILayout.Toggle("Přiřadit na vybraný objekt", assignToSelected);
EditorGUILayout.BeginHorizontal();
controllerSavePath = EditorGUILayout.TextField("Cesta uložení", controllerSavePath);
if (GUILayout.Button("...", GUILayout.Width(32)))
{
string path = EditorUtility.SaveFilePanelInProject(
"Uložit Animator Controller",
"ThirdPersonController",
"controller",
"Vyberte umístění pro Animator Controller");
if (!string.IsNullOrEmpty(path)) controllerSavePath = path;
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space(12);
using (new EditorGUI.DisabledScope(!CanBuild()))
{
if (GUILayout.Button("Vytvořit Animator Controller", GUILayout.Height(40)))
{
BuildController();
}
}
if (!CanBuild())
{
EditorGUILayout.HelpBox("Pro vytvoření controlleru musí být nalezeny minimálně: Idle, Walk Forward/Backward/Left/Right, Jump Begin/Fall/Land.", MessageType.Warning);
}
}
private void DrawClipField(string label, AnimationClip clip)
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField(label, GUILayout.Width(150));
GUI.enabled = false;
EditorGUILayout.ObjectField(clip, typeof(AnimationClip), false);
GUI.enabled = true;
EditorGUILayout.EndHorizontal();
}
private void AutoDiscoverAnimations()
{
Debug.Log("Hledám animace v Kevin Iglesias složce...");
idleClip = LoadAnimationClip("Idles/HumanM@Idle01.fbx");
walkForward = LoadAnimationClip("Movement/Walk/HumanM@Walk01_Forward.fbx");
walkBackward = LoadAnimationClip("Movement/Walk/HumanM@Walk01_Backward.fbx");
walkLeft = LoadAnimationClip("Movement/Walk/HumanM@Walk01_Left.fbx");
walkRight = LoadAnimationClip("Movement/Walk/HumanM@Walk01_Right.fbx");
walkForwardLeft = LoadAnimationClip("Movement/Walk/HumanM@Walk01_ForwardLeft.fbx");
walkForwardRight = LoadAnimationClip("Movement/Walk/HumanM@Walk01_ForwardRight.fbx");
walkBackwardLeft = LoadAnimationClip("Movement/Walk/HumanM@Walk01_BackwardLeft.fbx");
walkBackwardRight = LoadAnimationClip("Movement/Walk/HumanM@Walk01_BackwardRight.fbx");
crouchIdle = LoadAnimationClip("Movement/Crouch/HumanM@Crouch01_Idle.fbx");
jumpBegin = LoadAnimationClip("Movement/Jump/HumanM@Jump01 - Begin.fbx");
jumpFall = LoadAnimationClip("Movement/Jump/HumanM@Fall01.fbx");
jumpLand = LoadAnimationClip("Movement/Jump/HumanM@Jump01 - Land.fbx");
deathClip = LoadAnimationClip("Combat/HumanM@Death01.fbx");
Debug.Log("Hledání dokončeno.");
Repaint();
}
private AnimationClip LoadAnimationClip(string relativePath)
{
string fullPath = Path.Combine(ANIM_BASE_PATH, relativePath).Replace("\\", "/");
// Load all assets from the FBX file
var assets = AssetDatabase.LoadAllAssetsAtPath(fullPath);
if (assets == null || assets.Length == 0)
{
Debug.LogWarning($"Nelze najít assets v: {fullPath}");
return null;
}
// Find animation clips (excluding preview clips)
var clips = new List<AnimationClip>();
foreach (var asset in assets)
{
if (asset is AnimationClip clip && !clip.name.Contains("__preview__"))
{
clips.Add(clip);
}
}
if (clips.Count == 0)
{
Debug.LogWarning($"Žádné AnimationClip v: {fullPath}");
return null;
}
// Return the first valid clip
var result = clips[0];
Debug.Log($"Načteno: {result.name} z {fullPath}");
return result;
}
private bool CanBuild()
{
return idleClip != null &&
walkForward != null && walkBackward != null && walkLeft != null && walkRight != null &&
jumpBegin != null && jumpFall != null && jumpLand != null &&
!string.IsNullOrEmpty(controllerSavePath);
}
private void BuildController()
{
Debug.Log("=== Vytváření Animator Controller ===");
Debug.Log($"Idle: {(idleClip != null ? idleClip.name : "CHYBÍ")}");
Debug.Log($"Walk Forward: {(walkForward != null ? walkForward.name : "CHYBÍ")}");
Debug.Log($"Walk Backward: {(walkBackward != null ? walkBackward.name : "CHYBÍ")}");
Debug.Log($"Walk Left: {(walkLeft != null ? walkLeft.name : "CHYBÍ")}");
Debug.Log($"Walk Right: {(walkRight != null ? walkRight.name : "CHYBÍ")}");
Debug.Log($"Jump Begin: {(jumpBegin != null ? jumpBegin.name : "CHYBÍ")}");
Debug.Log($"Jump Fall: {(jumpFall != null ? jumpFall.name : "CHYBÍ")}");
Debug.Log($"Jump Land: {(jumpLand != null ? jumpLand.name : "CHYBÍ")}");
var controller = AnimatorController.CreateAnimatorControllerAtPath(controllerSavePath);
AddParameters(controller);
var root = controller.layers[0].stateMachine;
// States
var idle = root.AddState("Idle", new Vector3(250, 50, 0));
idle.motion = idleClip;
root.defaultState = idle;
var move = root.AddState("Move", new Vector3(250, 150, 0));
move.motion = CreateMoveBlendTree(controller);
AnimatorState crouch = null;
if (crouchIdle != null)
{
crouch = root.AddState("Crouch", new Vector3(250, 250, 0));
crouch.motion = crouchIdle;
}
var jumpBeginState = root.AddState("Jump Begin", new Vector3(500, 50, 0));
jumpBeginState.motion = jumpBegin;
var jumpFallState = root.AddState("Jump Fall", new Vector3(500, 150, 0));
jumpFallState.motion = jumpFall;
var jumpLandState = root.AddState("Jump Land", new Vector3(500, 250, 0));
jumpLandState.motion = jumpLand;
AnimatorState death = null;
if (deathClip != null)
{
death = root.AddState("Death", new Vector3(250, 350, 0));
death.motion = deathClip;
}
// Transitions: Idle <-> Move
var idleToMove = idle.AddTransition(move);
idleToMove.hasExitTime = false;
idleToMove.duration = 0.1f;
idleToMove.AddCondition(AnimatorConditionMode.Greater, 0.1f, "Speed");
var moveToIdle = move.AddTransition(idle);
moveToIdle.hasExitTime = false;
moveToIdle.duration = 0.1f;
moveToIdle.AddCondition(AnimatorConditionMode.Less, 0.1f, "Speed");
// Crouch transitions
if (crouch != null)
{
var anyToCrouch = root.AddAnyStateTransition(crouch);
anyToCrouch.hasExitTime = false;
anyToCrouch.duration = 0.1f;
anyToCrouch.AddCondition(AnimatorConditionMode.If, 0f, "IsCrouching");
var crouchToIdle = crouch.AddTransition(idle);
crouchToIdle.hasExitTime = false;
crouchToIdle.duration = 0.1f;
crouchToIdle.AddCondition(AnimatorConditionMode.IfNot, 0f, "IsCrouching");
crouchToIdle.AddCondition(AnimatorConditionMode.Less, 0.1f, "Speed");
var crouchToMove = crouch.AddTransition(move);
crouchToMove.hasExitTime = false;
crouchToMove.duration = 0.1f;
crouchToMove.AddCondition(AnimatorConditionMode.IfNot, 0f, "IsCrouching");
crouchToMove.AddCondition(AnimatorConditionMode.Greater, 0.1f, "Speed");
}
// Jump transitions: AnyState -> Jump Begin
var anyToJumpBegin = root.AddAnyStateTransition(jumpBeginState);
anyToJumpBegin.hasExitTime = false;
anyToJumpBegin.duration = 0.05f;
anyToJumpBegin.canTransitionToSelf = false;
anyToJumpBegin.AddCondition(AnimatorConditionMode.If, 0f, "Jump");
// Jump Begin -> Jump Fall (automatic after animation)
var jumpBeginToFall = jumpBeginState.AddTransition(jumpFallState);
jumpBeginToFall.hasExitTime = true;
jumpBeginToFall.exitTime = 0.8f;
jumpBeginToFall.duration = 0.05f;
jumpBeginToFall.hasFixedDuration = true;
// Jump Fall -> Jump Land (when grounded)
var jumpFallToLand = jumpFallState.AddTransition(jumpLandState);
jumpFallToLand.hasExitTime = false;
jumpFallToLand.duration = 0.05f;
jumpFallToLand.hasFixedDuration = true;
jumpFallToLand.AddCondition(AnimatorConditionMode.If, 0f, "IsGrounded");
// Jump Land -> Idle (exit time)
var jumpLandToIdle = jumpLandState.AddTransition(idle);
jumpLandToIdle.hasExitTime = true;
jumpLandToIdle.exitTime = 0.7f;
jumpLandToIdle.duration = 0.1f;
jumpLandToIdle.hasFixedDuration = true;
jumpLandToIdle.AddCondition(AnimatorConditionMode.Less, 0.1f, "Speed");
// Jump Land -> Move (exit time)
var jumpLandToMove = jumpLandState.AddTransition(move);
jumpLandToMove.hasExitTime = true;
jumpLandToMove.exitTime = 0.7f;
jumpLandToMove.duration = 0.1f;
jumpLandToMove.hasFixedDuration = true;
jumpLandToMove.AddCondition(AnimatorConditionMode.Greater, 0.1f, "Speed");
// Death
if (death != null)
{
var anyToDeath = root.AddAnyStateTransition(death);
anyToDeath.hasExitTime = false;
anyToDeath.duration = 0.2f;
anyToDeath.AddCondition(AnimatorConditionMode.If, 0f, "IsDead");
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
Debug.Log($"Animator Controller vytvořen: {controllerSavePath}");
if (assignToSelected && Selection.activeGameObject != null)
{
var go = Selection.activeGameObject;
var anim = go.GetComponent<Animator>();
if (anim == null) anim = go.AddComponent<Animator>();
anim.runtimeAnimatorController = controller;
Debug.Log($"Controller přiřazen na: {go.name}");
}
EditorGUIUtility.PingObject(controller);
}
private void AddParameters(AnimatorController controller)
{
controller.AddParameter("MoveX", AnimatorControllerParameterType.Float);
controller.AddParameter("MoveZ", AnimatorControllerParameterType.Float);
controller.AddParameter("Speed", AnimatorControllerParameterType.Float);
controller.AddParameter("IsGrounded", AnimatorControllerParameterType.Bool);
controller.AddParameter("IsCrouching", AnimatorControllerParameterType.Bool);
controller.AddParameter("IsDead", AnimatorControllerParameterType.Bool);
controller.AddParameter("Jump", AnimatorControllerParameterType.Trigger);
}
private Motion CreateMoveBlendTree(AnimatorController controller)
{
var tree = new BlendTree
{
name = "MoveTree",
blendType = BlendTreeType.FreeformCartesian2D,
useAutomaticThresholds = false,
blendParameter = "MoveX",
blendParameterY = "MoveZ"
};
AssetDatabase.AddObjectToAsset(tree, controller);
var children = new List<ChildMotion>();
if (walkForward != null) children.Add(new ChildMotion { motion = walkForward, position = new Vector2(0f, 1f), timeScale = 1f });
if (walkBackward != null) children.Add(new ChildMotion { motion = walkBackward, position = new Vector2(0f, -1f), timeScale = 1f });
if (walkLeft != null) children.Add(new ChildMotion { motion = walkLeft, position = new Vector2(-1f, 0f), timeScale = 1f });
if (walkRight != null) children.Add(new ChildMotion { motion = walkRight, position = new Vector2(1f, 0f), timeScale = 1f });
if (walkForwardLeft != null) children.Add(new ChildMotion { motion = walkForwardLeft, position = new Vector2(-0.707f, 0.707f), timeScale = 1f });
if (walkForwardRight != null) children.Add(new ChildMotion { motion = walkForwardRight, position = new Vector2(0.707f, 0.707f), timeScale = 1f });
if (walkBackwardLeft != null) children.Add(new ChildMotion { motion = walkBackwardLeft, position = new Vector2(-0.707f, -0.707f), timeScale = 1f });
if (walkBackwardRight != null) children.Add(new ChildMotion { motion = walkBackwardRight, position = new Vector2(0.707f, -0.707f), timeScale = 1f });
tree.children = children.ToArray();
return tree;
}
}
}