Skip to content

Commit 5646102

Browse files
Merge pull request #80 from Snowblaze/am/scene-crud-like
feat: add scene management tools
2 parents 4efa92e + aa11e05 commit 5646102

File tree

11 files changed

+634
-0
lines changed

11 files changed

+634
-0
lines changed

Editor/Tools/CreateSceneTool.cs

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
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 and saving a new Unity scene
12+
/// </summary>
13+
public class CreateSceneTool : McpToolBase
14+
{
15+
public CreateSceneTool()
16+
{
17+
Name = "create_scene";
18+
Description = "Creates a new scene and saves it to the specified path";
19+
}
20+
21+
/// <summary>
22+
/// Execute the CreateScene 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+
// Parameters
28+
string sceneName = parameters["sceneName"]?.ToObject<string>();
29+
string folderPath = parameters["folderPath"]?.ToObject<string>();
30+
bool addToBuildSettings = parameters["addToBuildSettings"]?.ToObject<bool?>() ?? false;
31+
bool makeActive = parameters["makeActive"]?.ToObject<bool?>() ?? true;
32+
33+
if (string.IsNullOrEmpty(sceneName))
34+
{
35+
return McpUnitySocketHandler.CreateErrorResponse(
36+
"Required parameter 'sceneName' not provided",
37+
"validation_error"
38+
);
39+
}
40+
41+
// Default folder path
42+
if (string.IsNullOrEmpty(folderPath))
43+
{
44+
folderPath = "Assets";
45+
}
46+
47+
// Ensure folder exists
48+
if (!AssetDatabase.IsValidFolder(folderPath))
49+
{
50+
// Attempt to create nested folders as needed
51+
string[] parts = folderPath.Split(new[] {'/'}, StringSplitOptions.RemoveEmptyEntries);
52+
string current = parts.Length > 0 && parts[0] == "Assets" ? "Assets" : "Assets";
53+
for (int i = 0; i < parts.Length; i++)
54+
{
55+
if (i == 0 && parts[i] == "Assets") continue;
56+
string next = current + "/" + parts[i];
57+
if (!AssetDatabase.IsValidFolder(next))
58+
{
59+
AssetDatabase.CreateFolder(current, parts[i]);
60+
}
61+
current = next;
62+
}
63+
}
64+
65+
// Create unique path for the scene
66+
string basePath = folderPath.TrimEnd('/');
67+
string scenePath = AssetDatabase.GenerateUniqueAssetPath($"{basePath}/{sceneName}.unity");
68+
69+
try
70+
{
71+
var newScene = UnityEditor.SceneManagement.EditorSceneManager.NewScene(UnityEditor.SceneManagement.NewSceneSetup.DefaultGameObjects, UnityEditor.SceneManagement.NewSceneMode.Single);
72+
73+
bool saved = UnityEditor.SceneManagement.EditorSceneManager.SaveScene(newScene, scenePath);
74+
if (!saved)
75+
{
76+
return McpUnitySocketHandler.CreateErrorResponse(
77+
$"Failed to save scene at '{scenePath}'",
78+
"save_error"
79+
);
80+
}
81+
82+
AssetDatabase.Refresh();
83+
84+
// Make the scene active if requested
85+
if (makeActive)
86+
{
87+
UnityEditor.SceneManagement.EditorSceneManager.OpenScene(scenePath, UnityEditor.SceneManagement.OpenSceneMode.Single);
88+
}
89+
90+
// Optionally add to build settings
91+
if (addToBuildSettings)
92+
{
93+
AddSceneToBuildSettings(scenePath);
94+
}
95+
96+
McpLogger.LogInfo($"Created scene '{sceneName}' at path '{scenePath}'");
97+
98+
return new JObject
99+
{
100+
["success"] = true,
101+
["type"] = "text",
102+
["message"] = $"Successfully created scene '{sceneName}' at path '{scenePath}'",
103+
["scenePath"] = scenePath
104+
};
105+
}
106+
catch (Exception ex)
107+
{
108+
return McpUnitySocketHandler.CreateErrorResponse(
109+
$"Error creating scene: {ex.Message}",
110+
"scene_creation_error"
111+
);
112+
}
113+
}
114+
115+
private void AddSceneToBuildSettings(string scenePath)
116+
{
117+
var scenes = UnityEditor.EditorBuildSettings.scenes;
118+
119+
// Check if already present
120+
foreach (var s in scenes)
121+
{
122+
if (s.path == scenePath)
123+
{
124+
return;
125+
}
126+
}
127+
128+
var newList = new UnityEditor.EditorBuildSettingsScene[scenes.Length + 1];
129+
for (int i = 0; i < scenes.Length; i++)
130+
{
131+
newList[i] = scenes[i];
132+
}
133+
newList[newList.Length - 1] = new UnityEditor.EditorBuildSettingsScene(scenePath, true);
134+
UnityEditor.EditorBuildSettings.scenes = newList;
135+
}
136+
}
137+
}
138+
139+

