Skip to content

Commit 845fdff

Browse files
committed
Update to AssetGen
Generator Pipeline combined with the TrellisPlugin
1 parent 2127444 commit 845fdff

File tree

6 files changed

+1318
-2
lines changed

6 files changed

+1318
-2
lines changed

.gitignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,12 @@ TestProjects/UnityMCPTests/Packages/packages-lock.json
3939
*.backup.meta
4040

4141
.wt-origin-main/
42+
MCPForUnity/Trellis/TrellisClient.cs
43+
MCPForUnity/Trellis/TrellisClient.cs.meta
44+
MCPForUnity/Trellis/TrellisPlugin.meta
45+
MCPForUnity/Trellis/TrellisPlugin/TrellisClientEditor.cs
46+
MCPForUnity/Trellis/TrellisPlugin/TrellisClientEditor.cs.meta
47+
MCPForUnity/Trellis/TrellisPlugin/TrellisGeneratorWindow.cs
48+
MCPForUnity/Trellis/TrellisPlugin/TrellisGeneratorWindow.cs.meta
49+
MCPForUnity/Trellis/TrellisPlugin/TrellisServiceHost.cs
50+
MCPForUnity/Trellis/TrellisPlugin/TrellisServiceHost.cs.meta

MCPForUnity/Editor/Tools/ManageGameObject.cs

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,10 @@ public static object HandleCommand(JObject @params)
189189
return RemoveComponentFromTarget(@params, targetToken, searchMethod);
190190
case "set_component_property":
191191
return SetComponentPropertyOnTarget(@params, targetToken, searchMethod);
192+
case "duplicate":
193+
return DuplicateGameObject(@params, targetToken, searchMethod);
194+
case "move_relative":
195+
return MoveRelativeToObject(@params, targetToken, searchMethod);
192196

193197
default:
194198
return new ErrorResponse($"Unknown action: '{action}'.");
@@ -898,6 +902,219 @@ string searchMethod
898902

899903
}
900904

