#if UNITY_EDITOR using System.Linq; using System.Collections.Generic; using UnityEditor; using UnityEditor.Animations; using UnityEngine; public static class BuildZibuAnimator { [MenuItem("Tools/Zibu/Build Animator Controller")] public static void Build() { const string animationsFolder = "Assets/Animations"; const string controllerPath = "Assets/Zibu_Animator.controller"; var allClips = AssetDatabase.FindAssets("t:AnimationClip", new[] { animationsFolder }) .Select(AssetDatabase.GUIDToAssetPath) .Select(AssetDatabase.LoadAssetAtPath) .Where(c => c != null) .ToList(); AnimationClip Find(string contains) => allClips.FirstOrDefault(c => c.name.ToLower().Contains(contains.ToLower())); // Core clips var idle = Find("Zibu_Idle") ?? Find("Idle"); var run = Find("Zibu_Run"); var runFast = Find("Zibu_RunFast"); var sprint = Find("Zibu_Sprint"); var runBackward = Find("Run_Backward"); var jumpStart = Find("JumpStart") ?? Find("Jump_Start_IPC") ?? Find("JumpOriginal"); var airLoop = Find("Falling_Loop") ?? Find("Falling") ?? Find("Fall"); var land = Find("JumpEnd") ?? Find("JumpEnd_Add") ?? Find("JumpEndOld"); var dblJump = Find("DoubleJump_inPlace_Short") ?? Find("DoubleJump_inPlace") ?? Find("DoubleJump"); var kickL = Find("Side_Kick_L"); var kickR = Find("Side_Kick_R"); var charge = Find("Shu_Charge"); var leanL = Find("Run_Lean_L"); var leanR = Find("Run_Lean_R"); // Create controller var controller = AnimatorController.CreateAnimatorControllerAtPath(controllerPath); var rootSM = controller.layers[0].stateMachine; // Parameters controller.AddParameter("Speed", AnimatorControllerParameterType.Float); controller.AddParameter("Turn", AnimatorControllerParameterType.Float); controller.AddParameter("IsGrounded", AnimatorControllerParameterType.Bool); controller.AddParameter("Jump", AnimatorControllerParameterType.Trigger); controller.AddParameter("DoubleJump", AnimatorControllerParameterType.Trigger); controller.AddParameter("Land", AnimatorControllerParameterType.Trigger); controller.AddParameter("AttackType", AnimatorControllerParameterType.Int); controller.AddParameter("Hit", AnimatorControllerParameterType.Trigger); // States var idleState = rootSM.AddState("Idle"); idleState.motion = idle; // Locomotion blend tree var locomotion = rootSM.AddState("Locomotion"); var bt = new BlendTree { name = "BT_Locomotion", blendParameter = "Speed", useAutomaticThresholds = false }; AssetDatabase.AddObjectToAsset(bt, controller); locomotion.motion = bt; var motions = new List<(AnimationClip clip, float thr)> { (idle, 0f), (run, 1f), (runFast, 2f), (sprint, 3f), }.Where(m => m.clip != null).ToList(); bt.children = motions.Select(m => new ChildMotion { motion = m.clip, threshold = m.thr }).ToArray(); // Backward (optional) AnimatorState backwardState = null; if (runBackward != null) { backwardState = rootSM.AddState("RunBackward"); backwardState.motion = runBackward; } // Jump sub-state machine var jumpSM = rootSM.AddStateMachine("Jump"); var stJumpStart = jumpSM.AddState("JumpStart"); stJumpStart.motion = jumpStart; var stAirLoop = jumpSM.AddState("AirLoop"); stAirLoop.motion = airLoop; var stDouble = jumpSM.AddState("DoubleJump"); stDouble.motion = dblJump; var stLand = jumpSM.AddState("Land"); stLand.motion = land; // Entry → JumpStart jumpSM.defaultState = stJumpStart; stJumpStart.AddTransition(stAirLoop).hasExitTime = true; // AirLoop → DoubleJump (on trigger) var tAirToDouble = stAirLoop.AddTransition(stDouble); tAirToDouble.hasExitTime = false; tAirToDouble.AddCondition(AnimatorConditionMode.If, 0, "DoubleJump"); // AirLoop → Land (when grounded) var tAirToLand = stAirLoop.AddTransition(stLand); tAirToLand.hasExitTime = false; tAirToLand.AddCondition(AnimatorConditionMode.If, 0, "IsGrounded"); // Land → Exit var tLandExit = stLand.AddExitTransition(); tLandExit.hasExitTime = true; tLandExit.exitTime = 0.9f; // Base transitions // Idle <-> Locomotion var tIdleToLoc = idleState.AddTransition(locomotion); tIdleToLoc.hasExitTime = false; tIdleToLoc.AddCondition(AnimatorConditionMode.Greater, 0.1f, "Speed"); var tLocToIdle = locomotion.AddTransition(idleState); tLocToIdle.hasExitTime = false; tLocToIdle.AddCondition(AnimatorConditionMode.Less, 0.1f, "Speed"); // Backward if (backwardState != null) { var tLocToBack = locomotion.AddTransition(backwardState); tLocToBack.hasExitTime = false; tLocToBack.AddCondition(AnimatorConditionMode.Less, -0.1f, "Speed"); var tBackToLoc = backwardState.AddTransition(locomotion); tBackToLoc.hasExitTime = false; tBackToLoc.AddCondition(AnimatorConditionMode.Greater, 0.1f, "Speed"); } // AnyState → Jump (trigger) var tAnyToJump = rootSM.AddAnyStateTransition(jumpSM.defaultState); tAnyToJump.hasExitTime = false; tAnyToJump.AddCondition(AnimatorConditionMode.If, 0, "Jump"); // Jump Land → Locomotion when grounded var tLandToLoc = stLand.AddTransition(locomotion); tLandToLoc.hasExitTime = false; tLandToLoc.AddCondition(AnimatorConditionMode.If, 0, "IsGrounded"); // Attack Upper Body Layer var upper = new AnimatorControllerLayer { name = "UpperBody", defaultWeight = 1f, stateMachine = new AnimatorStateMachine() }; AssetDatabase.AddObjectToAsset(upper.stateMachine, controller); controller.AddLayer(upper); var smU = upper.stateMachine; if (kickL != null) smU.AddState("SideKickL").motion = kickL; if (kickR != null) smU.AddState("SideKickR").motion = kickR; if (charge != null) smU.AddState("Charge").motion = charge; // Simple routing by AttackType if (kickL != null) { var t = smU.AddAnyStateTransition(smU.states.First(s => s.state.name == "SideKickL").state); t.hasExitTime = false; t.AddCondition(AnimatorConditionMode.Equals, 1, "AttackType"); } if (kickR != null) { var t = smU.AddAnyStateTransition(smU.states.First(s => s.state.name == "SideKickR").state); t.hasExitTime = false; t.AddCondition(AnimatorConditionMode.Equals, 2, "AttackType"); } if (charge != null) { var t = smU.AddAnyStateTransition(smU.states.First(s => s.state.name == "Charge").state); t.hasExitTime = false; t.AddCondition(AnimatorConditionMode.Equals, 3, "AttackType"); } // Optional Lean Additive Layer if (leanL != null && leanR != null) { var leanLayer = new AnimatorControllerLayer { name = "LeanAdd", defaultWeight = 1f, stateMachine = new AnimatorStateMachine(), blendingMode = AnimatorLayerBlendingMode.Additive }; AssetDatabase.AddObjectToAsset(leanLayer.stateMachine, controller); controller.AddLayer(leanLayer); var smLean = leanLayer.stateMachine; var btLean = new BlendTree { name = "BT_Lean", blendParameter = "Turn", useAutomaticThresholds = false }; AssetDatabase.AddObjectToAsset(btLean, controller); btLean.children = new[] { new ChildMotion { motion = leanL, threshold = -1f }, new ChildMotion { motion = leanR, threshold = 1f }, }; var leanState = smLean.AddState("LeanBlend"); leanState.motion = btLean; } EditorUtility.SetDirty(controller); AssetDatabase.SaveAssets(); Debug.Log("Zibu Animator built at: " + controllerPath); } } #endif