Editor/Tools/CreateSceneTool.cs.meta

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Editor/Tools/DeleteSceneTool.cs

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
using System;
2+
using System.Linq;
3+
using UnityEngine;
4+
using UnityEditor;
5+
using Newtonsoft.Json.Linq;
6+
using McpUnity.Unity;
7+
using McpUnity.Utils;
8+
9+
namespace McpUnity.Tools
10+
{
11+
/// <summary>
12+
/// Tool for deleting a Unity scene and removing it from Build Settings
13+
/// </summary>
14+
public class DeleteSceneTool : McpToolBase
15+
{
16+
public DeleteSceneTool()
17+
{
18+
Name = "delete_scene";
19+
Description = "Deletes a scene by path or name and removes it from Build Settings";
20+
}
21+
22+
/// <summary>
23+
/// Execute the DeleteScene tool with the provided parameters
24+
/// </summary>
25+
/// <param name="parameters">Tool parameters as a JObject</param>
26+
public override JObject Execute(JObject parameters)
27+
{
28+
string scenePath = parameters["scenePath"]?.ToObject<string>();
29+
string sceneName = parameters["sceneName"]?.ToObject<string>();
30+
string folderPath = parameters["folderPath"]?.ToObject<string>();
31+
32+
if (string.IsNullOrEmpty(scenePath))
33+
{
34+
if (string.IsNullOrEmpty(sceneName))
35+
{
36+
return McpUnitySocketHandler.CreateErrorResponse(
37+
"Provide either 'scenePath' or 'sceneName'",
38+
"validation_error"
39+
);
40+
}
41+
42+
// Resolve scene path by name (optionally within folderPath)
43+
string filter = $"{sceneName} t:Scene";
44+
string[] searchInFolders = null;
45+
if (!string.IsNullOrEmpty(folderPath))
46+
{
47+
// Ensure folder exists
48+
if (!AssetDatabase.IsValidFolder(folderPath))
49+
{
50+
return McpUnitySocketHandler.CreateErrorResponse(
51+
$"Folder '{folderPath}' does not exist",
52+
"not_found_error"
53+
);
54+
}
55+
searchInFolders = new[] { folderPath };
56+
}
57+
58+
var guids = AssetDatabase.FindAssets(filter, searchInFolders);
59+
foreach (var guid in guids)
60+
{
61+
var path = AssetDatabase.GUIDToAssetPath(guid);
62+
if (System.IO.Path.GetFileNameWithoutExtension(path) == sceneName)
63+
{
64+
scenePath = path;
65+
break;
66+
}
67+
}
68+
69+
if (string.IsNullOrEmpty(scenePath))
70+
{
71+
return McpUnitySocketHandler.CreateErrorResponse(
72+
$"Scene named '{sceneName}' not found",
73+
"not_found_error"
74+
);
75+
}
76+
}
77+
78+
try
79+
{
80+
// If the scene is open, close it without saving changes
81+
var scene = UnityEditor.SceneManagement.EditorSceneManager.GetSceneByPath(scenePath);
82+
if (scene.IsValid() && scene.isLoaded)
83+
{
84+
UnityEditor.SceneManagement.EditorSceneManager.CloseScene(scene, true);
85+
}
86+
87+
// Remove from Build Settings
88+
RemoveSceneFromBuildSettings(scenePath);
89+
90+
// Delete asset
91+
bool deleted = AssetDatabase.DeleteAsset(scenePath);
92+
AssetDatabase.Refresh();
93+
94+
if (!deleted)
95+
{
96+
return McpUnitySocketHandler.CreateErrorResponse(
97+
$"Failed to delete scene at '{scenePath}'",
98+
"delete_error"
99+
);
100+
}
101+
102+
McpLogger.LogInfo($"Deleted scene at path '{scenePath}' and removed from Build Settings");
103+
104+
return new JObject
105+
{
106+
["success"] = true,
107+
["type"] = "text",
108+
["message"] = $"Successfully deleted scene at path '{scenePath}' and removed from Build Settings",
109+
["scenePath"] = scenePath
110+
};
111+
}
112+
catch (Exception ex)
113+
{
114+
return McpUnitySocketHandler.CreateErrorResponse(
115+
$"Error deleting scene: {ex.Message}",
116+
"scene_delete_error"
117+
);
118+
}
119+
}
120+
121+
private void RemoveSceneFromBuildSettings(string scenePath)
122+
{
123+
var scenes = UnityEditor.EditorBuildSettings.scenes;
124+
var filtered = scenes.Where(s => s.path != scenePath).ToArray();
125+
if (filtered.Length != scenes.Length)
126+
{
127+
UnityEditor.EditorBuildSettings.scenes = filtered;
128+
}
129+
}
130+
}
131+
}
132+
133+

