Animace
This commit is contained in:
368
Game/Editor/AutoAnimatorControllerBuilder.cs
Normal file
368
Game/Editor/AutoAnimatorControllerBuilder.cs
Normal file
@@ -0,0 +1,368 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user