Skip to content

Commit c98a670

Browse files
committed
Add CreatePrefabTool Unity editor implementation
Implemented CreatePrefabTool class in Unity Editor (C#) to create prefabs with optional MonoBehaviour script attachment and serialized field value application. Handles prefab naming conflicts and asset database refresh.
1 parent 957acc3 commit c98a670

File tree

1 file changed

+191
-0
lines changed

1 file changed

+191
-0
lines changed

Editor/Tools/CreatePrefabTool.cs

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
using System;
2+
using UnityEngine;
3+
using UnityEditor;
4+
using Newtonsoft.Json.Linq;
5+
using McpUnity.Unity;
6+
using McpUnity.Utils;
7+
8+
namespace McpUnity.Tools
9+
{
10+
/// <summary>
11+
/// Tool for creating prefabs with optional MonoBehaviour scripts
12+
/// </summary>
13+
public class CreatePrefabTool : McpToolBase
14+
{
15+
public CreatePrefabTool()
16+
{
17+
Name = "create_prefab";
18+
Description = "Creates a prefab with optional MonoBehaviour script and serialized field values";
19+
}
20+
21+
/// <summary>
22+
/// Execute the CreatePrefab tool with the provided parameters
23+
/// </summary>
24+
/// <param name="parameters">Tool parameters as a JObject</param>
25+
public override JObject Execute(JObject parameters)
26+
{
27+
// Extract parameters
28+
string scriptName = parameters["scriptName"]?.ToObject<string>();
29+
string prefabName = parameters["prefabName"]?.ToObject<string>();
30+
JObject fieldValues = parameters["fieldValues"]?.ToObject<JObject>();
31+
32+
// Validate required parameters
33+
if (string.IsNullOrEmpty(prefabName))
34+
{
35+
return McpUnitySocketHandler.CreateErrorResponse(
36+
"Required parameter 'prefabName' not provided",
37+
"validation_error"
38+
);
39+
}
40+
41+
try
42+
{
43+
// Create a temporary GameObject
44+
GameObject tempObject = new GameObject(prefabName);
45+
Component component = null;
46+
47+
// Add script component if scriptName is provided
48+
if (!string.IsNullOrEmpty(scriptName))
49+
{
50+
// Find the script type
51+
Type scriptType = Type.GetType($"{scriptName}, Assembly-CSharp");
52+
if (scriptType == null)
53+
{
54+
// Try with just the class name
55+
scriptType = Type.GetType(scriptName);
56+
}
57+
58+
if (scriptType == null)
59+
{
60+
// Try to find the type using AppDomain
61+
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
62+
{
63+
scriptType = assembly.GetType(scriptName);
64+
if (scriptType != null)
65+
break;
66+
}
67+
}
68+
69+
if (scriptType == null)
70+
{
71+
return McpUnitySocketHandler.CreateErrorResponse(
72+
$"Script type '{scriptName}' not found. Ensure the script is compiled.",
73+
"not_found_error"
74+
);
75+
}
76+
77+
// Check if the type is a MonoBehaviour
78+
if (!typeof(MonoBehaviour).IsAssignableFrom(scriptType))
79+
{
80+
return McpUnitySocketHandler.CreateErrorResponse(
81+
$"Type '{scriptName}' is not a MonoBehaviour",
82+
"invalid_type_error"
83+
);
84+
}
85+
86+
// Add the script component
87+
component = tempObject.AddComponent(scriptType);
88+
}
89+
90+
// Apply field values if provided and component exists
91+
if (fieldValues != null && fieldValues.Count > 0 && component != null)
92+
{
93+
Undo.RecordObject(component, "Set field values");
94+
95+
foreach (var property in fieldValues.Properties())
96+
{
97+
try
98+
{
99+
// Get the field/property info
100+
var fieldInfo = component.GetType().GetField(property.Name,
101+
System.Reflection.BindingFlags.Public |
102+
System.Reflection.BindingFlags.NonPublic |
103+
System.Reflection.BindingFlags.Instance);
104+
105+
if (fieldInfo != null)
106+
{
107+
// Set field value
108+
object value = property.Value.ToObject(fieldInfo.FieldType);
109+
fieldInfo.SetValue(component, value);
110+
}
111+
else
112+
{
113+
// Try property
114+
var propInfo = component.GetType().GetProperty(property.Name,
115+
System.Reflection.BindingFlags.Public |
116+
System.Reflection.BindingFlags.NonPublic |
117+
System.Reflection.BindingFlags.Instance);
118+
119+
if (propInfo != null && propInfo.CanWrite)
120+
{
121+
object value = property.Value.ToObject(propInfo.PropertyType);
122+
propInfo.SetValue(component, value);
123+
}
124+
}
125+
}
126+
catch (Exception ex)
127+
{
128+
McpLogger.LogWarning($"Failed to set field '{property.Name}': {ex.Message}");
129+
}
130+
}
131+
}
132+
else if (fieldValues != null && fieldValues.Count > 0 && component == null)
133+
{
134+
McpLogger.LogWarning("Field values provided but no script component to apply them to");
135+
}
136+
137+
// Ensure Prefabs directory exists
138+
string prefabsPath = "Assets/Prefabs";
139+
if (!AssetDatabase.IsValidFolder(prefabsPath))
140+
{
141+
string guid = AssetDatabase.CreateFolder("Assets", "Prefabs");
142+
prefabsPath = AssetDatabase.GUIDToAssetPath(guid);
143+
}
144+
145+
// Create prefab path
146+
string prefabPath = $"{prefabsPath}/{prefabName}.prefab";
147+
148+
// Check if prefab already exists
149+
if (AssetDatabase.AssetPathToGUID(prefabPath) != "")
150+
{
151+
// For safety, we'll create a unique name
152+
int counter = 1;
153+
string basePrefabPath = prefabPath;
154+
while (AssetDatabase.AssetPathToGUID(prefabPath) != "")
155+
{
156+
prefabPath = $"{prefabsPath}/{prefabName}_{counter}.prefab";
157+
counter++;
158+
}
159+
}
160+
161+
// Create the prefab
162+
GameObject prefab = PrefabUtility.SaveAsPrefabAsset(tempObject, prefabPath);
163+
164+
// Clean up temporary object
165+
UnityEngine.Object.DestroyImmediate(tempObject);
166+
167+
// Refresh the asset database
168+
AssetDatabase.Refresh();
169+
170+
// Log the action
171+
McpLogger.LogInfo($"Created prefab '{prefab.name}' at path '{prefabPath}' from script '{scriptName}'");
172+
173+
// Create the response
174+
return new JObject
175+
{
176+
["success"] = true,
177+
["type"] = "text",
178+
["message"] = $"Successfully created prefab '{prefab.name}' at path '{prefabPath}'",
179+
["prefabPath"] = prefabPath
180+
};
181+
}
182+
catch (Exception ex)
183+
{
184+
return McpUnitySocketHandler.CreateErrorResponse(
185+
$"Error creating prefab: {ex.Message}",
186+
"creation_error"
187+
);
188+
}
189+
}
190+
}
191+
}

0 commit comments

Comments
 (0)