905+
/// <summary>
906+
/// Duplicates a GameObject with all its properties, components, and children.
907+
/// </summary>
908+
private static object DuplicateGameObject(JObject @params, JToken targetToken, string searchMethod)
909+
{
910+
GameObject sourceGo = FindObjectInternal(targetToken, searchMethod);
911+
if (sourceGo == null)
912+
{
913+
return new ErrorResponse(
914+
$"Target GameObject ('{targetToken}') not found using method '{searchMethod ?? "default"}'."
915+
);
916+
}
917+
918+
// Optional parameters
919+
string newName = @params["new_name"]?.ToString();
920+
Vector3? position = ParseVector3(@params["position"] as JArray);
921+
Vector3? offset = ParseVector3(@params["offset"] as JArray);
922+
JToken parentToken = @params["parent"];
923+
924+
// Duplicate the object
925+
GameObject duplicatedGo = UnityEngine.Object.Instantiate(sourceGo);
926+
Undo.RegisterCreatedObjectUndo(duplicatedGo, $"Duplicate {sourceGo.name}");
927+
928+
// Set name (default: SourceName_Copy or SourceName (1))
929+
if (!string.IsNullOrEmpty(newName))
930+
{
931+
duplicatedGo.name = newName;
932+
}
933+
else
934+
{
935+
// Remove "(Clone)" suffix added by Instantiate and add "_Copy"
936+
duplicatedGo.name = sourceGo.name.Replace("(Clone)", "").Trim() + "_Copy";
937+
}
938+
939+
// Handle positioning
940+
if (position.HasValue)
941+
{
942+
// Absolute position specified
943+
duplicatedGo.transform.position = position.Value;
944+
}
945+
else if (offset.HasValue)
946+
{
947+
// Offset from original
948+
duplicatedGo.transform.position = sourceGo.transform.position + offset.Value;
949+
}
950+
// else: keeps the same position as the original (default Instantiate behavior)
951+
952+
// Handle parent
953+
if (parentToken != null)
954+
{
955+
if (parentToken.Type == JTokenType.Null ||
956+
(parentToken.Type == JTokenType.String && string.IsNullOrEmpty(parentToken.ToString())))
957+
{
958+
// Explicit null parent - move to root
959+
duplicatedGo.transform.SetParent(null);
960+
}
961+
else
962+
{
963+
GameObject newParent = FindObjectInternal(parentToken, "by_id_or_name_or_path");
964+
if (newParent != null)
965+
{
966+
duplicatedGo.transform.SetParent(newParent.transform, true);
967+
}
968+
else
969+
{
970+
Debug.LogWarning($"[ManageGameObject.Duplicate] Parent '{parentToken}' not found. Keeping original parent.");
971+
}
972+
}
973+
}
974+
else
975+
{
976+
// Default: same parent as source
977+
duplicatedGo.transform.SetParent(sourceGo.transform.parent, true);
978+
}
979+
980+
// Mark scene dirty
981+
EditorUtility.SetDirty(duplicatedGo);
982+
EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
983+
984+
Selection.activeGameObject = duplicatedGo;
985+
986+
return new SuccessResponse(
987+
$"Duplicated '{sourceGo.name}' as '{duplicatedGo.name}'.",
988+
new
989+
{
990+
originalName = sourceGo.name,
991+
originalId = sourceGo.GetInstanceID(),
992+
duplicatedObject = Helpers.GameObjectSerializer.GetGameObjectData(duplicatedGo)
993+
}
994+
);
995+
}
996+
997+
/// <summary>
998+
/// Moves a GameObject relative to another reference object.
999+
/// Supports directional offsets (left, right, up, down, forward, back) and distance.
1000+
/// </summary>
1001+
private static object MoveRelativeToObject(JObject @params, JToken targetToken, string searchMethod)
1002+
{
1003+
GameObject targetGo = FindObjectInternal(targetToken, searchMethod);
1004+
if (targetGo == null)
1005+
{
1006+
return new ErrorResponse(
1007+
$"Target GameObject ('{targetToken}') not found using method '{searchMethod ?? "default"}'."
1008+
);
1009+
}
1010+
1011+
// Get reference object (required for relative movement)
1012+
JToken referenceToken = @params["reference_object"];
1013+
if (referenceToken == null)
1014+
{
1015+
return new ErrorResponse("'reference_object' parameter is required for 'move_relative' action.");
1016+
}
1017+
1018+
GameObject referenceGo = FindObjectInternal(referenceToken, "by_id_or_name_or_path");
1019+
if (referenceGo == null)
1020+
{
1021+
return new ErrorResponse($"Reference object '{referenceToken}' not found.");
1022+
}
1023+
1024+
// Get movement parameters
1025+
string direction = @params["direction"]?.ToString()?.ToLower();
1026+
float distance = @params["distance"]?.ToObject<float>() ?? 1f;
1027+
Vector3? customOffset = ParseVector3(@params["offset"] as JArray);
1028+
bool useWorldSpace = @params["world_space"]?.ToObject<bool>() ?? true;
1029+
1030+
// Record for undo
1031+
Undo.RecordObject(targetGo.transform, $"Move {targetGo.name} relative to {referenceGo.name}");
1032+
1033+
Vector3 newPosition;
1034+
1035+
if (customOffset.HasValue)
1036+
{
1037+
// Custom offset vector provided
1038+
if (useWorldSpace)
1039+
{
1040+
newPosition = referenceGo.transform.position + customOffset.Value;
1041+
}
1042+
else
1043+
{
1044+
// Offset in reference object's local space
1045+
newPosition = referenceGo.transform.TransformPoint(customOffset.Value);
1046+
}
1047+
}
1048+
else if (!string.IsNullOrEmpty(direction))
1049+
{
1050+
// Directional movement
1051+
Vector3 directionVector = GetDirectionVector(direction, referenceGo.transform, useWorldSpace);
1052+
newPosition = referenceGo.transform.position + directionVector * distance;
1053+
}
1054+
else
1055+
{
1056+
return new ErrorResponse("Either 'direction' or 'offset' parameter is required for 'move_relative' action.");
1057+
}
1058+
1059+
targetGo.transform.position = newPosition;
1060+
1061+
// Mark scene dirty
1062+
EditorUtility.SetDirty(targetGo);
1063+
EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
1064+
1065+
return new SuccessResponse(
1066+
$"Moved '{targetGo.name}' relative to '{referenceGo.name}'.",
1067+
new
1068+
{
1069+
movedObject = targetGo.name,
1070+
referenceObject = referenceGo.name,
1071+
newPosition = new[] { targetGo.transform.position.x, targetGo.transform.position.y, targetGo.transform.position.z },
1072+
direction = direction,
1073+
distance = distance,
1074+
gameObject = Helpers.GameObjectSerializer.GetGameObjectData(targetGo)
1075+
}
1076+
);
1077+
}
1078+
1079+
/// <summary>
1080+
/// Converts a direction string to a Vector3.
1081+
/// </summary>
1082+
private static Vector3 GetDirectionVector(string direction, Transform referenceTransform, bool useWorldSpace)
1083+
{
1084+
if (useWorldSpace)
1085+
{
1086+
// World space directions
1087+
switch (direction)
1088+
{
1089+
case "right": return Vector3.right;
1090+
case "left": return Vector3.left;
1091+
case "up": return Vector3.up;
1092+
case "down": return Vector3.down;
1093+
case "forward": case "front": return Vector3.forward;
1094+
case "back": case "backward": case "behind": return Vector3.back;
1095+
default:
1096+
Debug.LogWarning($"[ManageGameObject.MoveRelative] Unknown direction '{direction}', defaulting to forward.");
1097+
return Vector3.forward;
1098+
}
1099+
}
1100+
else
1101+
{
1102+
// Reference object's local space directions
1103+
switch (direction)
1104+
{
1105+
case "right": return referenceTransform.right;
1106+
case "left": return -referenceTransform.right;
1107+
case "up": return referenceTransform.up;
1108+
case "down": return -referenceTransform.up;
1109+
case "forward": case "front": return referenceTransform.forward;
1110+
case "back": case "backward": case "behind": return -referenceTransform.forward;
1111+
default:
1112+
Debug.LogWarning($"[ManageGameObject.MoveRelative] Unknown direction '{direction}', defaulting to forward.");
1113+
return referenceTransform.forward;
1114+
}
1115+
}
1116+
}
1117+
9011118
private static object DeleteGameObject(JToken targetToken, string searchMethod)
9021119
{
9031120
// Find potentially multiple objects if name/tag search is used without find_all=false implicitly

0 commit comments

Comments
 (0)