MiniGames/Assets/Editor/BuildZibuAnimator.cs
2025-08-14 20:29:09 +05:00

182 lines
8.1 KiB
C#

#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<AnimationClip>)
.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