254 lines
9.7 KiB
C#
254 lines
9.7 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using Newtonsoft.Json;
|
|
using Newtonsoft.Json.Linq;
|
|
using MCPForUnity.Editor.Helpers;
|
|
using UnityEngine;
|
|
|
|
namespace MCPForUnity.Editor.Tools.Animation
|
|
{
|
|
[McpForUnityTool("manage_animation", AutoRegister = false, Group = "animation")]
|
|
public static class ManageAnimation
|
|
{
|
|
private static readonly Dictionary<string, string> ParamAliases = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
{ "clip_path", "clipPath" },
|
|
{ "controller_path", "controllerPath" },
|
|
{ "state_name", "stateName" },
|
|
{ "from_state", "fromState" },
|
|
{ "to_state", "toState" },
|
|
{ "parameter_name", "parameterName" },
|
|
{ "parameter_type", "parameterType" },
|
|
{ "property_path", "propertyPath" },
|
|
{ "default_value", "defaultValue" },
|
|
{ "has_exit_time", "hasExitTime" },
|
|
{ "exit_time", "exitTime" },
|
|
{ "layer_index", "layerIndex" },
|
|
{ "is_default", "isDefault" },
|
|
{ "relative_path", "relativePath" },
|
|
{ "function_name", "functionName" },
|
|
{ "string_parameter", "stringParameter" },
|
|
{ "float_parameter", "floatParameter" },
|
|
{ "int_parameter", "intParameter" },
|
|
{ "event_index", "eventIndex" },
|
|
{ "layer_name", "layerName" },
|
|
{ "blending_mode", "blendingMode" },
|
|
{ "blend_parameter", "blendParameter" },
|
|
{ "blend_parameter_x", "blendParameterX" },
|
|
{ "blend_parameter_y", "blendParameterY" },
|
|
{ "blend_type", "blendType" },
|
|
};
|
|
|
|
private static JObject NormalizeParams(JObject source)
|
|
{
|
|
if (source == null)
|
|
{
|
|
return new JObject();
|
|
}
|
|
|
|
var normalized = new JObject();
|
|
var properties = ExtractProperties(source);
|
|
if (properties != null)
|
|
{
|
|
foreach (var prop in properties.Properties())
|
|
{
|
|
normalized[NormalizeKey(prop.Name, true)] = NormalizeToken(prop.Value);
|
|
}
|
|
}
|
|
|
|
foreach (var prop in source.Properties())
|
|
{
|
|
if (string.Equals(prop.Name, "properties", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
continue;
|
|
}
|
|
normalized[NormalizeKey(prop.Name, true)] = NormalizeToken(prop.Value);
|
|
}
|
|
|
|
return normalized;
|
|
}
|
|
|
|
private static JObject ExtractProperties(JObject source)
|
|
{
|
|
if (source == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if (!source.TryGetValue("properties", StringComparison.OrdinalIgnoreCase, out var token))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if (token == null || token.Type == JTokenType.Null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if (token is JObject obj)
|
|
{
|
|
return obj;
|
|
}
|
|
|
|
if (token.Type == JTokenType.String)
|
|
{
|
|
try
|
|
{
|
|
return JToken.Parse(token.ToString()) as JObject;
|
|
}
|
|
catch (JsonException ex)
|
|
{
|
|
throw new JsonException(
|
|
$"Failed to parse 'properties' JSON string. Raw value: {token}",
|
|
ex);
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private static string NormalizeKey(string key, bool allowAliases)
|
|
{
|
|
if (string.IsNullOrEmpty(key))
|
|
{
|
|
return key;
|
|
}
|
|
if (string.Equals(key, "action", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return "action";
|
|
}
|
|
if (allowAliases && ParamAliases.TryGetValue(key, out var alias))
|
|
{
|
|
return alias;
|
|
}
|
|
if (key.IndexOf('_') >= 0)
|
|
{
|
|
return StringCaseUtility.ToCamelCase(key);
|
|
}
|
|
return key;
|
|
}
|
|
|
|
private static JToken NormalizeToken(JToken token)
|
|
{
|
|
if (token == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if (token is JObject obj)
|
|
{
|
|
var normalized = new JObject();
|
|
foreach (var prop in obj.Properties())
|
|
{
|
|
normalized[NormalizeKey(prop.Name, false)] = NormalizeToken(prop.Value);
|
|
}
|
|
return normalized;
|
|
}
|
|
|
|
if (token is JArray array)
|
|
{
|
|
var normalized = new JArray();
|
|
foreach (var item in array)
|
|
{
|
|
normalized.Add(NormalizeToken(item));
|
|
}
|
|
return normalized;
|
|
}
|
|
|
|
return token;
|
|
}
|
|
|
|
public static object HandleCommand(JObject @params)
|
|
{
|
|
JObject normalizedParams = NormalizeParams(@params);
|
|
string action = normalizedParams["action"]?.ToString();
|
|
if (string.IsNullOrEmpty(action))
|
|
{
|
|
return new { success = false, message = "Action is required" };
|
|
}
|
|
|
|
try
|
|
{
|
|
string actionLower = action.ToLowerInvariant();
|
|
|
|
if (actionLower.StartsWith("animator_"))
|
|
{
|
|
return HandleAnimatorAction(normalizedParams, actionLower.Substring(9));
|
|
}
|
|
|
|
if (actionLower.StartsWith("controller_"))
|
|
{
|
|
return HandleControllerAction(normalizedParams, actionLower.Substring(11));
|
|
}
|
|
|
|
if (actionLower.StartsWith("clip_"))
|
|
{
|
|
return HandleClipAction(normalizedParams, actionLower.Substring(5));
|
|
}
|
|
|
|
return new { success = false, message = $"Unknown action: {action}. Actions must be prefixed with: animator_, controller_, or clip_" };
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
McpLog.Error($"[ManageAnimation] Action '{action}' failed: {e}");
|
|
return new ErrorResponse($"Internal error processing action '{action}': {e.Message}");
|
|
}
|
|
}
|
|
|
|
private static object HandleAnimatorAction(JObject @params, string action)
|
|
{
|
|
switch (action)
|
|
{
|
|
case "get_info": return AnimatorRead.GetInfo(@params);
|
|
case "get_parameter": return AnimatorRead.GetParameter(@params);
|
|
case "play": return AnimatorControl.Play(@params);
|
|
case "crossfade": return AnimatorControl.Crossfade(@params);
|
|
case "set_parameter": return AnimatorControl.SetParameter(@params);
|
|
case "set_speed": return AnimatorControl.SetSpeed(@params);
|
|
case "set_enabled": return AnimatorControl.SetEnabled(@params);
|
|
default:
|
|
return new { success = false, message = $"Unknown animator action: {action}. Valid: get_info, get_parameter, play, crossfade, set_parameter, set_speed, set_enabled" };
|
|
}
|
|
}
|
|
|
|
private static object HandleControllerAction(JObject @params, string action)
|
|
{
|
|
switch (action)
|
|
{
|
|
case "create": return ControllerCreate.Create(@params);
|
|
case "add_state": return ControllerCreate.AddState(@params);
|
|
case "add_transition": return ControllerCreate.AddTransition(@params);
|
|
case "add_parameter": return ControllerCreate.AddParameter(@params);
|
|
case "get_info": return ControllerCreate.GetInfo(@params);
|
|
case "assign": return ControllerCreate.AssignToGameObject(@params);
|
|
case "add_layer": return ControllerLayers.AddLayer(@params);
|
|
case "remove_layer": return ControllerLayers.RemoveLayer(@params);
|
|
case "set_layer_weight": return ControllerLayers.SetLayerWeight(@params);
|
|
case "create_blend_tree_1d": return ControllerBlendTrees.CreateBlendTree1D(@params);
|
|
case "create_blend_tree_2d": return ControllerBlendTrees.CreateBlendTree2D(@params);
|
|
case "add_blend_tree_child": return ControllerBlendTrees.AddBlendTreeChild(@params);
|
|
default:
|
|
return new { success = false, message = $"Unknown controller action: {action}. Valid: create, add_state, add_transition, add_parameter, get_info, assign, add_layer, remove_layer, set_layer_weight, create_blend_tree_1d, create_blend_tree_2d, add_blend_tree_child" };
|
|
}
|
|
}
|
|
|
|
private static object HandleClipAction(JObject @params, string action)
|
|
{
|
|
switch (action)
|
|
{
|
|
case "create": return ClipCreate.Create(@params);
|
|
case "get_info": return ClipCreate.GetInfo(@params);
|
|
case "add_curve": return ClipCreate.AddCurve(@params);
|
|
case "set_curve": return ClipCreate.SetCurve(@params);
|
|
case "set_vector_curve": return ClipCreate.SetVectorCurve(@params);
|
|
case "create_preset": return ClipPresets.CreatePreset(@params);
|
|
case "assign": return ClipCreate.Assign(@params);
|
|
case "add_event": return ClipCreate.AddEvent(@params);
|
|
case "remove_event": return ClipCreate.RemoveEvent(@params);
|
|
default:
|
|
return new { success = false, message = $"Unknown clip action: {action}. Valid: create, get_info, add_curve, set_curve, set_vector_curve, create_preset, assign, add_event, remove_event" };
|
|
}
|
|
}
|
|
}
|
|
}
|