Editor/Tools/DeleteSceneTool.cs.meta

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Editor/Tools/LoadSceneTool.cs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
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 loading a Unity scene, optionally additively
12+
/// </summary>
13+
public class LoadSceneTool : McpToolBase
14+
{
15+
public LoadSceneTool()
16+
{
17+
Name = "load_scene";
18+
Description = "Loads a scene by path or name. Supports additive loading (default: false)";
19+
}
20+
21+
/// <summary>
22+
/// Execute the LoadScene 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+
string scenePath = parameters["scenePath"]?.ToObject<string>();
28+
string sceneName = parameters["sceneName"]?.ToObject<string>();
29+
string folderPath = parameters["folderPath"]?.ToObject<string>();
30+
bool additive = parameters["additive"]?.ToObject<bool?>() ?? false;
31+
32+
if (string.IsNullOrEmpty(scenePath))
33+
{
34+
if (string.IsNullOrEmpty(sceneName))
35+
{
36+
return McpUnitySocketHandler.CreateErrorResponse(
37+
"Provide either 'scenePath' or 'sceneName'",
38+
"validation_error"
39+
);
40+
}
41+
42+
// Resolve scene path by name (optionally within folderPath)
43+
string filter = $"{sceneName} t:Scene";
44+
string[] searchInFolders = null;
45+
if (!string.IsNullOrEmpty(folderPath))
46+
{
47+
if (!AssetDatabase.IsValidFolder(folderPath))
48+
{
49+
return McpUnitySocketHandler.CreateErrorResponse(
50+
$"Folder '{folderPath}' does not exist",
51+
"not_found_error"
52+
);
53+
}
54+
searchInFolders = new[] { folderPath };
55+
}
56+
57+
var guids = AssetDatabase.FindAssets(filter, searchInFolders);
58+
foreach (var guid in guids)
59+
{
60+
var path = AssetDatabase.GUIDToAssetPath(guid);
61+
if (System.IO.Path.GetFileNameWithoutExtension(path) == sceneName)
62+
{
63+
scenePath = path;
64+
break;
65+
}
66+
}
67+
68+
if (string.IsNullOrEmpty(scenePath))
69+
{
70+
return McpUnitySocketHandler.CreateErrorResponse(
71+
$"Scene named '{sceneName}' not found",
72+
"not_found_error"
73+
);
74+
}
75+
}
76+
77+
try
78+
{
79+
// Avoid any save prompts: save open scenes before replacing them (non-additive)
80+
if (!additive)
81+
{
82+
UnityEditor.SceneManagement.EditorSceneManager.SaveOpenScenes();
83+
}
84+
85+
var mode = additive
86+
? UnityEditor.SceneManagement.OpenSceneMode.Additive
87+
: UnityEditor.SceneManagement.OpenSceneMode.Single;
88+
89+
var openedScene = UnityEditor.SceneManagement.EditorSceneManager.OpenScene(scenePath, mode);
90+
91+
// For non-additive, scene becomes active automatically. For additive, we do not change active scene.
92+
93+
McpLogger.LogInfo($"Loaded scene at path '{scenePath}' (additive={additive})");
94+
95+
return new JObject
96+
{
97+
["success"] = true,
98+
["type"] = "text",
99+
["message"] = $"Successfully loaded scene at path '{scenePath}' (additive={additive.ToString().ToLower()})",
100+
["scenePath"] = scenePath,
101+
["additive"] = additive
102+
};
103+
}
104+
catch (Exception ex)
105+
{
106+
return McpUnitySocketHandler.CreateErrorResponse(
107+
$"Error loading scene: {ex.Message}",
108+
"scene_load_error"
109+
);
110+
}
111+
}
112+
}
113+
}
114+
115+

0 commit comments

Comments
 (0)