chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,255 @@
|
||||
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
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user