256 lines
10 KiB
C#
256 lines
10 KiB
C#
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<AnimatorController>(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<int>() ?? 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<AnimatorController>(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<int>() ?? 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<AnimatorController>(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<AnimationClip>(clipPath);
|
|
if (clip == null)
|
|
return new { success = false, message = $"AnimationClip not found at '{clipPath}'" };
|
|
|
|
int layerIndex = @params["layerIndex"]?.ToObject<int>() ?? 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<float?>();
|
|
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>();
|
|
float posY = posArray[1].ToObject<float>();
|
|
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
|
|
}
|
|
};
|
|
}
|
|
}
|
|
}
|
|
}
|