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(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(); 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(); if (anim == null) anim = go.AddComponent(); 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(); 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; } } }