388 lines
17 KiB
C#
388 lines
17 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);
|
|
move.speedParameterActive = true;
|
|
move.speedParameter = "MoveSpeedNormalized";
|
|
|
|
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
|
|
// Jump transitions from grounded states
|
|
var idleToJump = idle.AddTransition(jumpBeginState);
|
|
idleToJump.hasExitTime = false;
|
|
idleToJump.duration = 0.05f;
|
|
idleToJump.AddCondition(AnimatorConditionMode.If, 0f, "IsJumping");
|
|
idleToJump.AddCondition(AnimatorConditionMode.IfNot, 0f, "IsGrounded");
|
|
|
|
var moveToJump = move.AddTransition(jumpBeginState);
|
|
moveToJump.hasExitTime = false;
|
|
moveToJump.duration = 0.05f;
|
|
moveToJump.AddCondition(AnimatorConditionMode.If, 0f, "IsJumping");
|
|
moveToJump.AddCondition(AnimatorConditionMode.IfNot, 0f, "IsGrounded");
|
|
|
|
if (crouch != null)
|
|
{
|
|
var crouchToJump = crouch.AddTransition(jumpBeginState);
|
|
crouchToJump.hasExitTime = false;
|
|
crouchToJump.duration = 0.05f;
|
|
crouchToJump.AddCondition(AnimatorConditionMode.If, 0f, "IsJumping");
|
|
crouchToJump.AddCondition(AnimatorConditionMode.IfNot, 0f, "IsGrounded");
|
|
}
|
|
|
|
// 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("MoveSpeedNormalized", AnimatorControllerParameterType.Float);
|
|
controller.AddParameter("IsGrounded", AnimatorControllerParameterType.Bool);
|
|
controller.AddParameter("IsCrouching", AnimatorControllerParameterType.Bool);
|
|
controller.AddParameter("IsDead", AnimatorControllerParameterType.Bool);
|
|
controller.AddParameter("IsJumping", AnimatorControllerParameterType.Bool);
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|