using System; using Newtonsoft.Json.Linq; using MCPForUnity.Editor.Helpers; using UnityEditor; using UnityEditor.Animations; using UnityEngine; namespace MCPForUnity.Editor.Tools.Animation { internal static class ControllerBlendTrees { public static object CreateBlendTree1D(JObject @params) { string controllerPath = @params["controllerPath"]?.ToString(); if (string.IsNullOrEmpty(controllerPath)) return new { success = false, message = "'controllerPath' is required" }; controllerPath = AssetPathUtility.SanitizeAssetPath(controllerPath); if (controllerPath == null) return new { success = false, message = "Invalid asset path" }; var controller = AssetDatabase.LoadAssetAtPath(controllerPath); if (controller == null) return new { success = false, message = $"AnimatorController not found at '{controllerPath}'" }; string stateName = @params["stateName"]?.ToString(); if (string.IsNullOrEmpty(stateName)) return new { success = false, message = "'stateName' is required" }; string blendParameter = @params["blendParameter"]?.ToString(); if (string.IsNullOrEmpty(blendParameter)) return new { success = false, message = "'blendParameter' is required" }; int layerIndex = @params["layerIndex"]?.ToObject() ?? 0; var layers = controller.layers; if (layerIndex < 0 || layerIndex >= layers.Length) return new { success = false, message = $"Layer index {layerIndex} out of range (0-{layers.Length - 1})" }; var stateMachine = layers[layerIndex].stateMachine; Undo.RecordObject(controller, "Create Blend Tree 1D"); var state = stateMachine.AddState(stateName); var blendTree = new BlendTree { name = stateName, blendType = BlendTreeType.Simple1D, blendParameter = blendParameter, hideFlags = HideFlags.HideInHierarchy }; AssetDatabase.AddObjectToAsset(blendTree, controller); state.motion = blendTree; EditorUtility.SetDirty(controller); AssetDatabase.SaveAssets(); return new { success = true, message = $"Created 1D blend tree state '{stateName}' in '{controllerPath}'", data = new { controllerPath, stateName, layerIndex, blendParameter, blendType = "Simple1D" } }; } public static object CreateBlendTree2D(JObject @params) { string controllerPath = @params["controllerPath"]?.ToString(); if (string.IsNullOrEmpty(controllerPath)) return new { success = false, message = "'controllerPath' is required" }; controllerPath = AssetPathUtility.SanitizeAssetPath(controllerPath); if (controllerPath == null) return new { success = false, message = "Invalid asset path" }; var controller = AssetDatabase.LoadAssetAtPath(controllerPath); if (controller == null) return new { success = false, message = $"AnimatorController not found at '{controllerPath}'" }; string stateName = @params["stateName"]?.ToString(); if (string.IsNullOrEmpty(stateName)) return new { success = false, message = "'stateName' is required" }; string blendParameterX = @params["blendParameterX"]?.ToString(); string blendParameterY = @params["blendParameterY"]?.ToString(); if (string.IsNullOrEmpty(blendParameterX) || string.IsNullOrEmpty(blendParameterY)) return new { success = false, message = "'blendParameterX' and 'blendParameterY' are required" }; int layerIndex = @params["layerIndex"]?.ToObject() ?? 0; string blendTypeStr = @params["blendType"]?.ToString()?.ToLowerInvariant() ?? "simpledirectional2d"; BlendTreeType blendType = blendTypeStr switch { "freeformdirectional2d" => BlendTreeType.FreeformDirectional2D, "freeformcartesian2d" => BlendTreeType.FreeformCartesian2D, _ => BlendTreeType.SimpleDirectional2D }; var layers = controller.layers; if (layerIndex < 0 || layerIndex >= layers.Length) return new { success = false, message = $"Layer index {layerIndex} out of range (0-{layers.Length - 1})" }; var stateMachine = layers[layerIndex].stateMachine; Undo.RecordObject(controller, "Create Blend Tree 2D"); var state = stateMachine.AddState(stateName); var blendTree = new BlendTree { name = stateName, blendType = blendType, blendParameter = blendParameterX, blendParameterY = blendParameterY, hideFlags = HideFlags.HideInHierarchy }; AssetDatabase.AddObjectToAsset(blendTree, controller); state.motion = blendTree; EditorUtility.SetDirty(controller); AssetDatabase.SaveAssets(); return new { success = true, message = $"Created 2D blend tree state '{stateName}' in '{controllerPath}'", data = new { controllerPath, stateName, layerIndex, blendParameterX, blendParameterY, blendType = blendType.ToString() } }; } public static object AddBlendTreeChild(JObject @params) { string controllerPath = @params["controllerPath"]?.ToString(); if (string.IsNullOrEmpty(controllerPath)) return new { success = false, message = "'controllerPath' is required" }; controllerPath = AssetPathUtility.SanitizeAssetPath(controllerPath); if (controllerPath == null) return new { success = false, message = "Invalid asset path" }; var controller = AssetDatabase.LoadAssetAtPath(controllerPath); if (controller == null) return new { success = false, message = $"AnimatorController not found at '{controllerPath}'" }; string stateName = @params["stateName"]?.ToString(); if (string.IsNullOrEmpty(stateName)) return new { success = false, message = "'stateName' is required" }; string clipPath = @params["clipPath"]?.ToString(); if (string.IsNullOrEmpty(clipPath)) return new { success = false, message = "'clipPath' is required" }; clipPath = AssetPathUtility.SanitizeAssetPath(clipPath); var clip = AssetDatabase.LoadAssetAtPath(clipPath); if (clip == null) return new { success = false, message = $"AnimationClip not found at '{clipPath}'" }; int layerIndex = @params["layerIndex"]?.ToObject() ?? 0; var layers = controller.layers; if (layerIndex < 0 || layerIndex >= layers.Length) return new { success = false, message = $"Layer index {layerIndex} out of range (0-{layers.Length - 1})" }; var stateMachine = layers[layerIndex].stateMachine; AnimatorState state = null; foreach (var s in stateMachine.states) { if (s.state.name == stateName) { state = s.state; break; } } if (state == null) return new { success = false, message = $"State '{stateName}' not found in layer {layerIndex}" }; if (!(state.motion is BlendTree blendTree)) return new { success = false, message = $"State '{stateName}' does not have a BlendTree motion" }; Undo.RecordObject(blendTree, "Add Blend Tree Child"); if (blendTree.blendType == BlendTreeType.Simple1D) { float? threshold = @params["threshold"]?.ToObject(); if (!threshold.HasValue) return new { success = false, message = "'threshold' is required for 1D blend trees" }; blendTree.AddChild(clip, threshold.Value); EditorUtility.SetDirty(blendTree); EditorUtility.SetDirty(controller); AssetDatabase.SaveAssets(); return new { success = true, message = $"Added clip '{clip.name}' to blend tree '{stateName}' at threshold {threshold.Value}", data = new { controllerPath, stateName, clipPath, threshold = threshold.Value, childCount = blendTree.children.Length } }; } else { JToken positionToken = @params["position"]; if (positionToken == null || !(positionToken is JArray posArray) || posArray.Count < 2) return new { success = false, message = "'position' is required for 2D blend trees as [x, y]" }; float posX = posArray[0].ToObject(); float posY = posArray[1].ToObject(); Vector2 position = new Vector2(posX, posY); blendTree.AddChild(clip, position); EditorUtility.SetDirty(blendTree); EditorUtility.SetDirty(controller); AssetDatabase.SaveAssets(); return new { success = true, message = $"Added clip '{clip.name}' to blend tree '{stateName}' at position ({posX}, {posY})", data = new { controllerPath, stateName, clipPath, position = new { x = posX, y = posY }, childCount = blendTree.children.Length } }; } } } }