uloop-cli 0.51.0 → 0.53.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.bundle.cjs +2510 -190
- package/dist/cli.bundle.cjs.map +3 -3
- package/package.json +1 -1
- package/scripts/generate-bundled-skills.ts +53 -10
- package/src/default-tools.json +1 -1
- package/src/port-resolver.ts +11 -5
- package/src/project-root.ts +103 -13
- package/src/skills/bundled-skills.ts +2216 -0
- package/src/skills/skills-manager.ts +49 -7
- package/src/version.ts +1 -1
package/dist/cli.bundle.cjs
CHANGED
|
@@ -5383,7 +5383,7 @@ var require_semver2 = __commonJS({
|
|
|
5383
5383
|
});
|
|
5384
5384
|
|
|
5385
5385
|
// src/cli.ts
|
|
5386
|
-
var
|
|
5386
|
+
var import_fs6 = require("fs");
|
|
5387
5387
|
var import_path6 = require("path");
|
|
5388
5388
|
var import_os2 = require("os");
|
|
5389
5389
|
var import_child_process = require("child_process");
|
|
@@ -5407,7 +5407,7 @@ var {
|
|
|
5407
5407
|
|
|
5408
5408
|
// src/execute-tool.ts
|
|
5409
5409
|
var readline = __toESM(require("readline"), 1);
|
|
5410
|
-
var
|
|
5410
|
+
var import_fs4 = require("fs");
|
|
5411
5411
|
var import_path4 = require("path");
|
|
5412
5412
|
var semver = __toESM(require_semver2(), 1);
|
|
5413
5413
|
|
|
@@ -5555,19 +5555,72 @@ var DirectUnityClient = class {
|
|
|
5555
5555
|
|
|
5556
5556
|
// src/port-resolver.ts
|
|
5557
5557
|
var import_promises = require("fs/promises");
|
|
5558
|
+
var import_fs2 = require("fs");
|
|
5558
5559
|
var import_path2 = require("path");
|
|
5559
5560
|
|
|
5560
5561
|
// src/project-root.ts
|
|
5561
5562
|
var import_fs = require("fs");
|
|
5562
5563
|
var import_path = require("path");
|
|
5563
|
-
|
|
5564
|
+
var CHILD_SEARCH_MAX_DEPTH = 3;
|
|
5565
|
+
var EXCLUDED_DIRS = /* @__PURE__ */ new Set([
|
|
5566
|
+
"node_modules",
|
|
5567
|
+
".git",
|
|
5568
|
+
"Temp",
|
|
5569
|
+
"obj",
|
|
5570
|
+
"Build",
|
|
5571
|
+
"Builds",
|
|
5572
|
+
"Logs",
|
|
5573
|
+
"Library"
|
|
5574
|
+
]);
|
|
5575
|
+
function isUnityProjectWithUloop(dirPath) {
|
|
5576
|
+
const hasAssets = (0, import_fs.existsSync)((0, import_path.join)(dirPath, "Assets"));
|
|
5577
|
+
const hasProjectSettings = (0, import_fs.existsSync)((0, import_path.join)(dirPath, "ProjectSettings"));
|
|
5578
|
+
const hasUloopSettings = (0, import_fs.existsSync)((0, import_path.join)(dirPath, "UserSettings/UnityMcpSettings.json"));
|
|
5579
|
+
return hasAssets && hasProjectSettings && hasUloopSettings;
|
|
5580
|
+
}
|
|
5581
|
+
function findUnityProjectsInChildren(startPath, maxDepth) {
|
|
5582
|
+
const projects = [];
|
|
5583
|
+
function scan(currentPath, depth) {
|
|
5584
|
+
if (depth > maxDepth) {
|
|
5585
|
+
return;
|
|
5586
|
+
}
|
|
5587
|
+
if (!(0, import_fs.existsSync)(currentPath)) {
|
|
5588
|
+
return;
|
|
5589
|
+
}
|
|
5590
|
+
if (isUnityProjectWithUloop(currentPath)) {
|
|
5591
|
+
projects.push(currentPath);
|
|
5592
|
+
return;
|
|
5593
|
+
}
|
|
5594
|
+
let entries;
|
|
5595
|
+
try {
|
|
5596
|
+
entries = (0, import_fs.readdirSync)(currentPath, { withFileTypes: true });
|
|
5597
|
+
} catch {
|
|
5598
|
+
return;
|
|
5599
|
+
}
|
|
5600
|
+
for (const entry of entries) {
|
|
5601
|
+
if (!entry.isDirectory()) {
|
|
5602
|
+
continue;
|
|
5603
|
+
}
|
|
5604
|
+
if (EXCLUDED_DIRS.has(entry.name)) {
|
|
5605
|
+
continue;
|
|
5606
|
+
}
|
|
5607
|
+
const fullPath = (0, import_path.join)(currentPath, entry.name);
|
|
5608
|
+
scan(fullPath, depth + 1);
|
|
5609
|
+
}
|
|
5610
|
+
}
|
|
5611
|
+
scan(startPath, 0);
|
|
5612
|
+
return projects.sort();
|
|
5613
|
+
}
|
|
5614
|
+
function findUnityProjectInParents(startPath) {
|
|
5564
5615
|
let currentPath = startPath;
|
|
5565
5616
|
while (true) {
|
|
5566
|
-
|
|
5567
|
-
const hasProjectSettings = (0, import_fs.existsSync)((0, import_path.join)(currentPath, "ProjectSettings"));
|
|
5568
|
-
if (hasAssets && hasProjectSettings) {
|
|
5617
|
+
if (isUnityProjectWithUloop(currentPath)) {
|
|
5569
5618
|
return currentPath;
|
|
5570
5619
|
}
|
|
5620
|
+
const isGitRoot = (0, import_fs.existsSync)((0, import_path.join)(currentPath, ".git"));
|
|
5621
|
+
if (isGitRoot) {
|
|
5622
|
+
return null;
|
|
5623
|
+
}
|
|
5571
5624
|
const parentPath = (0, import_path.dirname)(currentPath);
|
|
5572
5625
|
if (parentPath === currentPath) {
|
|
5573
5626
|
return null;
|
|
@@ -5575,6 +5628,21 @@ function findUnityProjectRoot(startPath = process.cwd()) {
|
|
|
5575
5628
|
currentPath = parentPath;
|
|
5576
5629
|
}
|
|
5577
5630
|
}
|
|
5631
|
+
function findUnityProjectRoot(startPath = process.cwd()) {
|
|
5632
|
+
const childProjects = findUnityProjectsInChildren(startPath, CHILD_SEARCH_MAX_DEPTH);
|
|
5633
|
+
if (childProjects.length > 0) {
|
|
5634
|
+
if (childProjects.length > 1) {
|
|
5635
|
+
console.error("\x1B[33mWarning: Multiple Unity projects found in child directories:\x1B[0m");
|
|
5636
|
+
for (const project of childProjects) {
|
|
5637
|
+
console.error(` - ${project}`);
|
|
5638
|
+
}
|
|
5639
|
+
console.error(`\x1B[33mUsing: ${childProjects[0]}\x1B[0m`);
|
|
5640
|
+
console.error("");
|
|
5641
|
+
}
|
|
5642
|
+
return childProjects[0];
|
|
5643
|
+
}
|
|
5644
|
+
return findUnityProjectInParents(startPath);
|
|
5645
|
+
}
|
|
5578
5646
|
|
|
5579
5647
|
// src/port-resolver.ts
|
|
5580
5648
|
var DEFAULT_PORT = 8700;
|
|
@@ -5582,18 +5650,21 @@ async function resolveUnityPort(explicitPort) {
|
|
|
5582
5650
|
if (explicitPort !== void 0) {
|
|
5583
5651
|
return explicitPort;
|
|
5584
5652
|
}
|
|
5585
|
-
const
|
|
5653
|
+
const projectRoot = findUnityProjectRoot();
|
|
5654
|
+
if (projectRoot === null) {
|
|
5655
|
+
throw new Error("Unity project not found. Use --port option to specify the port explicitly.");
|
|
5656
|
+
}
|
|
5657
|
+
const settingsPort = await readPortFromSettings(projectRoot);
|
|
5586
5658
|
if (settingsPort !== null) {
|
|
5587
5659
|
return settingsPort;
|
|
5588
5660
|
}
|
|
5589
5661
|
return DEFAULT_PORT;
|
|
5590
5662
|
}
|
|
5591
|
-
async function readPortFromSettings() {
|
|
5592
|
-
const
|
|
5593
|
-
if (
|
|
5663
|
+
async function readPortFromSettings(projectRoot) {
|
|
5664
|
+
const settingsPath = (0, import_path2.join)(projectRoot, "UserSettings/UnityMcpSettings.json");
|
|
5665
|
+
if (!(0, import_fs2.existsSync)(settingsPath)) {
|
|
5594
5666
|
return null;
|
|
5595
5667
|
}
|
|
5596
|
-
const settingsPath = (0, import_path2.join)(projectRoot, "UserSettings/UnityMcpSettings.json");
|
|
5597
5668
|
let content;
|
|
5598
5669
|
try {
|
|
5599
5670
|
content = await (0, import_promises.readFile)(settingsPath, "utf-8");
|
|
@@ -5616,12 +5687,12 @@ async function readPortFromSettings() {
|
|
|
5616
5687
|
}
|
|
5617
5688
|
|
|
5618
5689
|
// src/tool-cache.ts
|
|
5619
|
-
var
|
|
5690
|
+
var import_fs3 = require("fs");
|
|
5620
5691
|
var import_path3 = require("path");
|
|
5621
5692
|
|
|
5622
5693
|
// src/default-tools.json
|
|
5623
5694
|
var default_tools_default = {
|
|
5624
|
-
version: "0.
|
|
5695
|
+
version: "0.53.0",
|
|
5625
5696
|
tools: [
|
|
5626
5697
|
{
|
|
5627
5698
|
name: "compile",
|
|
@@ -6028,9 +6099,9 @@ function getDefaultTools() {
|
|
|
6028
6099
|
}
|
|
6029
6100
|
function loadToolsCache() {
|
|
6030
6101
|
const cachePath = getCachePath();
|
|
6031
|
-
if ((0,
|
|
6102
|
+
if ((0, import_fs3.existsSync)(cachePath)) {
|
|
6032
6103
|
try {
|
|
6033
|
-
const content = (0,
|
|
6104
|
+
const content = (0, import_fs3.readFileSync)(cachePath, "utf-8");
|
|
6034
6105
|
return JSON.parse(content);
|
|
6035
6106
|
} catch {
|
|
6036
6107
|
return getDefaultTools();
|
|
@@ -6041,18 +6112,18 @@ function loadToolsCache() {
|
|
|
6041
6112
|
function saveToolsCache(cache) {
|
|
6042
6113
|
const cacheDir = getCacheDir();
|
|
6043
6114
|
const cachePath = getCachePath();
|
|
6044
|
-
if (!(0,
|
|
6045
|
-
(0,
|
|
6115
|
+
if (!(0, import_fs3.existsSync)(cacheDir)) {
|
|
6116
|
+
(0, import_fs3.mkdirSync)(cacheDir, { recursive: true });
|
|
6046
6117
|
}
|
|
6047
6118
|
const content = JSON.stringify(cache, null, 2);
|
|
6048
|
-
(0,
|
|
6119
|
+
(0, import_fs3.writeFileSync)(cachePath, content, "utf-8");
|
|
6049
6120
|
}
|
|
6050
6121
|
function getCacheFilePath() {
|
|
6051
6122
|
return getCachePath();
|
|
6052
6123
|
}
|
|
6053
6124
|
|
|
6054
6125
|
// src/version.ts
|
|
6055
|
-
var VERSION = "0.
|
|
6126
|
+
var VERSION = "0.53.0";
|
|
6056
6127
|
|
|
6057
6128
|
// src/spinner.ts
|
|
6058
6129
|
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
@@ -6156,15 +6227,15 @@ function checkUnityBusyState() {
|
|
|
6156
6227
|
return;
|
|
6157
6228
|
}
|
|
6158
6229
|
const compilingLock = (0, import_path4.join)(projectRoot, "Temp", "compiling.lock");
|
|
6159
|
-
if ((0,
|
|
6230
|
+
if ((0, import_fs4.existsSync)(compilingLock)) {
|
|
6160
6231
|
throw new Error("UNITY_COMPILING");
|
|
6161
6232
|
}
|
|
6162
6233
|
const domainReloadLock = (0, import_path4.join)(projectRoot, "Temp", "domainreload.lock");
|
|
6163
|
-
if ((0,
|
|
6234
|
+
if ((0, import_fs4.existsSync)(domainReloadLock)) {
|
|
6164
6235
|
throw new Error("UNITY_DOMAIN_RELOAD");
|
|
6165
6236
|
}
|
|
6166
6237
|
const serverStartingLock = (0, import_path4.join)(projectRoot, "Temp", "serverstarting.lock");
|
|
6167
|
-
if ((0,
|
|
6238
|
+
if ((0, import_fs4.existsSync)(serverStartingLock)) {
|
|
6168
6239
|
throw new Error("UNITY_SERVER_STARTING");
|
|
6169
6240
|
}
|
|
6170
6241
|
}
|
|
@@ -6339,7 +6410,7 @@ function pascalToKebabCase(pascal) {
|
|
|
6339
6410
|
}
|
|
6340
6411
|
|
|
6341
6412
|
// src/skills/skills-manager.ts
|
|
6342
|
-
var
|
|
6413
|
+
var import_fs5 = require("fs");
|
|
6343
6414
|
var import_path5 = require("path");
|
|
6344
6415
|
var import_os = require("os");
|
|
6345
6416
|
|
|
@@ -6356,7 +6427,7 @@ var SKILL_default3 = '---\nname: uloop-compile\ndescription: Compile Unity proje
|
|
|
6356
6427
|
var SKILL_default4 = "---\nname: uloop-control-play-mode\ndescription: Control Unity Editor play mode via uloop CLI. Use when you need to: (1) Start play mode for testing, (2) Stop play mode after testing, (3) Pause play mode for debugging.\n---\n\n# uloop control-play-mode\n\nControl Unity Editor play mode (play/stop/pause).\n\n## Usage\n\n```bash\nuloop control-play-mode [options]\n```\n\n## Parameters\n\n| Parameter | Type | Default | Description |\n|-----------|------|---------|-------------|\n| `--action` | string | `Play` | Action to perform: `Play`, `Stop`, `Pause` |\n\n## Examples\n\n```bash\n# Start play mode\nuloop control-play-mode --action Play\n\n# Stop play mode\nuloop control-play-mode --action Stop\n\n# Pause play mode\nuloop control-play-mode --action Pause\n```\n\n## Output\n\nReturns JSON with the current play mode state:\n- `IsPlaying`: Whether Unity is currently in play mode\n- `IsPaused`: Whether play mode is paused\n- `Message`: Description of the action performed\n\n## Notes\n\n- Play action starts the game in the Unity Editor (also resumes from pause)\n- Stop action exits play mode and returns to edit mode\n- Pause action pauses the game while remaining in play mode\n- Useful for automated testing workflows\n";
|
|
6357
6428
|
|
|
6358
6429
|
// ../Editor/Api/McpTools/ExecuteDynamicCode/SKILL.md
|
|
6359
|
-
var SKILL_default5 = "---\nname: uloop-execute-dynamic-code\ndescription: Execute C# code dynamically in Unity Editor via uloop CLI. Use for editor automation: (1) Prefab/material wiring and AddComponent operations, (2) Reference wiring with SerializedObject, (3) Scene/hierarchy edits and batch operations. NOT for file I/O or script authoring.\n---\n\n# uloop execute-dynamic-code\n\nExecute C# code dynamically in Unity Editor.\n\n## Usage\n\n```bash\nuloop execute-dynamic-code --code '<c# code>'\n```\n\n## Parameters\n\n| Parameter | Type | Description |\n|-----------|------|-------------|\n| `--code` | string | C# code to execute (direct statements, no class wrapper) |\n| `--compile-only` | boolean | Compile without execution |\n| `--auto-qualify-unity-types-once` | boolean | Auto-qualify Unity types |\n\n## Code Format\n\nWrite direct statements only (no classes/namespaces/methods). Return is optional.\n\n```csharp\n// Using directives at top are hoisted\nusing UnityEngine;\nvar x = Mathf.PI;\nreturn x;\n```\n\n## String Literals (Shell-specific)\n\n| Shell | Method |\n|-------|--------|\n| bash/zsh/MINGW64/Git Bash | `'Debug.Log(\"Hello!\");'` |\n| PowerShell | `'Debug.Log(\"\"Hello!\"\");'` |\n\n## Allowed Operations\n\n- Prefab/material wiring (PrefabUtility)\n- AddComponent + reference wiring (SerializedObject)\n- Scene/hierarchy edits\n- Inspector modifications\n\n## Forbidden Operations\n\n- System.IO.* (File/Directory/Path)\n- AssetDatabase.CreateFolder / file writes\n- Create/edit .cs/.asmdef files\n\n## Examples\n\n### bash / zsh / MINGW64 / Git Bash\n\n```bash\nuloop execute-dynamic-code --code 'return Selection.activeGameObject?.name;'\nuloop execute-dynamic-code --code 'new GameObject(\"MyObject\");'\nuloop execute-dynamic-code --code 'UnityEngine.Debug.Log(\"Hello from CLI!\");'\n```\n\n### PowerShell\n\n```powershell\nuloop execute-dynamic-code --code 'return Selection.activeGameObject?.name;'\nuloop execute-dynamic-code --code 'new GameObject(\"\"MyObject\"\");'\nuloop execute-dynamic-code --code 'UnityEngine.Debug.Log(\"\"Hello from CLI!\"\");'\n```\n\n## Output\n\nReturns JSON with execution result or compile errors.\n\n## Notes\n\nFor file/directory operations, use terminal commands instead.\n";
|
|
6430
|
+
var SKILL_default5 = "---\nname: uloop-execute-dynamic-code\ndescription: Execute C# code dynamically in Unity Editor via uloop CLI. Use for editor automation: (1) Prefab/material wiring and AddComponent operations, (2) Reference wiring with SerializedObject, (3) Scene/hierarchy edits and batch operations. NOT for file I/O or script authoring.\n---\n\n# uloop execute-dynamic-code\n\nExecute C# code dynamically in Unity Editor.\n\n## Usage\n\n```bash\nuloop execute-dynamic-code --code '<c# code>'\n```\n\n## Parameters\n\n| Parameter | Type | Description |\n|-----------|------|-------------|\n| `--code` | string | C# code to execute (direct statements, no class wrapper) |\n| `--compile-only` | boolean | Compile without execution |\n| `--auto-qualify-unity-types-once` | boolean | Auto-qualify Unity types |\n\n## Code Format\n\nWrite direct statements only (no classes/namespaces/methods). Return is optional.\n\n```csharp\n// Using directives at top are hoisted\nusing UnityEngine;\nvar x = Mathf.PI;\nreturn x;\n```\n\n## String Literals (Shell-specific)\n\n| Shell | Method |\n|-------|--------|\n| bash/zsh/MINGW64/Git Bash | `'Debug.Log(\"Hello!\");'` |\n| PowerShell | `'Debug.Log(\"\"Hello!\"\");'` |\n\n## Allowed Operations\n\n- Prefab/material wiring (PrefabUtility)\n- AddComponent + reference wiring (SerializedObject)\n- Scene/hierarchy edits\n- Inspector modifications\n\n## Forbidden Operations\n\n- System.IO.* (File/Directory/Path)\n- AssetDatabase.CreateFolder / file writes\n- Create/edit .cs/.asmdef files\n\n## Examples\n\n### bash / zsh / MINGW64 / Git Bash\n\n```bash\nuloop execute-dynamic-code --code 'return Selection.activeGameObject?.name;'\nuloop execute-dynamic-code --code 'new GameObject(\"MyObject\");'\nuloop execute-dynamic-code --code 'UnityEngine.Debug.Log(\"Hello from CLI!\");'\n```\n\n### PowerShell\n\n```powershell\nuloop execute-dynamic-code --code 'return Selection.activeGameObject?.name;'\nuloop execute-dynamic-code --code 'new GameObject(\"\"MyObject\"\");'\nuloop execute-dynamic-code --code 'UnityEngine.Debug.Log(\"\"Hello from CLI!\"\");'\n```\n\n## Output\n\nReturns JSON with execution result or compile errors.\n\n## Notes\n\nFor file/directory operations, use terminal commands instead.\n\n## Code Examples by Category\n\nFor detailed code examples, refer to these files:\n\n- **Prefab operations**: See [examples/prefab-operations.md](examples/prefab-operations.md)\n - Create prefabs, instantiate, add components, modify properties\n- **Material operations**: See [examples/material-operations.md](examples/material-operations.md)\n - Create materials, set shaders/textures, modify properties\n- **Asset operations**: See [examples/asset-operations.md](examples/asset-operations.md)\n - Find/search assets, duplicate, move, rename, load\n- **ScriptableObject**: See [examples/scriptableobject.md](examples/scriptableobject.md)\n - Create ScriptableObjects, modify with SerializedObject\n- **Scene operations**: See [examples/scene-operations.md](examples/scene-operations.md)\n - Create/modify GameObjects, set parents, wire references, load scenes\n";
|
|
6360
6431
|
|
|
6361
6432
|
// ../Editor/Api/McpTools/ExecuteMenuItem/SKILL.md
|
|
6362
6433
|
var SKILL_default6 = '---\nname: uloop-execute-menu-item\ndescription: Execute Unity MenuItem via uloop CLI. Use when you need to: (1) Trigger menu commands programmatically, (2) Automate editor actions (save, build, refresh), (3) Run custom menu items defined in scripts.\n---\n\n# uloop execute-menu-item\n\nExecute Unity MenuItem.\n\n## Usage\n\n```bash\nuloop execute-menu-item --menu-item-path "<path>"\n```\n\n## Parameters\n\n| Parameter | Type | Default | Description |\n|-----------|------|---------|-------------|\n| `--menu-item-path` | string | - | Menu item path (e.g., "GameObject/Create Empty") |\n| `--use-reflection-fallback` | boolean | `true` | Use reflection fallback |\n\n## Examples\n\n```bash\n# Create empty GameObject\nuloop execute-menu-item --menu-item-path "GameObject/Create Empty"\n\n# Save scene\nuloop execute-menu-item --menu-item-path "File/Save"\n\n# Open project settings\nuloop execute-menu-item --menu-item-path "Edit/Project Settings..."\n```\n\n## Output\n\nReturns JSON with execution result.\n\n## Notes\n\n- Use `uloop get-menu-items` to discover available menu paths\n- Some menu items may require specific context or selection\n';
|
|
@@ -6410,175 +6481,2416 @@ var BUNDLED_SKILLS = [
|
|
|
6410
6481
|
{
|
|
6411
6482
|
name: "uloop-execute-dynamic-code",
|
|
6412
6483
|
dirName: "uloop-execute-dynamic-code",
|
|
6413
|
-
content: SKILL_default5
|
|
6414
|
-
|
|
6415
|
-
|
|
6416
|
-
name: "uloop-execute-menu-item",
|
|
6417
|
-
dirName: "uloop-execute-menu-item",
|
|
6418
|
-
content: SKILL_default6
|
|
6419
|
-
},
|
|
6420
|
-
{
|
|
6421
|
-
name: "uloop-find-game-objects",
|
|
6422
|
-
dirName: "uloop-find-game-objects",
|
|
6423
|
-
content: SKILL_default7
|
|
6424
|
-
},
|
|
6425
|
-
{
|
|
6426
|
-
name: "uloop-focus-window",
|
|
6427
|
-
dirName: "uloop-focus-window",
|
|
6428
|
-
content: SKILL_default8
|
|
6429
|
-
},
|
|
6430
|
-
{
|
|
6431
|
-
name: "uloop-get-hierarchy",
|
|
6432
|
-
dirName: "uloop-get-hierarchy",
|
|
6433
|
-
content: SKILL_default9
|
|
6434
|
-
},
|
|
6435
|
-
{
|
|
6436
|
-
name: "uloop-get-logs",
|
|
6437
|
-
dirName: "uloop-get-logs",
|
|
6438
|
-
content: SKILL_default10
|
|
6439
|
-
},
|
|
6440
|
-
{
|
|
6441
|
-
name: "uloop-get-menu-items",
|
|
6442
|
-
dirName: "uloop-get-menu-items",
|
|
6443
|
-
content: SKILL_default11
|
|
6444
|
-
},
|
|
6445
|
-
{
|
|
6446
|
-
name: "uloop-get-provider-details",
|
|
6447
|
-
dirName: "uloop-get-provider-details",
|
|
6448
|
-
content: SKILL_default12
|
|
6449
|
-
},
|
|
6450
|
-
{
|
|
6451
|
-
name: "uloop-run-tests",
|
|
6452
|
-
dirName: "uloop-run-tests",
|
|
6453
|
-
content: SKILL_default13
|
|
6454
|
-
},
|
|
6455
|
-
{
|
|
6456
|
-
name: "uloop-unity-search",
|
|
6457
|
-
dirName: "uloop-unity-search",
|
|
6458
|
-
content: SKILL_default14
|
|
6459
|
-
}
|
|
6460
|
-
];
|
|
6484
|
+
content: SKILL_default5,
|
|
6485
|
+
additionalFiles: {
|
|
6486
|
+
"examples/asset-operations.md": `# Asset Operations
|
|
6461
6487
|
|
|
6462
|
-
|
|
6463
|
-
|
|
6464
|
-
|
|
6465
|
-
|
|
6488
|
+
Code examples for AssetDatabase operations using \`execute-dynamic-code\`.
|
|
6489
|
+
|
|
6490
|
+
## Find Assets by Type
|
|
6491
|
+
|
|
6492
|
+
\`\`\`csharp
|
|
6493
|
+
using UnityEditor;
|
|
6494
|
+
using System.Collections.Generic;
|
|
6495
|
+
|
|
6496
|
+
string[] prefabGuids = AssetDatabase.FindAssets("t:Prefab");
|
|
6497
|
+
List<string> paths = new List<string>();
|
|
6498
|
+
|
|
6499
|
+
foreach (string guid in prefabGuids)
|
|
6500
|
+
{
|
|
6501
|
+
paths.Add(AssetDatabase.GUIDToAssetPath(guid));
|
|
6466
6502
|
}
|
|
6467
|
-
|
|
6468
|
-
|
|
6503
|
+
return $"Found {paths.Count} prefabs";
|
|
6504
|
+
\`\`\`
|
|
6505
|
+
|
|
6506
|
+
## Find Assets by Name
|
|
6507
|
+
|
|
6508
|
+
\`\`\`csharp
|
|
6509
|
+
using UnityEditor;
|
|
6510
|
+
using System.Collections.Generic;
|
|
6511
|
+
|
|
6512
|
+
string searchName = "Player";
|
|
6513
|
+
string[] guids = AssetDatabase.FindAssets(searchName);
|
|
6514
|
+
List<string> paths = new List<string>();
|
|
6515
|
+
|
|
6516
|
+
foreach (string guid in guids)
|
|
6517
|
+
{
|
|
6518
|
+
paths.Add(AssetDatabase.GUIDToAssetPath(guid));
|
|
6469
6519
|
}
|
|
6470
|
-
|
|
6471
|
-
|
|
6472
|
-
|
|
6520
|
+
return $"Found {paths.Count} assets matching '{searchName}'";
|
|
6521
|
+
\`\`\`
|
|
6522
|
+
|
|
6523
|
+
## Find Assets in Folder
|
|
6524
|
+
|
|
6525
|
+
\`\`\`csharp
|
|
6526
|
+
using UnityEditor;
|
|
6527
|
+
using System.Collections.Generic;
|
|
6528
|
+
|
|
6529
|
+
string folder = "Assets/Prefabs";
|
|
6530
|
+
string[] guids = AssetDatabase.FindAssets("t:Prefab", new[] { folder });
|
|
6531
|
+
List<string> paths = new List<string>();
|
|
6532
|
+
|
|
6533
|
+
foreach (string guid in guids)
|
|
6534
|
+
{
|
|
6535
|
+
paths.Add(AssetDatabase.GUIDToAssetPath(guid));
|
|
6473
6536
|
}
|
|
6474
|
-
|
|
6475
|
-
|
|
6476
|
-
|
|
6537
|
+
return $"Found {paths.Count} prefabs in {folder}";
|
|
6538
|
+
\`\`\`
|
|
6539
|
+
|
|
6540
|
+
## Duplicate Asset
|
|
6541
|
+
|
|
6542
|
+
\`\`\`csharp
|
|
6543
|
+
using UnityEditor;
|
|
6544
|
+
|
|
6545
|
+
string sourcePath = "Assets/Materials/MyMaterial.mat";
|
|
6546
|
+
string destPath = "Assets/Materials/MyMaterial_Backup.mat";
|
|
6547
|
+
|
|
6548
|
+
bool success = AssetDatabase.CopyAsset(sourcePath, destPath);
|
|
6549
|
+
return success ? $"Copied to {destPath}" : "Copy failed";
|
|
6550
|
+
\`\`\`
|
|
6551
|
+
|
|
6552
|
+
## Move Asset
|
|
6553
|
+
|
|
6554
|
+
\`\`\`csharp
|
|
6555
|
+
using UnityEditor;
|
|
6556
|
+
|
|
6557
|
+
string sourcePath = "Assets/OldFolder/MyAsset.asset";
|
|
6558
|
+
string destPath = "Assets/NewFolder/MyAsset.asset";
|
|
6559
|
+
|
|
6560
|
+
string error = AssetDatabase.MoveAsset(sourcePath, destPath);
|
|
6561
|
+
return string.IsNullOrEmpty(error) ? $"Moved to {destPath}" : $"Error: {error}";
|
|
6562
|
+
\`\`\`
|
|
6563
|
+
|
|
6564
|
+
## Rename Asset
|
|
6565
|
+
|
|
6566
|
+
\`\`\`csharp
|
|
6567
|
+
using UnityEditor;
|
|
6568
|
+
|
|
6569
|
+
string assetPath = "Assets/Materials/OldName.mat";
|
|
6570
|
+
string newName = "NewName";
|
|
6571
|
+
|
|
6572
|
+
string error = AssetDatabase.RenameAsset(assetPath, newName);
|
|
6573
|
+
return string.IsNullOrEmpty(error) ? $"Renamed to {newName}" : $"Error: {error}";
|
|
6574
|
+
\`\`\`
|
|
6575
|
+
|
|
6576
|
+
## Rename Asset (Undo-supported)
|
|
6577
|
+
|
|
6578
|
+
\`\`\`csharp
|
|
6579
|
+
using UnityEditor;
|
|
6580
|
+
|
|
6581
|
+
// ObjectNames.SetNameSmart() supports Undo (AssetDatabase.RenameAsset() does NOT)
|
|
6582
|
+
Object selected = Selection.activeObject;
|
|
6583
|
+
if (selected == null)
|
|
6584
|
+
{
|
|
6585
|
+
return "No asset selected";
|
|
6477
6586
|
}
|
|
6478
|
-
|
|
6479
|
-
|
|
6480
|
-
|
|
6481
|
-
|
|
6482
|
-
|
|
6483
|
-
|
|
6484
|
-
|
|
6587
|
+
|
|
6588
|
+
string oldName = selected.name;
|
|
6589
|
+
ObjectNames.SetNameSmart(selected, "NewName");
|
|
6590
|
+
AssetDatabase.SaveAssets();
|
|
6591
|
+
return $"Renamed {oldName} to {selected.name}";
|
|
6592
|
+
\`\`\`
|
|
6593
|
+
|
|
6594
|
+
## Get Asset Path from Object
|
|
6595
|
+
|
|
6596
|
+
\`\`\`csharp
|
|
6597
|
+
using UnityEditor;
|
|
6598
|
+
|
|
6599
|
+
GameObject selected = Selection.activeGameObject;
|
|
6600
|
+
if (selected == null)
|
|
6601
|
+
{
|
|
6602
|
+
return "No object selected";
|
|
6485
6603
|
}
|
|
6486
|
-
|
|
6487
|
-
|
|
6488
|
-
|
|
6489
|
-
|
|
6490
|
-
|
|
6491
|
-
return "outdated";
|
|
6492
|
-
}
|
|
6493
|
-
return "installed";
|
|
6604
|
+
|
|
6605
|
+
string path = AssetDatabase.GetAssetPath(selected);
|
|
6606
|
+
if (string.IsNullOrEmpty(path))
|
|
6607
|
+
{
|
|
6608
|
+
return "Selected object is not an asset (scene object)";
|
|
6494
6609
|
}
|
|
6495
|
-
|
|
6496
|
-
|
|
6497
|
-
|
|
6498
|
-
|
|
6499
|
-
|
|
6500
|
-
|
|
6501
|
-
|
|
6502
|
-
|
|
6503
|
-
|
|
6504
|
-
|
|
6505
|
-
|
|
6506
|
-
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
if (value === "true") {
|
|
6510
|
-
frontmatter[key] = true;
|
|
6511
|
-
} else if (value === "false") {
|
|
6512
|
-
frontmatter[key] = false;
|
|
6513
|
-
} else {
|
|
6514
|
-
frontmatter[key] = value;
|
|
6515
|
-
}
|
|
6516
|
-
}
|
|
6517
|
-
return frontmatter;
|
|
6610
|
+
return $"Asset path: {path}";
|
|
6611
|
+
\`\`\`
|
|
6612
|
+
|
|
6613
|
+
## Load Asset at Path
|
|
6614
|
+
|
|
6615
|
+
\`\`\`csharp
|
|
6616
|
+
using UnityEditor;
|
|
6617
|
+
|
|
6618
|
+
string path = "Assets/Prefabs/Player.prefab";
|
|
6619
|
+
GameObject asset = AssetDatabase.LoadAssetAtPath<GameObject>(path);
|
|
6620
|
+
|
|
6621
|
+
if (asset == null)
|
|
6622
|
+
{
|
|
6623
|
+
return $"Asset not found at {path}";
|
|
6518
6624
|
}
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
|
|
6528
|
-
|
|
6529
|
-
|
|
6530
|
-
|
|
6531
|
-
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
|
|
6535
|
-
|
|
6536
|
-
}
|
|
6537
|
-
const name = typeof frontmatter.name === "string" ? frontmatter.name : entry.name;
|
|
6538
|
-
skills.push({
|
|
6539
|
-
name,
|
|
6540
|
-
dirName: name,
|
|
6541
|
-
content,
|
|
6542
|
-
sourcePath: skillMdPath
|
|
6543
|
-
});
|
|
6544
|
-
}
|
|
6545
|
-
scanEditorFolderForSkills(fullPath, skills);
|
|
6625
|
+
return $"Loaded: {asset.name}";
|
|
6626
|
+
\`\`\`
|
|
6627
|
+
|
|
6628
|
+
## Get All Assets of Type
|
|
6629
|
+
|
|
6630
|
+
\`\`\`csharp
|
|
6631
|
+
using UnityEditor;
|
|
6632
|
+
|
|
6633
|
+
string[] scriptGuids = AssetDatabase.FindAssets("t:MonoScript");
|
|
6634
|
+
int count = 0;
|
|
6635
|
+
|
|
6636
|
+
foreach (string guid in scriptGuids)
|
|
6637
|
+
{
|
|
6638
|
+
string path = AssetDatabase.GUIDToAssetPath(guid);
|
|
6639
|
+
if (path.StartsWith("Assets/"))
|
|
6640
|
+
{
|
|
6641
|
+
count++;
|
|
6546
6642
|
}
|
|
6547
|
-
}
|
|
6548
6643
|
}
|
|
6549
|
-
|
|
6550
|
-
|
|
6551
|
-
|
|
6552
|
-
|
|
6553
|
-
|
|
6554
|
-
|
|
6555
|
-
|
|
6556
|
-
|
|
6557
|
-
|
|
6558
|
-
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
6563
|
-
|
|
6564
|
-
|
|
6565
|
-
|
|
6644
|
+
return $"Found {count} scripts in Assets folder";
|
|
6645
|
+
\`\`\`
|
|
6646
|
+
|
|
6647
|
+
## Check if Asset Exists
|
|
6648
|
+
|
|
6649
|
+
\`\`\`csharp
|
|
6650
|
+
using UnityEditor;
|
|
6651
|
+
|
|
6652
|
+
string path = "Assets/Prefabs/Player.prefab";
|
|
6653
|
+
string guid = AssetDatabase.AssetPathToGUID(path);
|
|
6654
|
+
|
|
6655
|
+
bool exists = !string.IsNullOrEmpty(guid);
|
|
6656
|
+
return exists ? $"Asset exists: {path}" : $"Asset not found: {path}";
|
|
6657
|
+
\`\`\`
|
|
6658
|
+
|
|
6659
|
+
## Get Asset Dependencies
|
|
6660
|
+
|
|
6661
|
+
\`\`\`csharp
|
|
6662
|
+
using UnityEditor;
|
|
6663
|
+
|
|
6664
|
+
string assetPath = "Assets/Prefabs/Player.prefab";
|
|
6665
|
+
string[] dependencies = AssetDatabase.GetDependencies(assetPath, true);
|
|
6666
|
+
|
|
6667
|
+
return $"Asset has {dependencies.Length} dependencies";
|
|
6668
|
+
\`\`\`
|
|
6669
|
+
|
|
6670
|
+
## Refresh AssetDatabase
|
|
6671
|
+
|
|
6672
|
+
\`\`\`csharp
|
|
6673
|
+
using UnityEditor;
|
|
6674
|
+
|
|
6675
|
+
AssetDatabase.Refresh();
|
|
6676
|
+
return "AssetDatabase refreshed";
|
|
6677
|
+
\`\`\`
|
|
6678
|
+
`,
|
|
6679
|
+
"examples/batch-operations.md": `# Batch Operations
|
|
6680
|
+
|
|
6681
|
+
Code examples for batch processing using \`execute-dynamic-code\`.
|
|
6682
|
+
|
|
6683
|
+
## Batch Modify Selected Objects
|
|
6684
|
+
|
|
6685
|
+
\`\`\`csharp
|
|
6686
|
+
using UnityEditor;
|
|
6687
|
+
|
|
6688
|
+
GameObject[] selected = Selection.gameObjects;
|
|
6689
|
+
if (selected.Length == 0)
|
|
6690
|
+
{
|
|
6691
|
+
return "No GameObjects selected";
|
|
6692
|
+
}
|
|
6693
|
+
|
|
6694
|
+
int undoGroup = Undo.GetCurrentGroup();
|
|
6695
|
+
Undo.SetCurrentGroupName("Batch Modify");
|
|
6696
|
+
|
|
6697
|
+
foreach (GameObject obj in selected)
|
|
6698
|
+
{
|
|
6699
|
+
Undo.RecordObject(obj.transform, "");
|
|
6700
|
+
obj.transform.localScale = Vector3.one * 2;
|
|
6701
|
+
}
|
|
6702
|
+
|
|
6703
|
+
Undo.CollapseUndoOperations(undoGroup);
|
|
6704
|
+
return $"Scaled {selected.Length} objects (Single undo step)";
|
|
6705
|
+
\`\`\`
|
|
6706
|
+
|
|
6707
|
+
## Edit Multiple Objects with SerializedObject
|
|
6708
|
+
|
|
6709
|
+
\`\`\`csharp
|
|
6710
|
+
using UnityEditor;
|
|
6711
|
+
|
|
6712
|
+
GameObject[] selected = Selection.gameObjects;
|
|
6713
|
+
if (selected.Length == 0)
|
|
6714
|
+
{
|
|
6715
|
+
return "No GameObjects selected";
|
|
6716
|
+
}
|
|
6717
|
+
|
|
6718
|
+
List<Transform> transforms = new List<Transform>();
|
|
6719
|
+
foreach (GameObject obj in selected)
|
|
6720
|
+
{
|
|
6721
|
+
transforms.Add(obj.transform);
|
|
6722
|
+
}
|
|
6723
|
+
|
|
6724
|
+
SerializedObject serializedObj = new SerializedObject(transforms.ToArray());
|
|
6725
|
+
SerializedProperty positionProp = serializedObj.FindProperty("m_LocalPosition");
|
|
6726
|
+
positionProp.vector3Value = Vector3.zero;
|
|
6727
|
+
serializedObj.ApplyModifiedProperties();
|
|
6728
|
+
|
|
6729
|
+
return $"Reset position of {selected.Length} objects";
|
|
6730
|
+
\`\`\`
|
|
6731
|
+
|
|
6732
|
+
## Batch Add Component
|
|
6733
|
+
|
|
6734
|
+
\`\`\`csharp
|
|
6735
|
+
using UnityEditor;
|
|
6736
|
+
|
|
6737
|
+
GameObject[] selected = Selection.gameObjects;
|
|
6738
|
+
if (selected.Length == 0)
|
|
6739
|
+
{
|
|
6740
|
+
return "No GameObjects selected";
|
|
6741
|
+
}
|
|
6742
|
+
|
|
6743
|
+
int undoGroup = Undo.GetCurrentGroup();
|
|
6744
|
+
Undo.SetCurrentGroupName("Batch Add Rigidbody");
|
|
6745
|
+
|
|
6746
|
+
int addedCount = 0;
|
|
6747
|
+
foreach (GameObject obj in selected)
|
|
6748
|
+
{
|
|
6749
|
+
if (obj.GetComponent<Rigidbody>() == null)
|
|
6750
|
+
{
|
|
6751
|
+
Undo.AddComponent<Rigidbody>(obj);
|
|
6752
|
+
addedCount++;
|
|
6566
6753
|
}
|
|
6567
|
-
}
|
|
6568
|
-
scan(basePath, 0);
|
|
6569
|
-
return editorFolders;
|
|
6570
6754
|
}
|
|
6571
|
-
|
|
6572
|
-
|
|
6573
|
-
|
|
6574
|
-
|
|
6575
|
-
|
|
6576
|
-
|
|
6577
|
-
|
|
6755
|
+
|
|
6756
|
+
Undo.CollapseUndoOperations(undoGroup);
|
|
6757
|
+
return $"Added Rigidbody to {addedCount} objects";
|
|
6758
|
+
\`\`\`
|
|
6759
|
+
|
|
6760
|
+
## Batch Process Assets with StartAssetEditing
|
|
6761
|
+
|
|
6762
|
+
\`\`\`csharp
|
|
6763
|
+
using UnityEditor;
|
|
6764
|
+
|
|
6765
|
+
string[] guids = AssetDatabase.FindAssets("t:Material", new[] { "Assets/Materials" });
|
|
6766
|
+
if (guids.Length == 0)
|
|
6767
|
+
{
|
|
6768
|
+
return "No materials found";
|
|
6769
|
+
}
|
|
6770
|
+
|
|
6771
|
+
AssetDatabase.StartAssetEditing();
|
|
6772
|
+
|
|
6773
|
+
int modified = 0;
|
|
6774
|
+
foreach (string guid in guids)
|
|
6775
|
+
{
|
|
6776
|
+
string path = AssetDatabase.GUIDToAssetPath(guid);
|
|
6777
|
+
Material mat = AssetDatabase.LoadAssetAtPath<Material>(path);
|
|
6778
|
+
if (mat != null)
|
|
6779
|
+
{
|
|
6780
|
+
mat.color = Color.white;
|
|
6781
|
+
EditorUtility.SetDirty(mat);
|
|
6782
|
+
modified++;
|
|
6783
|
+
}
|
|
6784
|
+
}
|
|
6785
|
+
|
|
6786
|
+
AssetDatabase.StopAssetEditing();
|
|
6787
|
+
AssetDatabase.SaveAssets();
|
|
6788
|
+
|
|
6789
|
+
return $"Reset color of {modified} materials";
|
|
6790
|
+
\`\`\`
|
|
6791
|
+
|
|
6792
|
+
## Batch Rename GameObjects
|
|
6793
|
+
|
|
6794
|
+
\`\`\`csharp
|
|
6795
|
+
using UnityEditor;
|
|
6796
|
+
|
|
6797
|
+
GameObject[] selected = Selection.gameObjects;
|
|
6798
|
+
if (selected.Length == 0)
|
|
6799
|
+
{
|
|
6800
|
+
return "No GameObjects selected";
|
|
6801
|
+
}
|
|
6802
|
+
|
|
6803
|
+
int undoGroup = Undo.GetCurrentGroup();
|
|
6804
|
+
Undo.SetCurrentGroupName("Batch Rename");
|
|
6805
|
+
|
|
6806
|
+
for (int i = 0; i < selected.Length; i++)
|
|
6807
|
+
{
|
|
6808
|
+
Undo.RecordObject(selected[i], "");
|
|
6809
|
+
selected[i].name = $"Item_{i:D3}";
|
|
6810
|
+
}
|
|
6811
|
+
|
|
6812
|
+
Undo.CollapseUndoOperations(undoGroup);
|
|
6813
|
+
return $"Renamed {selected.Length} objects";
|
|
6814
|
+
\`\`\`
|
|
6815
|
+
|
|
6816
|
+
## Batch Set Layer
|
|
6817
|
+
|
|
6818
|
+
\`\`\`csharp
|
|
6819
|
+
using UnityEditor;
|
|
6820
|
+
|
|
6821
|
+
GameObject[] selected = Selection.gameObjects;
|
|
6822
|
+
if (selected.Length == 0)
|
|
6823
|
+
{
|
|
6824
|
+
return "No GameObjects selected";
|
|
6825
|
+
}
|
|
6826
|
+
|
|
6827
|
+
int layer = LayerMask.NameToLayer("Default");
|
|
6828
|
+
|
|
6829
|
+
int undoGroup = Undo.GetCurrentGroup();
|
|
6830
|
+
Undo.SetCurrentGroupName("Batch Set Layer");
|
|
6831
|
+
|
|
6832
|
+
foreach (GameObject obj in selected)
|
|
6833
|
+
{
|
|
6834
|
+
Undo.RecordObject(obj, "");
|
|
6835
|
+
obj.layer = layer;
|
|
6836
|
+
}
|
|
6837
|
+
|
|
6838
|
+
Undo.CollapseUndoOperations(undoGroup);
|
|
6839
|
+
return $"Set layer of {selected.Length} objects to Default";
|
|
6840
|
+
\`\`\`
|
|
6841
|
+
|
|
6842
|
+
## Batch Set Tag
|
|
6843
|
+
|
|
6844
|
+
\`\`\`csharp
|
|
6845
|
+
using UnityEditor;
|
|
6846
|
+
|
|
6847
|
+
GameObject[] selected = Selection.gameObjects;
|
|
6848
|
+
if (selected.Length == 0)
|
|
6849
|
+
{
|
|
6850
|
+
return "No GameObjects selected";
|
|
6851
|
+
}
|
|
6852
|
+
|
|
6853
|
+
int undoGroup = Undo.GetCurrentGroup();
|
|
6854
|
+
Undo.SetCurrentGroupName("Batch Set Tag");
|
|
6855
|
+
|
|
6856
|
+
foreach (GameObject obj in selected)
|
|
6857
|
+
{
|
|
6858
|
+
Undo.RecordObject(obj, "");
|
|
6859
|
+
obj.tag = "Enemy";
|
|
6860
|
+
}
|
|
6861
|
+
|
|
6862
|
+
Undo.CollapseUndoOperations(undoGroup);
|
|
6863
|
+
return $"Tagged {selected.Length} objects as Enemy";
|
|
6864
|
+
\`\`\`
|
|
6865
|
+
|
|
6866
|
+
## Batch Modify ScriptableObjects
|
|
6867
|
+
|
|
6868
|
+
\`\`\`csharp
|
|
6869
|
+
using UnityEditor;
|
|
6870
|
+
|
|
6871
|
+
string[] guids = AssetDatabase.FindAssets("t:ScriptableObject", new[] { "Assets/Data" });
|
|
6872
|
+
if (guids.Length == 0)
|
|
6873
|
+
{
|
|
6874
|
+
return "No ScriptableObjects found";
|
|
6875
|
+
}
|
|
6876
|
+
|
|
6877
|
+
int modified = 0;
|
|
6878
|
+
foreach (string guid in guids)
|
|
6879
|
+
{
|
|
6880
|
+
string path = AssetDatabase.GUIDToAssetPath(guid);
|
|
6881
|
+
ScriptableObject so = AssetDatabase.LoadAssetAtPath<ScriptableObject>(path);
|
|
6882
|
+
if (so == null) continue;
|
|
6883
|
+
|
|
6884
|
+
SerializedObject serializedObj = new SerializedObject(so);
|
|
6885
|
+
SerializedProperty prop = serializedObj.FindProperty("isEnabled");
|
|
6886
|
+
if (prop != null)
|
|
6887
|
+
{
|
|
6888
|
+
prop.boolValue = true;
|
|
6889
|
+
serializedObj.ApplyModifiedProperties();
|
|
6890
|
+
EditorUtility.SetDirty(so);
|
|
6891
|
+
modified++;
|
|
6892
|
+
}
|
|
6893
|
+
}
|
|
6894
|
+
|
|
6895
|
+
AssetDatabase.SaveAssets();
|
|
6896
|
+
return $"Enabled {modified} ScriptableObjects";
|
|
6897
|
+
\`\`\`
|
|
6898
|
+
|
|
6899
|
+
## Batch Remove Component
|
|
6900
|
+
|
|
6901
|
+
\`\`\`csharp
|
|
6902
|
+
using UnityEditor;
|
|
6903
|
+
|
|
6904
|
+
GameObject[] selected = Selection.gameObjects;
|
|
6905
|
+
if (selected.Length == 0)
|
|
6906
|
+
{
|
|
6907
|
+
return "No GameObjects selected";
|
|
6908
|
+
}
|
|
6909
|
+
|
|
6910
|
+
int undoGroup = Undo.GetCurrentGroup();
|
|
6911
|
+
Undo.SetCurrentGroupName("Batch Remove Rigidbody");
|
|
6912
|
+
|
|
6913
|
+
int removedCount = 0;
|
|
6914
|
+
foreach (GameObject obj in selected)
|
|
6915
|
+
{
|
|
6916
|
+
Rigidbody rb = obj.GetComponent<Rigidbody>();
|
|
6917
|
+
if (rb != null)
|
|
6918
|
+
{
|
|
6919
|
+
Undo.DestroyObjectImmediate(rb);
|
|
6920
|
+
removedCount++;
|
|
6921
|
+
}
|
|
6922
|
+
}
|
|
6923
|
+
|
|
6924
|
+
Undo.CollapseUndoOperations(undoGroup);
|
|
6925
|
+
return $"Removed Rigidbody from {removedCount} objects";
|
|
6926
|
+
\`\`\`
|
|
6927
|
+
|
|
6928
|
+
## Batch Set Static Flags
|
|
6929
|
+
|
|
6930
|
+
\`\`\`csharp
|
|
6931
|
+
using UnityEditor;
|
|
6932
|
+
|
|
6933
|
+
GameObject[] selected = Selection.gameObjects;
|
|
6934
|
+
if (selected.Length == 0)
|
|
6935
|
+
{
|
|
6936
|
+
return "No GameObjects selected";
|
|
6937
|
+
}
|
|
6938
|
+
|
|
6939
|
+
int undoGroup = Undo.GetCurrentGroup();
|
|
6940
|
+
Undo.SetCurrentGroupName("Batch Set Static");
|
|
6941
|
+
|
|
6942
|
+
foreach (GameObject obj in selected)
|
|
6943
|
+
{
|
|
6944
|
+
Undo.RecordObject(obj, "");
|
|
6945
|
+
GameObjectUtility.SetStaticEditorFlags(obj, StaticEditorFlags.BatchingStatic | StaticEditorFlags.OccludeeStatic);
|
|
6946
|
+
}
|
|
6947
|
+
|
|
6948
|
+
Undo.CollapseUndoOperations(undoGroup);
|
|
6949
|
+
return $"Set static flags on {selected.Length} objects";
|
|
6950
|
+
\`\`\`
|
|
6951
|
+
|
|
6952
|
+
## Batch Process with Progress Bar
|
|
6953
|
+
|
|
6954
|
+
\`\`\`csharp
|
|
6955
|
+
using UnityEditor;
|
|
6956
|
+
|
|
6957
|
+
string[] guids = AssetDatabase.FindAssets("t:Texture2D");
|
|
6958
|
+
if (guids.Length == 0)
|
|
6959
|
+
{
|
|
6960
|
+
return "No textures found";
|
|
6961
|
+
}
|
|
6962
|
+
|
|
6963
|
+
int processed = 0;
|
|
6964
|
+
foreach (string guid in guids)
|
|
6965
|
+
{
|
|
6966
|
+
string path = AssetDatabase.GUIDToAssetPath(guid);
|
|
6967
|
+
TextureImporter importer = AssetImporter.GetAtPath(path) as TextureImporter;
|
|
6968
|
+
if (importer != null && importer.maxTextureSize > 1024)
|
|
6969
|
+
{
|
|
6970
|
+
importer.maxTextureSize = 1024;
|
|
6971
|
+
importer.SaveAndReimport();
|
|
6972
|
+
processed++;
|
|
6973
|
+
}
|
|
6974
|
+
|
|
6975
|
+
if (processed % 10 == 0)
|
|
6976
|
+
{
|
|
6977
|
+
EditorUtility.DisplayProgressBar("Processing Textures", path, (float)processed / guids.Length);
|
|
6978
|
+
}
|
|
6979
|
+
}
|
|
6980
|
+
|
|
6981
|
+
EditorUtility.ClearProgressBar();
|
|
6982
|
+
return $"Resized {processed} textures to max 1024";
|
|
6983
|
+
\`\`\`
|
|
6984
|
+
|
|
6985
|
+
## Batch Align Objects
|
|
6986
|
+
|
|
6987
|
+
\`\`\`csharp
|
|
6988
|
+
using UnityEditor;
|
|
6989
|
+
|
|
6990
|
+
GameObject[] selected = Selection.gameObjects;
|
|
6991
|
+
if (selected.Length < 2)
|
|
6992
|
+
{
|
|
6993
|
+
return "Select at least 2 objects";
|
|
6994
|
+
}
|
|
6995
|
+
|
|
6996
|
+
int undoGroup = Undo.GetCurrentGroup();
|
|
6997
|
+
Undo.SetCurrentGroupName("Align Objects");
|
|
6998
|
+
|
|
6999
|
+
float startX = selected[0].transform.position.x;
|
|
7000
|
+
float spacing = 2f;
|
|
7001
|
+
|
|
7002
|
+
for (int i = 0; i < selected.Length; i++)
|
|
7003
|
+
{
|
|
7004
|
+
Undo.RecordObject(selected[i].transform, "");
|
|
7005
|
+
Vector3 pos = selected[i].transform.position;
|
|
7006
|
+
pos.x = startX + (i * spacing);
|
|
7007
|
+
selected[i].transform.position = pos;
|
|
7008
|
+
}
|
|
7009
|
+
|
|
7010
|
+
Undo.CollapseUndoOperations(undoGroup);
|
|
7011
|
+
return $"Aligned {selected.Length} objects with {spacing}m spacing";
|
|
7012
|
+
\`\`\`
|
|
7013
|
+
|
|
7014
|
+
## Batch Rename Assets (Undo-supported)
|
|
7015
|
+
|
|
7016
|
+
\`\`\`csharp
|
|
7017
|
+
using UnityEditor;
|
|
7018
|
+
|
|
7019
|
+
// ObjectNames.SetNameSmart() supports Undo (AssetDatabase.RenameAsset() does NOT)
|
|
7020
|
+
Object[] selected = Selection.objects;
|
|
7021
|
+
if (selected.Length == 0)
|
|
7022
|
+
{
|
|
7023
|
+
return "No assets selected";
|
|
7024
|
+
}
|
|
7025
|
+
|
|
7026
|
+
for (int i = 0; i < selected.Length; i++)
|
|
7027
|
+
{
|
|
7028
|
+
string newName = $"{i:D3}_{selected[i].name}";
|
|
7029
|
+
ObjectNames.SetNameSmart(selected[i], newName);
|
|
7030
|
+
}
|
|
7031
|
+
|
|
7032
|
+
AssetDatabase.SaveAssets();
|
|
7033
|
+
return $"Renamed {selected.Length} assets";
|
|
7034
|
+
\`\`\`
|
|
7035
|
+
|
|
7036
|
+
## Batch Replace Material
|
|
7037
|
+
|
|
7038
|
+
\`\`\`csharp
|
|
7039
|
+
using UnityEditor;
|
|
7040
|
+
|
|
7041
|
+
GameObject[] selected = Selection.gameObjects;
|
|
7042
|
+
if (selected.Length == 0)
|
|
7043
|
+
{
|
|
7044
|
+
return "No GameObjects selected";
|
|
7045
|
+
}
|
|
7046
|
+
|
|
7047
|
+
string materialPath = "Assets/Materials/NewMaterial.mat";
|
|
7048
|
+
Material newMat = AssetDatabase.LoadAssetAtPath<Material>(materialPath);
|
|
7049
|
+
if (newMat == null)
|
|
7050
|
+
{
|
|
7051
|
+
return $"Material not found at {materialPath}";
|
|
7052
|
+
}
|
|
7053
|
+
|
|
7054
|
+
int undoGroup = Undo.GetCurrentGroup();
|
|
7055
|
+
Undo.SetCurrentGroupName("Batch Replace Material");
|
|
7056
|
+
|
|
7057
|
+
int replaced = 0;
|
|
7058
|
+
foreach (GameObject obj in selected)
|
|
7059
|
+
{
|
|
7060
|
+
MeshRenderer renderer = obj.GetComponent<MeshRenderer>();
|
|
7061
|
+
if (renderer != null)
|
|
7062
|
+
{
|
|
7063
|
+
Undo.RecordObject(renderer, "");
|
|
7064
|
+
renderer.sharedMaterial = newMat;
|
|
7065
|
+
replaced++;
|
|
7066
|
+
}
|
|
7067
|
+
}
|
|
7068
|
+
|
|
7069
|
+
Undo.CollapseUndoOperations(undoGroup);
|
|
7070
|
+
return $"Replaced material on {replaced} objects";
|
|
7071
|
+
\`\`\`
|
|
7072
|
+
|
|
7073
|
+
`,
|
|
7074
|
+
"examples/cleanup-operations.md": `# Cleanup Operations
|
|
7075
|
+
|
|
7076
|
+
Code examples for project cleanup operations using \`execute-dynamic-code\`.
|
|
7077
|
+
|
|
7078
|
+
## Detect Missing Scripts on GameObject
|
|
7079
|
+
|
|
7080
|
+
\`\`\`csharp
|
|
7081
|
+
using UnityEditor;
|
|
7082
|
+
|
|
7083
|
+
GameObject selected = Selection.activeGameObject;
|
|
7084
|
+
if (selected == null)
|
|
7085
|
+
{
|
|
7086
|
+
return "No GameObject selected";
|
|
7087
|
+
}
|
|
7088
|
+
|
|
7089
|
+
int missingCount = GameObjectUtility.GetMonoBehavioursWithMissingScriptCount(selected);
|
|
7090
|
+
return $"{selected.name} has {missingCount} missing script(s)";
|
|
7091
|
+
\`\`\`
|
|
7092
|
+
|
|
7093
|
+
## Remove Missing Scripts from GameObject
|
|
7094
|
+
|
|
7095
|
+
\`\`\`csharp
|
|
7096
|
+
using UnityEditor;
|
|
7097
|
+
|
|
7098
|
+
GameObject selected = Selection.activeGameObject;
|
|
7099
|
+
if (selected == null)
|
|
7100
|
+
{
|
|
7101
|
+
return "No GameObject selected";
|
|
7102
|
+
}
|
|
7103
|
+
|
|
7104
|
+
int removedCount = GameObjectUtility.RemoveMonoBehavioursWithMissingScript(selected);
|
|
7105
|
+
return $"Removed {removedCount} missing script(s) from {selected.name}";
|
|
7106
|
+
\`\`\`
|
|
7107
|
+
|
|
7108
|
+
## Scan Scene for Missing Scripts
|
|
7109
|
+
|
|
7110
|
+
\`\`\`csharp
|
|
7111
|
+
using UnityEditor;
|
|
7112
|
+
|
|
7113
|
+
GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
|
|
7114
|
+
List<string> objectsWithMissing = new List<string>();
|
|
7115
|
+
|
|
7116
|
+
foreach (GameObject obj in allObjects)
|
|
7117
|
+
{
|
|
7118
|
+
int count = GameObjectUtility.GetMonoBehavioursWithMissingScriptCount(obj);
|
|
7119
|
+
if (count > 0)
|
|
7120
|
+
{
|
|
7121
|
+
objectsWithMissing.Add($"{obj.name} ({count})");
|
|
7122
|
+
}
|
|
7123
|
+
}
|
|
7124
|
+
|
|
7125
|
+
if (objectsWithMissing.Count == 0)
|
|
7126
|
+
{
|
|
7127
|
+
return "No missing scripts found in scene";
|
|
7128
|
+
}
|
|
7129
|
+
|
|
7130
|
+
return $"Objects with missing scripts: {string.Join(", ", objectsWithMissing)}";
|
|
7131
|
+
\`\`\`
|
|
7132
|
+
|
|
7133
|
+
## Remove All Missing Scripts from Scene
|
|
7134
|
+
|
|
7135
|
+
\`\`\`csharp
|
|
7136
|
+
using UnityEditor;
|
|
7137
|
+
|
|
7138
|
+
GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
|
|
7139
|
+
int totalRemoved = 0;
|
|
7140
|
+
|
|
7141
|
+
int undoGroup = Undo.GetCurrentGroup();
|
|
7142
|
+
Undo.SetCurrentGroupName("Remove All Missing Scripts");
|
|
7143
|
+
|
|
7144
|
+
foreach (GameObject obj in allObjects)
|
|
7145
|
+
{
|
|
7146
|
+
int removed = GameObjectUtility.RemoveMonoBehavioursWithMissingScript(obj);
|
|
7147
|
+
totalRemoved += removed;
|
|
7148
|
+
}
|
|
7149
|
+
|
|
7150
|
+
Undo.CollapseUndoOperations(undoGroup);
|
|
7151
|
+
return $"Removed {totalRemoved} missing scripts from scene";
|
|
7152
|
+
\`\`\`
|
|
7153
|
+
|
|
7154
|
+
## Detect Missing References in Component
|
|
7155
|
+
|
|
7156
|
+
\`\`\`csharp
|
|
7157
|
+
using UnityEditor;
|
|
7158
|
+
|
|
7159
|
+
GameObject selected = Selection.activeGameObject;
|
|
7160
|
+
if (selected == null)
|
|
7161
|
+
{
|
|
7162
|
+
return "No GameObject selected";
|
|
7163
|
+
}
|
|
7164
|
+
|
|
7165
|
+
List<string> missingRefs = new List<string>();
|
|
7166
|
+
|
|
7167
|
+
Component[] components = selected.GetComponents<Component>();
|
|
7168
|
+
foreach (Component comp in components)
|
|
7169
|
+
{
|
|
7170
|
+
if (comp == null) continue;
|
|
7171
|
+
|
|
7172
|
+
SerializedObject so = new SerializedObject(comp);
|
|
7173
|
+
SerializedProperty prop = so.GetIterator();
|
|
7174
|
+
|
|
7175
|
+
while (prop.NextVisible(true))
|
|
7176
|
+
{
|
|
7177
|
+
if (prop.propertyType == SerializedPropertyType.ObjectReference)
|
|
7178
|
+
{
|
|
7179
|
+
if (prop.objectReferenceValue == null && prop.objectReferenceInstanceIDValue != 0)
|
|
7180
|
+
{
|
|
7181
|
+
missingRefs.Add($"{comp.GetType().Name}.{prop.name}");
|
|
7182
|
+
}
|
|
7183
|
+
}
|
|
7184
|
+
}
|
|
7185
|
+
}
|
|
7186
|
+
|
|
7187
|
+
if (missingRefs.Count == 0)
|
|
7188
|
+
{
|
|
7189
|
+
return "No missing references found";
|
|
7190
|
+
}
|
|
7191
|
+
|
|
7192
|
+
return $"Missing references: {string.Join(", ", missingRefs)}";
|
|
7193
|
+
\`\`\`
|
|
7194
|
+
|
|
7195
|
+
## Scan Scene for Missing References
|
|
7196
|
+
|
|
7197
|
+
\`\`\`csharp
|
|
7198
|
+
using UnityEditor;
|
|
7199
|
+
|
|
7200
|
+
GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
|
|
7201
|
+
List<string> results = new List<string>();
|
|
7202
|
+
|
|
7203
|
+
foreach (GameObject obj in allObjects)
|
|
7204
|
+
{
|
|
7205
|
+
Component[] components = obj.GetComponents<Component>();
|
|
7206
|
+
foreach (Component comp in components)
|
|
7207
|
+
{
|
|
7208
|
+
if (comp == null) continue;
|
|
7209
|
+
|
|
7210
|
+
SerializedObject so = new SerializedObject(comp);
|
|
7211
|
+
SerializedProperty prop = so.GetIterator();
|
|
7212
|
+
|
|
7213
|
+
while (prop.NextVisible(true))
|
|
7214
|
+
{
|
|
7215
|
+
if (prop.propertyType == SerializedPropertyType.ObjectReference)
|
|
7216
|
+
{
|
|
7217
|
+
if (prop.objectReferenceValue == null && prop.objectReferenceInstanceIDValue != 0)
|
|
7218
|
+
{
|
|
7219
|
+
results.Add($"{obj.name}/{comp.GetType().Name}.{prop.name}");
|
|
7220
|
+
}
|
|
7221
|
+
}
|
|
7222
|
+
}
|
|
7223
|
+
}
|
|
7224
|
+
}
|
|
7225
|
+
|
|
7226
|
+
if (results.Count == 0)
|
|
7227
|
+
{
|
|
7228
|
+
return "No missing references found in scene";
|
|
7229
|
+
}
|
|
7230
|
+
|
|
7231
|
+
return $"Missing references ({results.Count}): {string.Join(", ", results.Take(10))}...";
|
|
7232
|
+
\`\`\`
|
|
7233
|
+
|
|
7234
|
+
## Find Unused Materials in Project
|
|
7235
|
+
|
|
7236
|
+
\`\`\`csharp
|
|
7237
|
+
using UnityEditor;
|
|
7238
|
+
|
|
7239
|
+
string[] materialGuids = AssetDatabase.FindAssets("t:Material");
|
|
7240
|
+
HashSet<string> usedMaterials = new HashSet<string>();
|
|
7241
|
+
|
|
7242
|
+
string[] prefabGuids = AssetDatabase.FindAssets("t:Prefab");
|
|
7243
|
+
foreach (string guid in prefabGuids)
|
|
7244
|
+
{
|
|
7245
|
+
string path = AssetDatabase.GUIDToAssetPath(guid);
|
|
7246
|
+
string[] deps = AssetDatabase.GetDependencies(path, true);
|
|
7247
|
+
foreach (string dep in deps)
|
|
7248
|
+
{
|
|
7249
|
+
if (dep.EndsWith(".mat"))
|
|
7250
|
+
{
|
|
7251
|
+
usedMaterials.Add(dep);
|
|
7252
|
+
}
|
|
7253
|
+
}
|
|
7254
|
+
}
|
|
7255
|
+
|
|
7256
|
+
List<string> unusedMaterials = new List<string>();
|
|
7257
|
+
foreach (string guid in materialGuids)
|
|
7258
|
+
{
|
|
7259
|
+
string path = AssetDatabase.GUIDToAssetPath(guid);
|
|
7260
|
+
if (!usedMaterials.Contains(path))
|
|
7261
|
+
{
|
|
7262
|
+
unusedMaterials.Add(path);
|
|
7263
|
+
}
|
|
7264
|
+
}
|
|
7265
|
+
|
|
7266
|
+
return $"Found {unusedMaterials.Count} potentially unused materials";
|
|
7267
|
+
\`\`\`
|
|
7268
|
+
|
|
7269
|
+
## Find Empty GameObjects
|
|
7270
|
+
|
|
7271
|
+
\`\`\`csharp
|
|
7272
|
+
using UnityEditor;
|
|
7273
|
+
|
|
7274
|
+
GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
|
|
7275
|
+
List<string> emptyObjects = new List<string>();
|
|
7276
|
+
|
|
7277
|
+
foreach (GameObject obj in allObjects)
|
|
7278
|
+
{
|
|
7279
|
+
Component[] components = obj.GetComponents<Component>();
|
|
7280
|
+
if (components.Length == 1 && obj.transform.childCount == 0)
|
|
7281
|
+
{
|
|
7282
|
+
emptyObjects.Add(obj.name);
|
|
7283
|
+
}
|
|
7284
|
+
}
|
|
7285
|
+
|
|
7286
|
+
if (emptyObjects.Count == 0)
|
|
7287
|
+
{
|
|
7288
|
+
return "No empty GameObjects found";
|
|
7289
|
+
}
|
|
7290
|
+
|
|
7291
|
+
return $"Empty objects ({emptyObjects.Count}): {string.Join(", ", emptyObjects.Take(20))}";
|
|
7292
|
+
\`\`\`
|
|
7293
|
+
|
|
7294
|
+
## Find Duplicate Names in Hierarchy
|
|
7295
|
+
|
|
7296
|
+
\`\`\`csharp
|
|
7297
|
+
using UnityEditor;
|
|
7298
|
+
|
|
7299
|
+
GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
|
|
7300
|
+
Dictionary<string, int> nameCounts = new Dictionary<string, int>();
|
|
7301
|
+
|
|
7302
|
+
foreach (GameObject obj in allObjects)
|
|
7303
|
+
{
|
|
7304
|
+
if (nameCounts.ContainsKey(obj.name))
|
|
7305
|
+
{
|
|
7306
|
+
nameCounts[obj.name]++;
|
|
7307
|
+
}
|
|
7308
|
+
else
|
|
7309
|
+
{
|
|
7310
|
+
nameCounts[obj.name] = 1;
|
|
7311
|
+
}
|
|
7312
|
+
}
|
|
7313
|
+
|
|
7314
|
+
List<string> duplicates = new List<string>();
|
|
7315
|
+
foreach (KeyValuePair<string, int> kvp in nameCounts)
|
|
7316
|
+
{
|
|
7317
|
+
if (kvp.Value > 1)
|
|
7318
|
+
{
|
|
7319
|
+
duplicates.Add($"{kvp.Key} ({kvp.Value})");
|
|
7320
|
+
}
|
|
7321
|
+
}
|
|
7322
|
+
|
|
7323
|
+
if (duplicates.Count == 0)
|
|
7324
|
+
{
|
|
7325
|
+
return "No duplicate names found";
|
|
7326
|
+
}
|
|
7327
|
+
|
|
7328
|
+
return $"Duplicate names: {string.Join(", ", duplicates.Take(15))}";
|
|
7329
|
+
\`\`\`
|
|
7330
|
+
|
|
7331
|
+
## Check for Broken Prefab Instances
|
|
7332
|
+
|
|
7333
|
+
\`\`\`csharp
|
|
7334
|
+
using UnityEditor;
|
|
7335
|
+
|
|
7336
|
+
GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
|
|
7337
|
+
List<string> brokenPrefabs = new List<string>();
|
|
7338
|
+
|
|
7339
|
+
foreach (GameObject obj in allObjects)
|
|
7340
|
+
{
|
|
7341
|
+
if (PrefabUtility.IsPartOfPrefabInstance(obj))
|
|
7342
|
+
{
|
|
7343
|
+
GameObject prefabAsset = PrefabUtility.GetCorrespondingObjectFromSource(obj);
|
|
7344
|
+
if (prefabAsset == null)
|
|
7345
|
+
{
|
|
7346
|
+
brokenPrefabs.Add(obj.name);
|
|
7347
|
+
}
|
|
7348
|
+
}
|
|
7349
|
+
}
|
|
7350
|
+
|
|
7351
|
+
if (brokenPrefabs.Count == 0)
|
|
7352
|
+
{
|
|
7353
|
+
return "No broken prefab instances found";
|
|
7354
|
+
}
|
|
7355
|
+
|
|
7356
|
+
return $"Broken prefab instances: {string.Join(", ", brokenPrefabs)}";
|
|
7357
|
+
\`\`\`
|
|
7358
|
+
|
|
7359
|
+
## Find Objects with Negative Scale
|
|
7360
|
+
|
|
7361
|
+
\`\`\`csharp
|
|
7362
|
+
using UnityEditor;
|
|
7363
|
+
|
|
7364
|
+
GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
|
|
7365
|
+
List<string> negativeScale = new List<string>();
|
|
7366
|
+
|
|
7367
|
+
foreach (GameObject obj in allObjects)
|
|
7368
|
+
{
|
|
7369
|
+
Vector3 scale = obj.transform.localScale;
|
|
7370
|
+
if (scale.x < 0 || scale.y < 0 || scale.z < 0)
|
|
7371
|
+
{
|
|
7372
|
+
negativeScale.Add($"{obj.name} ({scale})");
|
|
7373
|
+
}
|
|
7374
|
+
}
|
|
7375
|
+
|
|
7376
|
+
if (negativeScale.Count == 0)
|
|
7377
|
+
{
|
|
7378
|
+
return "No objects with negative scale found";
|
|
7379
|
+
}
|
|
7380
|
+
|
|
7381
|
+
return $"Negative scale objects: {string.Join(", ", negativeScale.Take(10))}";
|
|
7382
|
+
\`\`\`
|
|
7383
|
+
|
|
7384
|
+
## Remove Empty Parent GameObjects
|
|
7385
|
+
|
|
7386
|
+
\`\`\`csharp
|
|
7387
|
+
using UnityEditor;
|
|
7388
|
+
|
|
7389
|
+
GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
|
|
7390
|
+
|
|
7391
|
+
int undoGroup = Undo.GetCurrentGroup();
|
|
7392
|
+
Undo.SetCurrentGroupName("Remove Empty Parents");
|
|
7393
|
+
|
|
7394
|
+
int removedCount = 0;
|
|
7395
|
+
foreach (GameObject obj in allObjects)
|
|
7396
|
+
{
|
|
7397
|
+
if (obj == null) continue;
|
|
7398
|
+
|
|
7399
|
+
Component[] components = obj.GetComponents<Component>();
|
|
7400
|
+
if (components.Length == 1 && obj.transform.childCount == 0)
|
|
7401
|
+
{
|
|
7402
|
+
Undo.DestroyObjectImmediate(obj);
|
|
7403
|
+
removedCount++;
|
|
7404
|
+
}
|
|
7405
|
+
}
|
|
7406
|
+
|
|
7407
|
+
Undo.CollapseUndoOperations(undoGroup);
|
|
7408
|
+
return $"Removed {removedCount} empty GameObjects";
|
|
7409
|
+
\`\`\`
|
|
7410
|
+
|
|
7411
|
+
## Find Large Meshes
|
|
7412
|
+
|
|
7413
|
+
\`\`\`csharp
|
|
7414
|
+
using UnityEditor;
|
|
7415
|
+
|
|
7416
|
+
string[] meshGuids = AssetDatabase.FindAssets("t:Mesh");
|
|
7417
|
+
List<string> largeMeshes = new List<string>();
|
|
7418
|
+
int threshold = 10000;
|
|
7419
|
+
|
|
7420
|
+
foreach (string guid in meshGuids)
|
|
7421
|
+
{
|
|
7422
|
+
string path = AssetDatabase.GUIDToAssetPath(guid);
|
|
7423
|
+
Mesh mesh = AssetDatabase.LoadAssetAtPath<Mesh>(path);
|
|
7424
|
+
if (mesh != null && mesh.vertexCount > threshold)
|
|
7425
|
+
{
|
|
7426
|
+
largeMeshes.Add($"{path} ({mesh.vertexCount} verts)");
|
|
7427
|
+
}
|
|
7428
|
+
}
|
|
7429
|
+
|
|
7430
|
+
if (largeMeshes.Count == 0)
|
|
7431
|
+
{
|
|
7432
|
+
return $"No meshes with more than {threshold} vertices found";
|
|
7433
|
+
}
|
|
7434
|
+
|
|
7435
|
+
return $"Large meshes: {string.Join(", ", largeMeshes.Take(10))}";
|
|
7436
|
+
\`\`\`
|
|
7437
|
+
|
|
7438
|
+
## Validate Asset References
|
|
7439
|
+
|
|
7440
|
+
\`\`\`csharp
|
|
7441
|
+
using UnityEditor;
|
|
7442
|
+
|
|
7443
|
+
string[] guids = AssetDatabase.FindAssets("t:ScriptableObject", new[] { "Assets/Data" });
|
|
7444
|
+
List<string> invalidRefs = new List<string>();
|
|
7445
|
+
|
|
7446
|
+
foreach (string guid in guids)
|
|
7447
|
+
{
|
|
7448
|
+
string path = AssetDatabase.GUIDToAssetPath(guid);
|
|
7449
|
+
ScriptableObject so = AssetDatabase.LoadAssetAtPath<ScriptableObject>(path);
|
|
7450
|
+
if (so == null) continue;
|
|
7451
|
+
|
|
7452
|
+
SerializedObject serializedObj = new SerializedObject(so);
|
|
7453
|
+
SerializedProperty prop = serializedObj.GetIterator();
|
|
7454
|
+
|
|
7455
|
+
while (prop.NextVisible(true))
|
|
7456
|
+
{
|
|
7457
|
+
if (prop.propertyType == SerializedPropertyType.ObjectReference)
|
|
7458
|
+
{
|
|
7459
|
+
if (prop.objectReferenceValue == null && prop.objectReferenceInstanceIDValue != 0)
|
|
7460
|
+
{
|
|
7461
|
+
invalidRefs.Add($"{path}: {prop.name}");
|
|
7462
|
+
}
|
|
7463
|
+
}
|
|
7464
|
+
}
|
|
7465
|
+
}
|
|
7466
|
+
|
|
7467
|
+
if (invalidRefs.Count == 0)
|
|
7468
|
+
{
|
|
7469
|
+
return "All asset references are valid";
|
|
7470
|
+
}
|
|
7471
|
+
|
|
7472
|
+
return $"Invalid references ({invalidRefs.Count}): {string.Join(", ", invalidRefs.Take(10))}";
|
|
7473
|
+
\`\`\`
|
|
7474
|
+
|
|
7475
|
+
`,
|
|
7476
|
+
"examples/material-operations.md": `# Material Operations
|
|
7477
|
+
|
|
7478
|
+
Code examples for Material operations using \`execute-dynamic-code\`.
|
|
7479
|
+
|
|
7480
|
+
## Create a New Material
|
|
7481
|
+
|
|
7482
|
+
\`\`\`csharp
|
|
7483
|
+
using UnityEditor;
|
|
7484
|
+
|
|
7485
|
+
Shader shader = Shader.Find("Standard");
|
|
7486
|
+
Material mat = new Material(shader);
|
|
7487
|
+
mat.name = "MyMaterial";
|
|
7488
|
+
string path = "Assets/Materials/MyMaterial.mat";
|
|
7489
|
+
AssetDatabase.CreateAsset(mat, path);
|
|
7490
|
+
AssetDatabase.SaveAssets();
|
|
7491
|
+
return $"Material created at {path}";
|
|
7492
|
+
\`\`\`
|
|
7493
|
+
|
|
7494
|
+
## Set Material Color
|
|
7495
|
+
|
|
7496
|
+
\`\`\`csharp
|
|
7497
|
+
using UnityEditor;
|
|
7498
|
+
|
|
7499
|
+
string matPath = "Assets/Materials/MyMaterial.mat";
|
|
7500
|
+
Material mat = AssetDatabase.LoadAssetAtPath<Material>(matPath);
|
|
7501
|
+
mat.SetColor("_Color", new Color(1f, 0.5f, 0f, 1f));
|
|
7502
|
+
EditorUtility.SetDirty(mat);
|
|
7503
|
+
AssetDatabase.SaveAssets();
|
|
7504
|
+
return "Material color set to orange";
|
|
7505
|
+
\`\`\`
|
|
7506
|
+
|
|
7507
|
+
## Set Material Properties (Float, Vector)
|
|
7508
|
+
|
|
7509
|
+
\`\`\`csharp
|
|
7510
|
+
using UnityEditor;
|
|
7511
|
+
|
|
7512
|
+
string matPath = "Assets/Materials/MyMaterial.mat";
|
|
7513
|
+
Material mat = AssetDatabase.LoadAssetAtPath<Material>(matPath);
|
|
7514
|
+
|
|
7515
|
+
mat.SetFloat("_Metallic", 0.8f);
|
|
7516
|
+
mat.SetFloat("_Glossiness", 0.6f);
|
|
7517
|
+
mat.SetVector("_EmissionColor", new Vector4(1, 1, 0, 1));
|
|
7518
|
+
|
|
7519
|
+
EditorUtility.SetDirty(mat);
|
|
7520
|
+
AssetDatabase.SaveAssets();
|
|
7521
|
+
return "Material properties updated";
|
|
7522
|
+
\`\`\`
|
|
7523
|
+
|
|
7524
|
+
## Assign Texture to Material
|
|
7525
|
+
|
|
7526
|
+
\`\`\`csharp
|
|
7527
|
+
using UnityEditor;
|
|
7528
|
+
|
|
7529
|
+
string matPath = "Assets/Materials/MyMaterial.mat";
|
|
7530
|
+
string texPath = "Assets/Textures/MyTexture.png";
|
|
7531
|
+
|
|
7532
|
+
Material mat = AssetDatabase.LoadAssetAtPath<Material>(matPath);
|
|
7533
|
+
Texture2D tex = AssetDatabase.LoadAssetAtPath<Texture2D>(texPath);
|
|
7534
|
+
|
|
7535
|
+
mat.SetTexture("_MainTex", tex);
|
|
7536
|
+
EditorUtility.SetDirty(mat);
|
|
7537
|
+
AssetDatabase.SaveAssets();
|
|
7538
|
+
return $"Assigned {tex.name} to material";
|
|
7539
|
+
\`\`\`
|
|
7540
|
+
|
|
7541
|
+
## Assign Material to GameObject
|
|
7542
|
+
|
|
7543
|
+
\`\`\`csharp
|
|
7544
|
+
using UnityEditor;
|
|
7545
|
+
|
|
7546
|
+
string matPath = "Assets/Materials/MyMaterial.mat";
|
|
7547
|
+
Material mat = AssetDatabase.LoadAssetAtPath<Material>(matPath);
|
|
7548
|
+
|
|
7549
|
+
GameObject selected = Selection.activeGameObject;
|
|
7550
|
+
if (selected == null)
|
|
7551
|
+
{
|
|
7552
|
+
return "No GameObject selected";
|
|
7553
|
+
}
|
|
7554
|
+
|
|
7555
|
+
Renderer renderer = selected.GetComponent<Renderer>();
|
|
7556
|
+
if (renderer == null)
|
|
7557
|
+
{
|
|
7558
|
+
return "Selected object has no Renderer";
|
|
7559
|
+
}
|
|
7560
|
+
|
|
7561
|
+
renderer.sharedMaterial = mat;
|
|
7562
|
+
EditorUtility.SetDirty(selected);
|
|
7563
|
+
return $"Assigned {mat.name} to {selected.name}";
|
|
7564
|
+
\`\`\`
|
|
7565
|
+
|
|
7566
|
+
## Enable/Disable Material Keywords
|
|
7567
|
+
|
|
7568
|
+
\`\`\`csharp
|
|
7569
|
+
using UnityEditor;
|
|
7570
|
+
|
|
7571
|
+
string matPath = "Assets/Materials/MyMaterial.mat";
|
|
7572
|
+
Material mat = AssetDatabase.LoadAssetAtPath<Material>(matPath);
|
|
7573
|
+
|
|
7574
|
+
mat.EnableKeyword("_EMISSION");
|
|
7575
|
+
mat.globalIlluminationFlags = MaterialGlobalIlluminationFlags.RealtimeEmissive;
|
|
7576
|
+
|
|
7577
|
+
EditorUtility.SetDirty(mat);
|
|
7578
|
+
AssetDatabase.SaveAssets();
|
|
7579
|
+
return "Emission enabled on material";
|
|
7580
|
+
\`\`\`
|
|
7581
|
+
|
|
7582
|
+
## Find All Materials Using a Shader
|
|
7583
|
+
|
|
7584
|
+
\`\`\`csharp
|
|
7585
|
+
using UnityEditor;
|
|
7586
|
+
|
|
7587
|
+
string shaderName = "Standard";
|
|
7588
|
+
string[] guids = AssetDatabase.FindAssets("t:Material");
|
|
7589
|
+
List<string> matchingMaterials = new List<string>();
|
|
7590
|
+
|
|
7591
|
+
foreach (string guid in guids)
|
|
7592
|
+
{
|
|
7593
|
+
string path = AssetDatabase.GUIDToAssetPath(guid);
|
|
7594
|
+
Material mat = AssetDatabase.LoadAssetAtPath<Material>(path);
|
|
7595
|
+
if (mat != null && mat.shader != null && mat.shader.name == shaderName)
|
|
7596
|
+
{
|
|
7597
|
+
matchingMaterials.Add(path);
|
|
7598
|
+
}
|
|
7599
|
+
}
|
|
7600
|
+
return $"Found {matchingMaterials.Count} materials using {shaderName}";
|
|
7601
|
+
\`\`\`
|
|
7602
|
+
|
|
7603
|
+
## Duplicate Material
|
|
7604
|
+
|
|
7605
|
+
\`\`\`csharp
|
|
7606
|
+
using UnityEditor;
|
|
7607
|
+
|
|
7608
|
+
string sourcePath = "Assets/Materials/MyMaterial.mat";
|
|
7609
|
+
string destPath = "Assets/Materials/MyMaterial_Copy.mat";
|
|
7610
|
+
|
|
7611
|
+
Material source = AssetDatabase.LoadAssetAtPath<Material>(sourcePath);
|
|
7612
|
+
Material copy = new Material(source);
|
|
7613
|
+
AssetDatabase.CreateAsset(copy, destPath);
|
|
7614
|
+
AssetDatabase.SaveAssets();
|
|
7615
|
+
return $"Material duplicated to {destPath}";
|
|
7616
|
+
\`\`\`
|
|
7617
|
+
`,
|
|
7618
|
+
"examples/prefab-operations.md": `# Prefab Operations
|
|
7619
|
+
|
|
7620
|
+
Code examples for Prefab operations using \`execute-dynamic-code\`.
|
|
7621
|
+
|
|
7622
|
+
## Create a Prefab from GameObject
|
|
7623
|
+
|
|
7624
|
+
\`\`\`csharp
|
|
7625
|
+
using UnityEditor;
|
|
7626
|
+
|
|
7627
|
+
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
|
7628
|
+
cube.name = "MyCube";
|
|
7629
|
+
string path = "Assets/Prefabs/MyCube.prefab";
|
|
7630
|
+
PrefabUtility.SaveAsPrefabAsset(cube, path);
|
|
7631
|
+
Object.DestroyImmediate(cube);
|
|
7632
|
+
return $"Prefab created at {path}";
|
|
7633
|
+
\`\`\`
|
|
7634
|
+
|
|
7635
|
+
## Instantiate a Prefab
|
|
7636
|
+
|
|
7637
|
+
\`\`\`csharp
|
|
7638
|
+
using UnityEditor;
|
|
7639
|
+
|
|
7640
|
+
string prefabPath = "Assets/Prefabs/MyCube.prefab";
|
|
7641
|
+
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
|
|
7642
|
+
if (prefab == null)
|
|
7643
|
+
{
|
|
7644
|
+
return $"Prefab not found at {prefabPath}";
|
|
7645
|
+
}
|
|
7646
|
+
|
|
7647
|
+
GameObject instance = (GameObject)PrefabUtility.InstantiatePrefab(prefab);
|
|
7648
|
+
instance.transform.position = new Vector3(0, 1, 0);
|
|
7649
|
+
return $"Instantiated {instance.name}";
|
|
7650
|
+
\`\`\`
|
|
7651
|
+
|
|
7652
|
+
## Add Component to Prefab
|
|
7653
|
+
|
|
7654
|
+
\`\`\`csharp
|
|
7655
|
+
using UnityEditor;
|
|
7656
|
+
|
|
7657
|
+
string prefabPath = "Assets/Prefabs/MyCube.prefab";
|
|
7658
|
+
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
|
|
7659
|
+
string assetPath = AssetDatabase.GetAssetPath(prefab);
|
|
7660
|
+
|
|
7661
|
+
using (PrefabUtility.EditPrefabContentsScope scope = new PrefabUtility.EditPrefabContentsScope(assetPath))
|
|
7662
|
+
{
|
|
7663
|
+
GameObject root = scope.prefabContentsRoot;
|
|
7664
|
+
if (root.GetComponent<Rigidbody>() == null)
|
|
7665
|
+
{
|
|
7666
|
+
root.AddComponent<Rigidbody>();
|
|
7667
|
+
}
|
|
7668
|
+
}
|
|
7669
|
+
return "Added Rigidbody to prefab";
|
|
7670
|
+
\`\`\`
|
|
7671
|
+
|
|
7672
|
+
## Modify Prefab Properties
|
|
7673
|
+
|
|
7674
|
+
\`\`\`csharp
|
|
7675
|
+
using UnityEditor;
|
|
7676
|
+
|
|
7677
|
+
string prefabPath = "Assets/Prefabs/MyCube.prefab";
|
|
7678
|
+
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
|
|
7679
|
+
|
|
7680
|
+
using (PrefabUtility.EditPrefabContentsScope scope = new PrefabUtility.EditPrefabContentsScope(prefabPath))
|
|
7681
|
+
{
|
|
7682
|
+
GameObject root = scope.prefabContentsRoot;
|
|
7683
|
+
root.transform.localScale = new Vector3(2, 2, 2);
|
|
7684
|
+
|
|
7685
|
+
MeshRenderer renderer = root.GetComponent<MeshRenderer>();
|
|
7686
|
+
if (renderer != null)
|
|
7687
|
+
{
|
|
7688
|
+
renderer.sharedMaterial.color = Color.red;
|
|
7689
|
+
}
|
|
7690
|
+
}
|
|
7691
|
+
return "Modified prefab properties";
|
|
7692
|
+
\`\`\`
|
|
7693
|
+
|
|
7694
|
+
## Find All Prefab Instances in Scene
|
|
7695
|
+
|
|
7696
|
+
\`\`\`csharp
|
|
7697
|
+
using UnityEditor;
|
|
7698
|
+
using System.Collections.Generic;
|
|
7699
|
+
|
|
7700
|
+
string prefabPath = "Assets/Prefabs/MyCube.prefab";
|
|
7701
|
+
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
|
|
7702
|
+
if (prefab == null)
|
|
7703
|
+
{
|
|
7704
|
+
return $"Prefab not found at {prefabPath}";
|
|
7705
|
+
}
|
|
7706
|
+
|
|
7707
|
+
List<GameObject> instances = new List<GameObject>();
|
|
7708
|
+
|
|
7709
|
+
foreach (GameObject obj in Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None))
|
|
7710
|
+
{
|
|
7711
|
+
if (PrefabUtility.GetCorrespondingObjectFromSource(obj) == prefab)
|
|
7712
|
+
{
|
|
7713
|
+
instances.Add(obj);
|
|
7714
|
+
}
|
|
7715
|
+
}
|
|
7716
|
+
return $"Found {instances.Count} instances of {prefab.name}";
|
|
7717
|
+
\`\`\`
|
|
7718
|
+
|
|
7719
|
+
## Apply Prefab Overrides
|
|
7720
|
+
|
|
7721
|
+
\`\`\`csharp
|
|
7722
|
+
using UnityEditor;
|
|
7723
|
+
|
|
7724
|
+
GameObject selected = Selection.activeGameObject;
|
|
7725
|
+
if (selected == null)
|
|
7726
|
+
{
|
|
7727
|
+
return "No GameObject selected";
|
|
7728
|
+
}
|
|
7729
|
+
|
|
7730
|
+
if (!PrefabUtility.IsPartOfPrefabInstance(selected))
|
|
7731
|
+
{
|
|
7732
|
+
return "Selected object is not a prefab instance";
|
|
7733
|
+
}
|
|
7734
|
+
|
|
7735
|
+
PrefabUtility.ApplyPrefabInstance(selected, InteractionMode.UserAction);
|
|
7736
|
+
return $"Applied overrides from {selected.name} to prefab";
|
|
7737
|
+
\`\`\`
|
|
7738
|
+
`,
|
|
7739
|
+
"examples/scene-operations.md": `# Scene Operations
|
|
7740
|
+
|
|
7741
|
+
Code examples for Scene and Hierarchy operations using \`execute-dynamic-code\`.
|
|
7742
|
+
|
|
7743
|
+
## Create GameObject
|
|
7744
|
+
|
|
7745
|
+
\`\`\`csharp
|
|
7746
|
+
GameObject obj = new GameObject("MyObject");
|
|
7747
|
+
obj.transform.position = new Vector3(0, 1, 0);
|
|
7748
|
+
return $"Created {obj.name}";
|
|
7749
|
+
\`\`\`
|
|
7750
|
+
|
|
7751
|
+
## Create UI GameObject (under Canvas)
|
|
7752
|
+
|
|
7753
|
+
\`\`\`csharp
|
|
7754
|
+
using UnityEngine.UI;
|
|
7755
|
+
|
|
7756
|
+
// UI objects require RectTransform, which is auto-added when parented to Canvas
|
|
7757
|
+
GameObject canvas = GameObject.Find("Canvas");
|
|
7758
|
+
if (canvas == null)
|
|
7759
|
+
{
|
|
7760
|
+
return "Canvas not found in scene";
|
|
7761
|
+
}
|
|
7762
|
+
|
|
7763
|
+
GameObject uiObj = new GameObject("MyUIElement");
|
|
7764
|
+
uiObj.transform.SetParent(canvas.transform, false);
|
|
7765
|
+
uiObj.AddComponent<RectTransform>();
|
|
7766
|
+
uiObj.AddComponent<Image>();
|
|
7767
|
+
return $"Created UI element: {uiObj.name}";
|
|
7768
|
+
\`\`\`
|
|
7769
|
+
|
|
7770
|
+
## Create Primitive
|
|
7771
|
+
|
|
7772
|
+
\`\`\`csharp
|
|
7773
|
+
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
|
7774
|
+
cube.name = "MyCube";
|
|
7775
|
+
cube.transform.position = new Vector3(2, 0, 0);
|
|
7776
|
+
return $"Created {cube.name}";
|
|
7777
|
+
\`\`\`
|
|
7778
|
+
|
|
7779
|
+
## Add Component to GameObject
|
|
7780
|
+
|
|
7781
|
+
\`\`\`csharp
|
|
7782
|
+
GameObject selected = Selection.activeGameObject;
|
|
7783
|
+
if (selected == null)
|
|
7784
|
+
{
|
|
7785
|
+
return "No GameObject selected";
|
|
7786
|
+
}
|
|
7787
|
+
|
|
7788
|
+
Rigidbody rb = selected.AddComponent<Rigidbody>();
|
|
7789
|
+
rb.mass = 2f;
|
|
7790
|
+
rb.useGravity = true;
|
|
7791
|
+
return $"Added Rigidbody to {selected.name}";
|
|
7792
|
+
\`\`\`
|
|
7793
|
+
|
|
7794
|
+
## Find GameObject by Name
|
|
7795
|
+
|
|
7796
|
+
\`\`\`csharp
|
|
7797
|
+
GameObject obj = GameObject.Find("Player");
|
|
7798
|
+
if (obj == null)
|
|
7799
|
+
{
|
|
7800
|
+
return "GameObject 'Player' not found";
|
|
7801
|
+
}
|
|
7802
|
+
return $"Found: {obj.name} at {obj.transform.position}";
|
|
7803
|
+
\`\`\`
|
|
7804
|
+
|
|
7805
|
+
## Find GameObjects by Tag
|
|
7806
|
+
|
|
7807
|
+
\`\`\`csharp
|
|
7808
|
+
GameObject[] enemies = GameObject.FindGameObjectsWithTag("Enemy");
|
|
7809
|
+
return $"Found {enemies.Length} GameObjects with tag 'Enemy'";
|
|
7810
|
+
\`\`\`
|
|
7811
|
+
|
|
7812
|
+
## Set Parent
|
|
7813
|
+
|
|
7814
|
+
\`\`\`csharp
|
|
7815
|
+
GameObject child = GameObject.Find("Child");
|
|
7816
|
+
GameObject parent = GameObject.Find("Parent");
|
|
7817
|
+
|
|
7818
|
+
if (child == null || parent == null)
|
|
7819
|
+
{
|
|
7820
|
+
return "Child or Parent not found";
|
|
7821
|
+
}
|
|
7822
|
+
|
|
7823
|
+
child.transform.SetParent(parent.transform);
|
|
7824
|
+
return $"Set {child.name}'s parent to {parent.name}";
|
|
7825
|
+
\`\`\`
|
|
7826
|
+
|
|
7827
|
+
## Get All Children
|
|
7828
|
+
|
|
7829
|
+
\`\`\`csharp
|
|
7830
|
+
GameObject parent = Selection.activeGameObject;
|
|
7831
|
+
if (parent == null)
|
|
7832
|
+
{
|
|
7833
|
+
return "No GameObject selected";
|
|
7834
|
+
}
|
|
7835
|
+
|
|
7836
|
+
List<string> children = new List<string>();
|
|
7837
|
+
foreach (Transform child in parent.transform)
|
|
7838
|
+
{
|
|
7839
|
+
children.Add(child.name);
|
|
7840
|
+
}
|
|
7841
|
+
return $"Children: {string.Join(", ", children)}";
|
|
7842
|
+
\`\`\`
|
|
7843
|
+
|
|
7844
|
+
## Wire Component References
|
|
7845
|
+
|
|
7846
|
+
\`\`\`csharp
|
|
7847
|
+
using UnityEditor;
|
|
7848
|
+
|
|
7849
|
+
GameObject player = GameObject.Find("Player");
|
|
7850
|
+
GameObject target = GameObject.Find("Target");
|
|
7851
|
+
|
|
7852
|
+
if (player == null || target == null)
|
|
7853
|
+
{
|
|
7854
|
+
return "Player or Target not found";
|
|
7855
|
+
}
|
|
7856
|
+
|
|
7857
|
+
MonoBehaviour script = player.GetComponent("PlayerController") as MonoBehaviour;
|
|
7858
|
+
if (script == null)
|
|
7859
|
+
{
|
|
7860
|
+
return "PlayerController not found on Player";
|
|
7861
|
+
}
|
|
7862
|
+
|
|
7863
|
+
SerializedObject serializedScript = new SerializedObject(script);
|
|
7864
|
+
SerializedProperty targetProp = serializedScript.FindProperty("target");
|
|
7865
|
+
|
|
7866
|
+
if (targetProp != null)
|
|
7867
|
+
{
|
|
7868
|
+
targetProp.objectReferenceValue = target.transform;
|
|
7869
|
+
serializedScript.ApplyModifiedProperties();
|
|
7870
|
+
return "Target reference wired";
|
|
7871
|
+
}
|
|
7872
|
+
return "Property 'target' not found";
|
|
7873
|
+
\`\`\`
|
|
7874
|
+
|
|
7875
|
+
## Load Scene (Editor)
|
|
7876
|
+
|
|
7877
|
+
\`\`\`csharp
|
|
7878
|
+
using UnityEditor.SceneManagement;
|
|
7879
|
+
|
|
7880
|
+
string scenePath = "Assets/Scenes/MainMenu.unity";
|
|
7881
|
+
EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Single);
|
|
7882
|
+
return $"Loaded scene: {scenePath}";
|
|
7883
|
+
\`\`\`
|
|
7884
|
+
|
|
7885
|
+
## Save Current Scene
|
|
7886
|
+
|
|
7887
|
+
\`\`\`csharp
|
|
7888
|
+
using UnityEditor.SceneManagement;
|
|
7889
|
+
|
|
7890
|
+
UnityEngine.SceneManagement.Scene scene = EditorSceneManager.GetActiveScene();
|
|
7891
|
+
EditorSceneManager.SaveScene(scene);
|
|
7892
|
+
return $"Saved scene: {scene.name}";
|
|
7893
|
+
\`\`\`
|
|
7894
|
+
|
|
7895
|
+
## Create New Scene
|
|
7896
|
+
|
|
7897
|
+
\`\`\`csharp
|
|
7898
|
+
using UnityEditor.SceneManagement;
|
|
7899
|
+
|
|
7900
|
+
UnityEngine.SceneManagement.Scene newScene = EditorSceneManager.NewScene(NewSceneSetup.DefaultGameObjects, NewSceneMode.Single);
|
|
7901
|
+
return $"Created new scene: {newScene.name}";
|
|
7902
|
+
\`\`\`
|
|
7903
|
+
|
|
7904
|
+
## Get All Root GameObjects in Scene
|
|
7905
|
+
|
|
7906
|
+
\`\`\`csharp
|
|
7907
|
+
UnityEngine.SceneManagement.Scene scene = UnityEngine.SceneManagement.SceneManager.GetActiveScene();
|
|
7908
|
+
GameObject[] roots = scene.GetRootGameObjects();
|
|
7909
|
+
|
|
7910
|
+
List<string> names = new List<string>();
|
|
7911
|
+
foreach (GameObject root in roots)
|
|
7912
|
+
{
|
|
7913
|
+
names.Add(root.name);
|
|
7914
|
+
}
|
|
7915
|
+
return $"Root objects: {string.Join(", ", names)}";
|
|
7916
|
+
\`\`\`
|
|
7917
|
+
|
|
7918
|
+
## Destroy GameObject
|
|
7919
|
+
|
|
7920
|
+
\`\`\`csharp
|
|
7921
|
+
GameObject obj = GameObject.Find("OldObject");
|
|
7922
|
+
if (obj == null)
|
|
7923
|
+
{
|
|
7924
|
+
return "GameObject not found";
|
|
7925
|
+
}
|
|
7926
|
+
|
|
7927
|
+
Object.DestroyImmediate(obj);
|
|
7928
|
+
return "GameObject destroyed";
|
|
7929
|
+
\`\`\`
|
|
7930
|
+
|
|
7931
|
+
## Duplicate GameObject
|
|
7932
|
+
|
|
7933
|
+
\`\`\`csharp
|
|
7934
|
+
GameObject selected = Selection.activeGameObject;
|
|
7935
|
+
if (selected == null)
|
|
7936
|
+
{
|
|
7937
|
+
return "No GameObject selected";
|
|
7938
|
+
}
|
|
7939
|
+
|
|
7940
|
+
GameObject copy = Object.Instantiate(selected);
|
|
7941
|
+
copy.name = selected.name + "_Copy";
|
|
7942
|
+
copy.transform.position = selected.transform.position + Vector3.right * 2;
|
|
7943
|
+
return $"Created duplicate: {copy.name}";
|
|
7944
|
+
\`\`\`
|
|
7945
|
+
|
|
7946
|
+
## Set Active/Inactive
|
|
7947
|
+
|
|
7948
|
+
\`\`\`csharp
|
|
7949
|
+
GameObject obj = GameObject.Find("MyObject");
|
|
7950
|
+
if (obj == null)
|
|
7951
|
+
{
|
|
7952
|
+
return "GameObject not found";
|
|
7953
|
+
}
|
|
7954
|
+
|
|
7955
|
+
obj.SetActive(!obj.activeSelf);
|
|
7956
|
+
return $"{obj.name} is now {(obj.activeSelf ? "active" : "inactive")}";
|
|
7957
|
+
\`\`\`
|
|
7958
|
+
|
|
7959
|
+
## Modify Transform
|
|
7960
|
+
|
|
7961
|
+
\`\`\`csharp
|
|
7962
|
+
GameObject selected = Selection.activeGameObject;
|
|
7963
|
+
if (selected == null)
|
|
7964
|
+
{
|
|
7965
|
+
return "No GameObject selected";
|
|
7966
|
+
}
|
|
7967
|
+
|
|
7968
|
+
selected.transform.position = new Vector3(0, 5, 0);
|
|
7969
|
+
selected.transform.rotation = Quaternion.Euler(0, 45, 0);
|
|
7970
|
+
selected.transform.localScale = new Vector3(2, 2, 2);
|
|
7971
|
+
return "Transform modified";
|
|
7972
|
+
\`\`\`
|
|
7973
|
+
`,
|
|
7974
|
+
"examples/scriptableobject.md": `# ScriptableObject Operations
|
|
7975
|
+
|
|
7976
|
+
Code examples for ScriptableObject operations using \`execute-dynamic-code\`.
|
|
7977
|
+
|
|
7978
|
+
## Create ScriptableObject Instance
|
|
7979
|
+
|
|
7980
|
+
\`\`\`csharp
|
|
7981
|
+
using UnityEditor;
|
|
7982
|
+
|
|
7983
|
+
ScriptableObject so = ScriptableObject.CreateInstance<ScriptableObject>();
|
|
7984
|
+
string path = "Assets/Data/MyData.asset";
|
|
7985
|
+
AssetDatabase.CreateAsset(so, path);
|
|
7986
|
+
AssetDatabase.SaveAssets();
|
|
7987
|
+
return $"ScriptableObject created at {path}";
|
|
7988
|
+
\`\`\`
|
|
7989
|
+
|
|
7990
|
+
## Create Custom ScriptableObject
|
|
7991
|
+
|
|
7992
|
+
\`\`\`csharp
|
|
7993
|
+
using UnityEditor;
|
|
7994
|
+
|
|
7995
|
+
ScriptableObject so = ScriptableObject.CreateInstance("MyCustomSO");
|
|
7996
|
+
if (so == null)
|
|
7997
|
+
{
|
|
7998
|
+
return "Type 'MyCustomSO' not found. Ensure the class exists.";
|
|
7999
|
+
}
|
|
8000
|
+
|
|
8001
|
+
string path = "Assets/Data/MyCustomData.asset";
|
|
8002
|
+
AssetDatabase.CreateAsset(so, path);
|
|
8003
|
+
AssetDatabase.SaveAssets();
|
|
8004
|
+
return $"Created {so.GetType().Name} at {path}";
|
|
8005
|
+
\`\`\`
|
|
8006
|
+
|
|
8007
|
+
## Modify ScriptableObject with SerializedObject
|
|
8008
|
+
|
|
8009
|
+
\`\`\`csharp
|
|
8010
|
+
using UnityEditor;
|
|
8011
|
+
|
|
8012
|
+
string path = "Assets/Data/MyData.asset";
|
|
8013
|
+
ScriptableObject so = AssetDatabase.LoadAssetAtPath<ScriptableObject>(path);
|
|
8014
|
+
|
|
8015
|
+
if (so == null)
|
|
8016
|
+
{
|
|
8017
|
+
return $"Asset not found at {path}";
|
|
8018
|
+
}
|
|
8019
|
+
|
|
8020
|
+
SerializedObject serializedObj = new SerializedObject(so);
|
|
8021
|
+
SerializedProperty prop = serializedObj.FindProperty("myField");
|
|
8022
|
+
|
|
8023
|
+
if (prop != null)
|
|
8024
|
+
{
|
|
8025
|
+
prop.stringValue = "New Value";
|
|
8026
|
+
serializedObj.ApplyModifiedProperties();
|
|
8027
|
+
EditorUtility.SetDirty(so);
|
|
8028
|
+
AssetDatabase.SaveAssets();
|
|
8029
|
+
return "Property updated";
|
|
8030
|
+
}
|
|
8031
|
+
return "Property 'myField' not found";
|
|
8032
|
+
\`\`\`
|
|
8033
|
+
|
|
8034
|
+
## Set Int/Float/Bool Properties
|
|
8035
|
+
|
|
8036
|
+
\`\`\`csharp
|
|
8037
|
+
using UnityEditor;
|
|
8038
|
+
|
|
8039
|
+
string path = "Assets/Data/GameSettings.asset";
|
|
8040
|
+
ScriptableObject so = AssetDatabase.LoadAssetAtPath<ScriptableObject>(path);
|
|
8041
|
+
|
|
8042
|
+
SerializedObject serializedObj = new SerializedObject(so);
|
|
8043
|
+
|
|
8044
|
+
SerializedProperty intProp = serializedObj.FindProperty("maxHealth");
|
|
8045
|
+
if (intProp != null) intProp.intValue = 100;
|
|
8046
|
+
|
|
8047
|
+
SerializedProperty floatProp = serializedObj.FindProperty("moveSpeed");
|
|
8048
|
+
if (floatProp != null) floatProp.floatValue = 5.5f;
|
|
8049
|
+
|
|
8050
|
+
SerializedProperty boolProp = serializedObj.FindProperty("isEnabled");
|
|
8051
|
+
if (boolProp != null) boolProp.boolValue = true;
|
|
8052
|
+
|
|
8053
|
+
serializedObj.ApplyModifiedProperties();
|
|
8054
|
+
EditorUtility.SetDirty(so);
|
|
8055
|
+
AssetDatabase.SaveAssets();
|
|
8056
|
+
return "Properties updated";
|
|
8057
|
+
\`\`\`
|
|
8058
|
+
|
|
8059
|
+
## Set Reference Properties
|
|
8060
|
+
|
|
8061
|
+
\`\`\`csharp
|
|
8062
|
+
using UnityEditor;
|
|
8063
|
+
|
|
8064
|
+
string soPath = "Assets/Data/CharacterData.asset";
|
|
8065
|
+
string prefabPath = "Assets/Prefabs/Player.prefab";
|
|
8066
|
+
|
|
8067
|
+
ScriptableObject so = AssetDatabase.LoadAssetAtPath<ScriptableObject>(soPath);
|
|
8068
|
+
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
|
|
8069
|
+
|
|
8070
|
+
if (so == null)
|
|
8071
|
+
{
|
|
8072
|
+
return $"ScriptableObject not found at {soPath}";
|
|
8073
|
+
}
|
|
8074
|
+
if (prefab == null)
|
|
8075
|
+
{
|
|
8076
|
+
return $"Prefab not found at {prefabPath}";
|
|
8077
|
+
}
|
|
8078
|
+
|
|
8079
|
+
SerializedObject serializedObj = new SerializedObject(so);
|
|
8080
|
+
SerializedProperty prop = serializedObj.FindProperty("playerPrefab");
|
|
8081
|
+
|
|
8082
|
+
if (prop != null)
|
|
8083
|
+
{
|
|
8084
|
+
prop.objectReferenceValue = prefab;
|
|
8085
|
+
serializedObj.ApplyModifiedProperties();
|
|
8086
|
+
EditorUtility.SetDirty(so);
|
|
8087
|
+
AssetDatabase.SaveAssets();
|
|
8088
|
+
return "Reference set successfully";
|
|
8089
|
+
}
|
|
8090
|
+
return "Property not found";
|
|
8091
|
+
\`\`\`
|
|
8092
|
+
|
|
8093
|
+
## Set Array/List Properties
|
|
8094
|
+
|
|
8095
|
+
\`\`\`csharp
|
|
8096
|
+
using UnityEditor;
|
|
8097
|
+
|
|
8098
|
+
string path = "Assets/Data/ItemDatabase.asset";
|
|
8099
|
+
ScriptableObject so = AssetDatabase.LoadAssetAtPath<ScriptableObject>(path);
|
|
8100
|
+
|
|
8101
|
+
if (so == null)
|
|
8102
|
+
{
|
|
8103
|
+
return $"Asset not found at {path}";
|
|
8104
|
+
}
|
|
8105
|
+
|
|
8106
|
+
SerializedObject serializedObj = new SerializedObject(so);
|
|
8107
|
+
SerializedProperty arrayProp = serializedObj.FindProperty("items");
|
|
8108
|
+
|
|
8109
|
+
if (arrayProp != null && arrayProp.isArray)
|
|
8110
|
+
{
|
|
8111
|
+
arrayProp.ClearArray();
|
|
8112
|
+
arrayProp.InsertArrayElementAtIndex(0);
|
|
8113
|
+
arrayProp.GetArrayElementAtIndex(0).stringValue = "Sword";
|
|
8114
|
+
arrayProp.InsertArrayElementAtIndex(1);
|
|
8115
|
+
arrayProp.GetArrayElementAtIndex(1).stringValue = "Shield";
|
|
8116
|
+
|
|
8117
|
+
serializedObj.ApplyModifiedProperties();
|
|
8118
|
+
EditorUtility.SetDirty(so);
|
|
8119
|
+
AssetDatabase.SaveAssets();
|
|
8120
|
+
return "Array updated with 2 items";
|
|
8121
|
+
}
|
|
8122
|
+
return "Array property not found";
|
|
8123
|
+
\`\`\`
|
|
8124
|
+
|
|
8125
|
+
## Find All ScriptableObjects of Type
|
|
8126
|
+
|
|
8127
|
+
\`\`\`csharp
|
|
8128
|
+
using UnityEditor;
|
|
8129
|
+
using System.Collections.Generic;
|
|
8130
|
+
|
|
8131
|
+
string typeName = "GameSettings";
|
|
8132
|
+
string[] guids = AssetDatabase.FindAssets($"t:{typeName}");
|
|
8133
|
+
List<string> paths = new List<string>();
|
|
8134
|
+
|
|
8135
|
+
foreach (string guid in guids)
|
|
8136
|
+
{
|
|
8137
|
+
paths.Add(AssetDatabase.GUIDToAssetPath(guid));
|
|
8138
|
+
}
|
|
8139
|
+
return $"Found {paths.Count} {typeName} assets";
|
|
8140
|
+
\`\`\`
|
|
8141
|
+
|
|
8142
|
+
## Duplicate ScriptableObject
|
|
8143
|
+
|
|
8144
|
+
\`\`\`csharp
|
|
8145
|
+
using UnityEditor;
|
|
8146
|
+
|
|
8147
|
+
string sourcePath = "Assets/Data/Template.asset";
|
|
8148
|
+
string destPath = "Assets/Data/NewInstance.asset";
|
|
8149
|
+
|
|
8150
|
+
ScriptableObject source = AssetDatabase.LoadAssetAtPath<ScriptableObject>(sourcePath);
|
|
8151
|
+
if (source == null)
|
|
8152
|
+
{
|
|
8153
|
+
return $"Source asset not found at {sourcePath}";
|
|
8154
|
+
}
|
|
8155
|
+
|
|
8156
|
+
ScriptableObject copy = Object.Instantiate(source);
|
|
8157
|
+
AssetDatabase.CreateAsset(copy, destPath);
|
|
8158
|
+
AssetDatabase.SaveAssets();
|
|
8159
|
+
return $"Duplicated to {destPath}";
|
|
8160
|
+
\`\`\`
|
|
8161
|
+
|
|
8162
|
+
## List All Properties of ScriptableObject
|
|
8163
|
+
|
|
8164
|
+
\`\`\`csharp
|
|
8165
|
+
using UnityEditor;
|
|
8166
|
+
using System.Collections.Generic;
|
|
8167
|
+
|
|
8168
|
+
string path = "Assets/Data/MyData.asset";
|
|
8169
|
+
ScriptableObject so = AssetDatabase.LoadAssetAtPath<ScriptableObject>(path);
|
|
8170
|
+
|
|
8171
|
+
if (so == null)
|
|
8172
|
+
{
|
|
8173
|
+
return $"Asset not found at {path}";
|
|
8174
|
+
}
|
|
8175
|
+
|
|
8176
|
+
SerializedObject serializedObj = new SerializedObject(so);
|
|
8177
|
+
SerializedProperty prop = serializedObj.GetIterator();
|
|
8178
|
+
|
|
8179
|
+
List<string> properties = new List<string>();
|
|
8180
|
+
while (prop.NextVisible(true))
|
|
8181
|
+
{
|
|
8182
|
+
properties.Add($"{prop.name} ({prop.propertyType})");
|
|
8183
|
+
}
|
|
8184
|
+
return string.Join(", ", properties);
|
|
8185
|
+
\`\`\`
|
|
8186
|
+
`,
|
|
8187
|
+
"examples/selection-operations.md": `# Selection Operations
|
|
8188
|
+
|
|
8189
|
+
Code examples for Selection operations using \`execute-dynamic-code\`.
|
|
8190
|
+
|
|
8191
|
+
## Get Selected GameObjects
|
|
8192
|
+
|
|
8193
|
+
\`\`\`csharp
|
|
8194
|
+
using UnityEditor;
|
|
8195
|
+
using System.Collections.Generic;
|
|
8196
|
+
|
|
8197
|
+
GameObject[] selected = Selection.gameObjects;
|
|
8198
|
+
if (selected.Length == 0)
|
|
8199
|
+
{
|
|
8200
|
+
return "No GameObjects selected";
|
|
8201
|
+
}
|
|
8202
|
+
|
|
8203
|
+
List<string> names = new List<string>();
|
|
8204
|
+
foreach (GameObject obj in selected)
|
|
8205
|
+
{
|
|
8206
|
+
names.Add(obj.name);
|
|
8207
|
+
}
|
|
8208
|
+
return $"Selected: {string.Join(", ", names)}";
|
|
8209
|
+
\`\`\`
|
|
8210
|
+
|
|
8211
|
+
## Get Active (Last Selected) GameObject
|
|
8212
|
+
|
|
8213
|
+
\`\`\`csharp
|
|
8214
|
+
using UnityEditor;
|
|
8215
|
+
|
|
8216
|
+
GameObject active = Selection.activeGameObject;
|
|
8217
|
+
if (active == null)
|
|
8218
|
+
{
|
|
8219
|
+
return "No active GameObject";
|
|
8220
|
+
}
|
|
8221
|
+
return $"Active: {active.name}";
|
|
8222
|
+
\`\`\`
|
|
8223
|
+
|
|
8224
|
+
## Set Selection Programmatically
|
|
8225
|
+
|
|
8226
|
+
\`\`\`csharp
|
|
8227
|
+
using UnityEditor;
|
|
8228
|
+
|
|
8229
|
+
GameObject obj = GameObject.Find("Player");
|
|
8230
|
+
if (obj == null)
|
|
8231
|
+
{
|
|
8232
|
+
return "GameObject 'Player' not found";
|
|
8233
|
+
}
|
|
8234
|
+
|
|
8235
|
+
Selection.activeGameObject = obj;
|
|
8236
|
+
return $"Selected {obj.name}";
|
|
8237
|
+
\`\`\`
|
|
8238
|
+
|
|
8239
|
+
## Select Multiple GameObjects
|
|
8240
|
+
|
|
8241
|
+
\`\`\`csharp
|
|
8242
|
+
using UnityEditor;
|
|
8243
|
+
|
|
8244
|
+
GameObject[] enemies = GameObject.FindGameObjectsWithTag("Enemy");
|
|
8245
|
+
if (enemies.Length == 0)
|
|
8246
|
+
{
|
|
8247
|
+
return "No enemies found";
|
|
8248
|
+
}
|
|
8249
|
+
|
|
8250
|
+
Selection.objects = enemies;
|
|
8251
|
+
return $"Selected {enemies.Length} enemies";
|
|
8252
|
+
\`\`\`
|
|
8253
|
+
|
|
8254
|
+
## Get Top-Level Transforms Only
|
|
8255
|
+
|
|
8256
|
+
\`\`\`csharp
|
|
8257
|
+
using UnityEditor;
|
|
8258
|
+
using System.Collections.Generic;
|
|
8259
|
+
|
|
8260
|
+
Transform[] transforms = Selection.GetTransforms(SelectionMode.TopLevel);
|
|
8261
|
+
if (transforms.Length == 0)
|
|
8262
|
+
{
|
|
8263
|
+
return "No transforms selected";
|
|
8264
|
+
}
|
|
8265
|
+
|
|
8266
|
+
List<string> names = new List<string>();
|
|
8267
|
+
foreach (Transform t in transforms)
|
|
8268
|
+
{
|
|
8269
|
+
names.Add(t.name);
|
|
8270
|
+
}
|
|
8271
|
+
return $"Top-level: {string.Join(", ", names)}";
|
|
8272
|
+
\`\`\`
|
|
8273
|
+
|
|
8274
|
+
## Get Deep Selection (Including Children)
|
|
8275
|
+
|
|
8276
|
+
\`\`\`csharp
|
|
8277
|
+
using UnityEditor;
|
|
8278
|
+
|
|
8279
|
+
Transform[] transforms = Selection.GetTransforms(SelectionMode.Deep);
|
|
8280
|
+
if (transforms.Length == 0)
|
|
8281
|
+
{
|
|
8282
|
+
return "No transforms selected";
|
|
8283
|
+
}
|
|
8284
|
+
|
|
8285
|
+
return $"Deep selection count: {transforms.Length}";
|
|
8286
|
+
\`\`\`
|
|
8287
|
+
|
|
8288
|
+
## Get Editable Objects Only
|
|
8289
|
+
|
|
8290
|
+
\`\`\`csharp
|
|
8291
|
+
using UnityEditor;
|
|
8292
|
+
using System.Collections.Generic;
|
|
8293
|
+
|
|
8294
|
+
Transform[] transforms = Selection.GetTransforms(SelectionMode.Editable);
|
|
8295
|
+
if (transforms.Length == 0)
|
|
8296
|
+
{
|
|
8297
|
+
return "No editable transforms selected";
|
|
8298
|
+
}
|
|
8299
|
+
|
|
8300
|
+
List<string> names = new List<string>();
|
|
8301
|
+
foreach (Transform t in transforms)
|
|
8302
|
+
{
|
|
8303
|
+
names.Add(t.name);
|
|
8304
|
+
}
|
|
8305
|
+
return $"Editable: {string.Join(", ", names)}";
|
|
8306
|
+
\`\`\`
|
|
8307
|
+
|
|
8308
|
+
## Get Selected Assets
|
|
8309
|
+
|
|
8310
|
+
\`\`\`csharp
|
|
8311
|
+
using UnityEditor;
|
|
8312
|
+
using System.Collections.Generic;
|
|
8313
|
+
|
|
8314
|
+
Object[] selectedAssets = Selection.GetFiltered<Object>(SelectionMode.Assets);
|
|
8315
|
+
if (selectedAssets.Length == 0)
|
|
8316
|
+
{
|
|
8317
|
+
return "No assets selected";
|
|
8318
|
+
}
|
|
8319
|
+
|
|
8320
|
+
List<string> paths = new List<string>();
|
|
8321
|
+
foreach (Object asset in selectedAssets)
|
|
8322
|
+
{
|
|
8323
|
+
paths.Add(AssetDatabase.GetAssetPath(asset));
|
|
8324
|
+
}
|
|
8325
|
+
return $"Assets: {string.Join(", ", paths)}";
|
|
8326
|
+
\`\`\`
|
|
8327
|
+
|
|
8328
|
+
## Get Selected Asset GUIDs
|
|
8329
|
+
|
|
8330
|
+
\`\`\`csharp
|
|
8331
|
+
using UnityEditor;
|
|
8332
|
+
using System.Collections.Generic;
|
|
8333
|
+
|
|
8334
|
+
string[] guids = Selection.assetGUIDs;
|
|
8335
|
+
if (guids.Length == 0)
|
|
8336
|
+
{
|
|
8337
|
+
return "No assets selected";
|
|
8338
|
+
}
|
|
8339
|
+
|
|
8340
|
+
List<string> paths = new List<string>();
|
|
8341
|
+
foreach (string guid in guids)
|
|
8342
|
+
{
|
|
8343
|
+
paths.Add(AssetDatabase.GUIDToAssetPath(guid));
|
|
8344
|
+
}
|
|
8345
|
+
return $"Selected assets: {string.Join(", ", paths)}";
|
|
8346
|
+
\`\`\`
|
|
8347
|
+
|
|
8348
|
+
## Select All Children of Selected Object
|
|
8349
|
+
|
|
8350
|
+
\`\`\`csharp
|
|
8351
|
+
using UnityEditor;
|
|
8352
|
+
using UnityEngine;
|
|
8353
|
+
using System.Collections.Generic;
|
|
8354
|
+
|
|
8355
|
+
GameObject parent = Selection.activeGameObject;
|
|
8356
|
+
if (parent == null)
|
|
8357
|
+
{
|
|
8358
|
+
return "No GameObject selected";
|
|
8359
|
+
}
|
|
8360
|
+
|
|
8361
|
+
List<GameObject> children = new List<GameObject>();
|
|
8362
|
+
foreach (Transform child in parent.GetComponentsInChildren<Transform>())
|
|
8363
|
+
{
|
|
8364
|
+
if (child != parent.transform)
|
|
8365
|
+
{
|
|
8366
|
+
children.Add(child.gameObject);
|
|
8367
|
+
}
|
|
8368
|
+
}
|
|
8369
|
+
|
|
8370
|
+
if (children.Count == 0)
|
|
8371
|
+
{
|
|
8372
|
+
return "No children found";
|
|
8373
|
+
}
|
|
8374
|
+
|
|
8375
|
+
Selection.objects = children.ToArray();
|
|
8376
|
+
return $"Selected {children.Count} children";
|
|
8377
|
+
\`\`\`
|
|
8378
|
+
|
|
8379
|
+
## Filter Selection by Component
|
|
8380
|
+
|
|
8381
|
+
\`\`\`csharp
|
|
8382
|
+
using UnityEditor;
|
|
8383
|
+
using System.Collections.Generic;
|
|
8384
|
+
|
|
8385
|
+
GameObject[] selected = Selection.gameObjects;
|
|
8386
|
+
List<GameObject> withRigidbody = new List<GameObject>();
|
|
8387
|
+
|
|
8388
|
+
foreach (GameObject obj in selected)
|
|
8389
|
+
{
|
|
8390
|
+
if (obj.GetComponent<Rigidbody>() != null)
|
|
8391
|
+
{
|
|
8392
|
+
withRigidbody.Add(obj);
|
|
8393
|
+
}
|
|
8394
|
+
}
|
|
8395
|
+
|
|
8396
|
+
if (withRigidbody.Count == 0)
|
|
8397
|
+
{
|
|
8398
|
+
return "No objects with Rigidbody in selection";
|
|
8399
|
+
}
|
|
8400
|
+
|
|
8401
|
+
Selection.objects = withRigidbody.ToArray();
|
|
8402
|
+
return $"Filtered to {withRigidbody.Count} objects with Rigidbody";
|
|
8403
|
+
\`\`\`
|
|
8404
|
+
|
|
8405
|
+
## Check if Object is Selected
|
|
8406
|
+
|
|
8407
|
+
\`\`\`csharp
|
|
8408
|
+
using UnityEditor;
|
|
8409
|
+
|
|
8410
|
+
GameObject player = GameObject.Find("Player");
|
|
8411
|
+
if (player == null)
|
|
8412
|
+
{
|
|
8413
|
+
return "Player not found";
|
|
8414
|
+
}
|
|
8415
|
+
|
|
8416
|
+
bool isSelected = Selection.Contains(player);
|
|
8417
|
+
return $"Player is {(isSelected ? "" : "not ")}selected";
|
|
8418
|
+
\`\`\`
|
|
8419
|
+
|
|
8420
|
+
## Clear Selection
|
|
8421
|
+
|
|
8422
|
+
\`\`\`csharp
|
|
8423
|
+
using UnityEditor;
|
|
8424
|
+
|
|
8425
|
+
Selection.activeObject = null;
|
|
8426
|
+
return "Selection cleared";
|
|
8427
|
+
\`\`\`
|
|
8428
|
+
|
|
8429
|
+
## Select Objects by Layer
|
|
8430
|
+
|
|
8431
|
+
\`\`\`csharp
|
|
8432
|
+
using UnityEditor;
|
|
8433
|
+
using System.Collections.Generic;
|
|
8434
|
+
|
|
8435
|
+
int layer = LayerMask.NameToLayer("UI");
|
|
8436
|
+
GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
|
|
8437
|
+
List<GameObject> layerObjects = new List<GameObject>();
|
|
8438
|
+
|
|
8439
|
+
foreach (GameObject obj in allObjects)
|
|
8440
|
+
{
|
|
8441
|
+
if (obj.layer == layer)
|
|
8442
|
+
{
|
|
8443
|
+
layerObjects.Add(obj);
|
|
8444
|
+
}
|
|
8445
|
+
}
|
|
8446
|
+
|
|
8447
|
+
if (layerObjects.Count == 0)
|
|
8448
|
+
{
|
|
8449
|
+
return "No objects found on UI layer";
|
|
8450
|
+
}
|
|
8451
|
+
|
|
8452
|
+
Selection.objects = layerObjects.ToArray();
|
|
8453
|
+
return $"Selected {layerObjects.Count} objects on UI layer";
|
|
8454
|
+
\`\`\`
|
|
8455
|
+
|
|
8456
|
+
## Ping Object in Hierarchy/Project
|
|
8457
|
+
|
|
8458
|
+
\`\`\`csharp
|
|
8459
|
+
using UnityEditor;
|
|
8460
|
+
|
|
8461
|
+
GameObject obj = GameObject.Find("Player");
|
|
8462
|
+
if (obj == null)
|
|
8463
|
+
{
|
|
8464
|
+
return "Player not found";
|
|
8465
|
+
}
|
|
8466
|
+
|
|
8467
|
+
EditorGUIUtility.PingObject(obj);
|
|
8468
|
+
return $"Pinged {obj.name} in Hierarchy";
|
|
8469
|
+
\`\`\`
|
|
8470
|
+
|
|
8471
|
+
## Focus on Selected Object in Scene View
|
|
8472
|
+
|
|
8473
|
+
\`\`\`csharp
|
|
8474
|
+
using UnityEditor;
|
|
8475
|
+
|
|
8476
|
+
if (Selection.activeGameObject == null)
|
|
8477
|
+
{
|
|
8478
|
+
return "No GameObject selected";
|
|
8479
|
+
}
|
|
8480
|
+
|
|
8481
|
+
SceneView.FrameLastActiveSceneView();
|
|
8482
|
+
return "Focused on selected object";
|
|
8483
|
+
\`\`\`
|
|
8484
|
+
|
|
8485
|
+
`,
|
|
8486
|
+
"examples/undo-operations.md": `# Undo Operations
|
|
8487
|
+
|
|
8488
|
+
Code examples for Undo-supported operations using \`execute-dynamic-code\`.
|
|
8489
|
+
|
|
8490
|
+
## Record Property Change (Undo.RecordObject)
|
|
8491
|
+
|
|
8492
|
+
\`\`\`csharp
|
|
8493
|
+
using UnityEditor;
|
|
8494
|
+
|
|
8495
|
+
GameObject selected = Selection.activeGameObject;
|
|
8496
|
+
if (selected == null)
|
|
8497
|
+
{
|
|
8498
|
+
return "No GameObject selected";
|
|
8499
|
+
}
|
|
8500
|
+
|
|
8501
|
+
Undo.RecordObject(selected.transform, "Move Object");
|
|
8502
|
+
selected.transform.position = new Vector3(0, 5, 0);
|
|
8503
|
+
return $"Moved {selected.name} (Undo available)";
|
|
8504
|
+
\`\`\`
|
|
8505
|
+
|
|
8506
|
+
## Record Multiple Objects
|
|
8507
|
+
|
|
8508
|
+
\`\`\`csharp
|
|
8509
|
+
using UnityEditor;
|
|
8510
|
+
|
|
8511
|
+
GameObject[] selectedObjects = Selection.gameObjects;
|
|
8512
|
+
if (selectedObjects.Length == 0)
|
|
8513
|
+
{
|
|
8514
|
+
return "No GameObjects selected";
|
|
8515
|
+
}
|
|
8516
|
+
|
|
8517
|
+
Object[] transforms = new Object[selectedObjects.Length];
|
|
8518
|
+
for (int i = 0; i < selectedObjects.Length; i++)
|
|
8519
|
+
{
|
|
8520
|
+
transforms[i] = selectedObjects[i].transform;
|
|
8521
|
+
}
|
|
8522
|
+
|
|
8523
|
+
Undo.RecordObjects(transforms, "Move Multiple Objects");
|
|
8524
|
+
foreach (GameObject obj in selectedObjects)
|
|
8525
|
+
{
|
|
8526
|
+
obj.transform.position += Vector3.up * 2;
|
|
8527
|
+
}
|
|
8528
|
+
return $"Moved {selectedObjects.Length} objects (Undo available)";
|
|
8529
|
+
\`\`\`
|
|
8530
|
+
|
|
8531
|
+
## Complete Object Undo (For Complex Changes)
|
|
8532
|
+
|
|
8533
|
+
\`\`\`csharp
|
|
8534
|
+
using UnityEditor;
|
|
8535
|
+
|
|
8536
|
+
GameObject selected = Selection.activeGameObject;
|
|
8537
|
+
if (selected == null)
|
|
8538
|
+
{
|
|
8539
|
+
return "No GameObject selected";
|
|
8540
|
+
}
|
|
8541
|
+
|
|
8542
|
+
Undo.RegisterCompleteObjectUndo(selected, "Complete Object Change");
|
|
8543
|
+
selected.name = "RenamedObject";
|
|
8544
|
+
selected.layer = LayerMask.NameToLayer("Default");
|
|
8545
|
+
selected.tag = "Untagged";
|
|
8546
|
+
return $"Modified object completely (Undo available)";
|
|
8547
|
+
\`\`\`
|
|
8548
|
+
|
|
8549
|
+
## Add Component with Undo
|
|
8550
|
+
|
|
8551
|
+
\`\`\`csharp
|
|
8552
|
+
using UnityEditor;
|
|
8553
|
+
|
|
8554
|
+
GameObject selected = Selection.activeGameObject;
|
|
8555
|
+
if (selected == null)
|
|
8556
|
+
{
|
|
8557
|
+
return "No GameObject selected";
|
|
8558
|
+
}
|
|
8559
|
+
|
|
8560
|
+
Rigidbody rb = Undo.AddComponent<Rigidbody>(selected);
|
|
8561
|
+
rb.mass = 2f;
|
|
8562
|
+
rb.useGravity = true;
|
|
8563
|
+
return $"Added Rigidbody to {selected.name} (Undo available)";
|
|
8564
|
+
\`\`\`
|
|
8565
|
+
|
|
8566
|
+
## Set Parent with Undo
|
|
8567
|
+
|
|
8568
|
+
\`\`\`csharp
|
|
8569
|
+
using UnityEditor;
|
|
8570
|
+
|
|
8571
|
+
GameObject child = GameObject.Find("Child");
|
|
8572
|
+
GameObject parent = GameObject.Find("Parent");
|
|
8573
|
+
|
|
8574
|
+
if (child == null || parent == null)
|
|
8575
|
+
{
|
|
8576
|
+
return "Child or Parent not found";
|
|
8577
|
+
}
|
|
8578
|
+
|
|
8579
|
+
Undo.SetTransformParent(child.transform, parent.transform, "Set Parent");
|
|
8580
|
+
return $"Set {child.name}'s parent to {parent.name} (Undo available)";
|
|
8581
|
+
\`\`\`
|
|
8582
|
+
|
|
8583
|
+
## Create GameObject with Undo
|
|
8584
|
+
|
|
8585
|
+
\`\`\`csharp
|
|
8586
|
+
using UnityEditor;
|
|
8587
|
+
|
|
8588
|
+
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
|
8589
|
+
cube.name = "UndoableCube";
|
|
8590
|
+
cube.transform.position = new Vector3(0, 1, 0);
|
|
8591
|
+
Undo.RegisterCreatedObjectUndo(cube, "Create Cube");
|
|
8592
|
+
return $"Created {cube.name} (Undo available)";
|
|
8593
|
+
\`\`\`
|
|
8594
|
+
|
|
8595
|
+
## Destroy GameObject with Undo
|
|
8596
|
+
|
|
8597
|
+
\`\`\`csharp
|
|
8598
|
+
using UnityEditor;
|
|
8599
|
+
|
|
8600
|
+
GameObject obj = GameObject.Find("ObjectToDelete");
|
|
8601
|
+
if (obj == null)
|
|
8602
|
+
{
|
|
8603
|
+
return "GameObject not found";
|
|
8604
|
+
}
|
|
8605
|
+
|
|
8606
|
+
Undo.DestroyObjectImmediate(obj);
|
|
8607
|
+
return "GameObject destroyed (Undo available)";
|
|
8608
|
+
\`\`\`
|
|
8609
|
+
|
|
8610
|
+
## Named Undo Group
|
|
8611
|
+
|
|
8612
|
+
\`\`\`csharp
|
|
8613
|
+
using UnityEditor;
|
|
8614
|
+
|
|
8615
|
+
GameObject selected = Selection.activeGameObject;
|
|
8616
|
+
if (selected == null)
|
|
8617
|
+
{
|
|
8618
|
+
return "No GameObject selected";
|
|
8619
|
+
}
|
|
8620
|
+
|
|
8621
|
+
Undo.SetCurrentGroupName("Complex Transform Operation");
|
|
8622
|
+
|
|
8623
|
+
Undo.RecordObject(selected.transform, "");
|
|
8624
|
+
selected.transform.position = Vector3.zero;
|
|
8625
|
+
selected.transform.rotation = Quaternion.identity;
|
|
8626
|
+
selected.transform.localScale = Vector3.one;
|
|
8627
|
+
|
|
8628
|
+
return "Reset transform (Single undo step)";
|
|
8629
|
+
\`\`\`
|
|
8630
|
+
|
|
8631
|
+
## Collapse Multiple Operations into One Undo
|
|
8632
|
+
|
|
8633
|
+
\`\`\`csharp
|
|
8634
|
+
using UnityEditor;
|
|
8635
|
+
|
|
8636
|
+
int undoGroup = Undo.GetCurrentGroup();
|
|
8637
|
+
Undo.SetCurrentGroupName("Batch Operation");
|
|
8638
|
+
|
|
8639
|
+
GameObject cube1 = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
|
8640
|
+
cube1.name = "Cube1";
|
|
8641
|
+
Undo.RegisterCreatedObjectUndo(cube1, "");
|
|
8642
|
+
|
|
8643
|
+
GameObject cube2 = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
|
8644
|
+
cube2.name = "Cube2";
|
|
8645
|
+
cube2.transform.position = Vector3.right * 2;
|
|
8646
|
+
Undo.RegisterCreatedObjectUndo(cube2, "");
|
|
8647
|
+
|
|
8648
|
+
GameObject cube3 = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
|
8649
|
+
cube3.name = "Cube3";
|
|
8650
|
+
cube3.transform.position = Vector3.right * 4;
|
|
8651
|
+
Undo.RegisterCreatedObjectUndo(cube3, "");
|
|
8652
|
+
|
|
8653
|
+
Undo.CollapseUndoOperations(undoGroup);
|
|
8654
|
+
return "Created 3 cubes (Single undo step)";
|
|
8655
|
+
\`\`\`
|
|
8656
|
+
|
|
8657
|
+
## Modify ScriptableObject with Undo
|
|
8658
|
+
|
|
8659
|
+
\`\`\`csharp
|
|
8660
|
+
using UnityEditor;
|
|
8661
|
+
|
|
8662
|
+
string path = "Assets/Data/GameSettings.asset";
|
|
8663
|
+
ScriptableObject so = AssetDatabase.LoadAssetAtPath<ScriptableObject>(path);
|
|
8664
|
+
if (so == null)
|
|
8665
|
+
{
|
|
8666
|
+
return $"Asset not found at {path}";
|
|
8667
|
+
}
|
|
8668
|
+
|
|
8669
|
+
Undo.RecordObject(so, "Modify Settings");
|
|
8670
|
+
SerializedObject serializedObj = new SerializedObject(so);
|
|
8671
|
+
SerializedProperty prop = serializedObj.FindProperty("maxHealth");
|
|
8672
|
+
if (prop != null)
|
|
8673
|
+
{
|
|
8674
|
+
prop.intValue = 200;
|
|
8675
|
+
serializedObj.ApplyModifiedProperties();
|
|
8676
|
+
}
|
|
8677
|
+
return "Modified ScriptableObject (Undo available)";
|
|
8678
|
+
\`\`\`
|
|
8679
|
+
|
|
8680
|
+
## Modify Material with Undo
|
|
8681
|
+
|
|
8682
|
+
\`\`\`csharp
|
|
8683
|
+
using UnityEditor;
|
|
8684
|
+
|
|
8685
|
+
string path = "Assets/Materials/MyMaterial.mat";
|
|
8686
|
+
Material mat = AssetDatabase.LoadAssetAtPath<Material>(path);
|
|
8687
|
+
if (mat == null)
|
|
8688
|
+
{
|
|
8689
|
+
return $"Material not found at {path}";
|
|
8690
|
+
}
|
|
8691
|
+
|
|
8692
|
+
Undo.RecordObject(mat, "Change Material Color");
|
|
8693
|
+
mat.color = Color.red;
|
|
8694
|
+
return "Changed material color (Undo available)";
|
|
8695
|
+
\`\`\`
|
|
8696
|
+
|
|
8697
|
+
`
|
|
8698
|
+
}
|
|
8699
|
+
},
|
|
8700
|
+
{
|
|
8701
|
+
name: "uloop-execute-menu-item",
|
|
8702
|
+
dirName: "uloop-execute-menu-item",
|
|
8703
|
+
content: SKILL_default6
|
|
8704
|
+
},
|
|
8705
|
+
{
|
|
8706
|
+
name: "uloop-find-game-objects",
|
|
8707
|
+
dirName: "uloop-find-game-objects",
|
|
8708
|
+
content: SKILL_default7
|
|
8709
|
+
},
|
|
8710
|
+
{
|
|
8711
|
+
name: "uloop-focus-window",
|
|
8712
|
+
dirName: "uloop-focus-window",
|
|
8713
|
+
content: SKILL_default8
|
|
8714
|
+
},
|
|
8715
|
+
{
|
|
8716
|
+
name: "uloop-get-hierarchy",
|
|
8717
|
+
dirName: "uloop-get-hierarchy",
|
|
8718
|
+
content: SKILL_default9
|
|
8719
|
+
},
|
|
8720
|
+
{
|
|
8721
|
+
name: "uloop-get-logs",
|
|
8722
|
+
dirName: "uloop-get-logs",
|
|
8723
|
+
content: SKILL_default10
|
|
8724
|
+
},
|
|
8725
|
+
{
|
|
8726
|
+
name: "uloop-get-menu-items",
|
|
8727
|
+
dirName: "uloop-get-menu-items",
|
|
8728
|
+
content: SKILL_default11
|
|
8729
|
+
},
|
|
8730
|
+
{
|
|
8731
|
+
name: "uloop-get-provider-details",
|
|
8732
|
+
dirName: "uloop-get-provider-details",
|
|
8733
|
+
content: SKILL_default12
|
|
8734
|
+
},
|
|
8735
|
+
{
|
|
8736
|
+
name: "uloop-run-tests",
|
|
8737
|
+
dirName: "uloop-run-tests",
|
|
8738
|
+
content: SKILL_default13
|
|
8739
|
+
},
|
|
8740
|
+
{
|
|
8741
|
+
name: "uloop-unity-search",
|
|
8742
|
+
dirName: "uloop-unity-search",
|
|
8743
|
+
content: SKILL_default14
|
|
8744
|
+
}
|
|
8745
|
+
];
|
|
8746
|
+
|
|
8747
|
+
// src/skills/skills-manager.ts
|
|
8748
|
+
var EXCLUDED_DIRS2 = /* @__PURE__ */ new Set(["node_modules", ".git", "Temp", "obj", "Build", "Builds", "Logs"]);
|
|
8749
|
+
function getGlobalSkillsDir(target) {
|
|
8750
|
+
return (0, import_path5.join)((0, import_os.homedir)(), target.projectDir, "skills");
|
|
8751
|
+
}
|
|
8752
|
+
function getProjectSkillsDir(target) {
|
|
8753
|
+
const projectRoot = findUnityProjectRoot();
|
|
8754
|
+
if (!projectRoot) {
|
|
8755
|
+
throw new Error(
|
|
8756
|
+
"Not inside a Unity project. Run this command from within a Unity project directory."
|
|
8757
|
+
);
|
|
8758
|
+
}
|
|
8759
|
+
return (0, import_path5.join)(projectRoot, target.projectDir, "skills");
|
|
8760
|
+
}
|
|
8761
|
+
function getSkillPath(skillDirName, target, global) {
|
|
8762
|
+
const baseDir = global ? getGlobalSkillsDir(target) : getProjectSkillsDir(target);
|
|
8763
|
+
return (0, import_path5.join)(baseDir, skillDirName, target.skillFileName);
|
|
8764
|
+
}
|
|
8765
|
+
function isSkillInstalled(skill, target, global) {
|
|
8766
|
+
const skillPath = getSkillPath(skill.dirName, target, global);
|
|
8767
|
+
return (0, import_fs5.existsSync)(skillPath);
|
|
8768
|
+
}
|
|
8769
|
+
function isSkillOutdated(skill, target, global) {
|
|
8770
|
+
const baseDir = global ? getGlobalSkillsDir(target) : getProjectSkillsDir(target);
|
|
8771
|
+
const skillDir = (0, import_path5.join)(baseDir, skill.dirName);
|
|
8772
|
+
const skillPath = (0, import_path5.join)(skillDir, target.skillFileName);
|
|
8773
|
+
if (!(0, import_fs5.existsSync)(skillPath)) {
|
|
8774
|
+
return false;
|
|
8775
|
+
}
|
|
8776
|
+
const installedContent = (0, import_fs5.readFileSync)(skillPath, "utf-8");
|
|
8777
|
+
if (installedContent !== skill.content) {
|
|
8778
|
+
return true;
|
|
8779
|
+
}
|
|
8780
|
+
if ("additionalFiles" in skill && skill.additionalFiles) {
|
|
8781
|
+
const additionalFiles = skill.additionalFiles;
|
|
8782
|
+
for (const [relativePath, expectedContent] of Object.entries(additionalFiles)) {
|
|
8783
|
+
const filePath = (0, import_path5.join)(skillDir, relativePath);
|
|
8784
|
+
if (!(0, import_fs5.existsSync)(filePath)) {
|
|
8785
|
+
return true;
|
|
8786
|
+
}
|
|
8787
|
+
const installedFileContent = (0, import_fs5.readFileSync)(filePath, "utf-8");
|
|
8788
|
+
if (installedFileContent !== expectedContent) {
|
|
8789
|
+
return true;
|
|
8790
|
+
}
|
|
8791
|
+
}
|
|
8792
|
+
}
|
|
8793
|
+
return false;
|
|
8794
|
+
}
|
|
8795
|
+
function getSkillStatus(skill, target, global) {
|
|
8796
|
+
if (!isSkillInstalled(skill, target, global)) {
|
|
8797
|
+
return "not_installed";
|
|
8798
|
+
}
|
|
8799
|
+
if (isSkillOutdated(skill, target, global)) {
|
|
8800
|
+
return "outdated";
|
|
8801
|
+
}
|
|
8802
|
+
return "installed";
|
|
8803
|
+
}
|
|
8804
|
+
function parseFrontmatter(content) {
|
|
8805
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
8806
|
+
if (!frontmatterMatch) {
|
|
8807
|
+
return {};
|
|
8808
|
+
}
|
|
8809
|
+
const frontmatter = {};
|
|
8810
|
+
const lines = frontmatterMatch[1].split("\n");
|
|
8811
|
+
for (const line of lines) {
|
|
8812
|
+
const colonIndex = line.indexOf(":");
|
|
8813
|
+
if (colonIndex === -1) {
|
|
8814
|
+
continue;
|
|
8815
|
+
}
|
|
8816
|
+
const key = line.slice(0, colonIndex).trim();
|
|
8817
|
+
const value = line.slice(colonIndex + 1).trim();
|
|
8818
|
+
if (value === "true") {
|
|
8819
|
+
frontmatter[key] = true;
|
|
8820
|
+
} else if (value === "false") {
|
|
8821
|
+
frontmatter[key] = false;
|
|
8822
|
+
} else {
|
|
8823
|
+
frontmatter[key] = value;
|
|
8824
|
+
}
|
|
8825
|
+
}
|
|
8826
|
+
return frontmatter;
|
|
8827
|
+
}
|
|
8828
|
+
function scanEditorFolderForSkills(editorPath, skills) {
|
|
8829
|
+
if (!(0, import_fs5.existsSync)(editorPath)) {
|
|
8830
|
+
return;
|
|
8831
|
+
}
|
|
8832
|
+
const entries = (0, import_fs5.readdirSync)(editorPath, { withFileTypes: true });
|
|
8833
|
+
for (const entry of entries) {
|
|
8834
|
+
if (EXCLUDED_DIRS2.has(entry.name)) {
|
|
8835
|
+
continue;
|
|
8836
|
+
}
|
|
8837
|
+
const fullPath = (0, import_path5.join)(editorPath, entry.name);
|
|
8838
|
+
if (entry.isDirectory()) {
|
|
8839
|
+
const skillMdPath = (0, import_path5.join)(fullPath, "SKILL.md");
|
|
8840
|
+
if ((0, import_fs5.existsSync)(skillMdPath)) {
|
|
8841
|
+
const content = (0, import_fs5.readFileSync)(skillMdPath, "utf-8");
|
|
8842
|
+
const frontmatter = parseFrontmatter(content);
|
|
8843
|
+
if (frontmatter.internal === true) {
|
|
8844
|
+
continue;
|
|
8845
|
+
}
|
|
8846
|
+
const name = typeof frontmatter.name === "string" ? frontmatter.name : entry.name;
|
|
8847
|
+
skills.push({
|
|
8848
|
+
name,
|
|
8849
|
+
dirName: name,
|
|
8850
|
+
content,
|
|
8851
|
+
sourcePath: skillMdPath
|
|
8852
|
+
});
|
|
8853
|
+
}
|
|
8854
|
+
scanEditorFolderForSkills(fullPath, skills);
|
|
8855
|
+
}
|
|
8856
|
+
}
|
|
8857
|
+
}
|
|
8858
|
+
function findEditorFolders(basePath, maxDepth = 2) {
|
|
8859
|
+
const editorFolders = [];
|
|
8860
|
+
function scan(currentPath, depth) {
|
|
8861
|
+
if (depth > maxDepth || !(0, import_fs5.existsSync)(currentPath)) {
|
|
8862
|
+
return;
|
|
8863
|
+
}
|
|
8864
|
+
const entries = (0, import_fs5.readdirSync)(currentPath, { withFileTypes: true });
|
|
8865
|
+
for (const entry of entries) {
|
|
8866
|
+
if (!entry.isDirectory() || EXCLUDED_DIRS2.has(entry.name)) {
|
|
8867
|
+
continue;
|
|
8868
|
+
}
|
|
8869
|
+
const fullPath = (0, import_path5.join)(currentPath, entry.name);
|
|
8870
|
+
if (entry.name === "Editor") {
|
|
8871
|
+
editorFolders.push(fullPath);
|
|
8872
|
+
} else {
|
|
8873
|
+
scan(fullPath, depth + 1);
|
|
8874
|
+
}
|
|
8875
|
+
}
|
|
8876
|
+
}
|
|
8877
|
+
scan(basePath, 0);
|
|
8878
|
+
return editorFolders;
|
|
8879
|
+
}
|
|
8880
|
+
function collectProjectSkills() {
|
|
8881
|
+
const projectRoot = findUnityProjectRoot();
|
|
8882
|
+
if (!projectRoot) {
|
|
8883
|
+
return [];
|
|
8884
|
+
}
|
|
8885
|
+
const skills = [];
|
|
8886
|
+
const seenNames = /* @__PURE__ */ new Set();
|
|
8887
|
+
const searchPaths = [
|
|
8888
|
+
(0, import_path5.join)(projectRoot, "Assets"),
|
|
8889
|
+
(0, import_path5.join)(projectRoot, "Packages"),
|
|
6578
8890
|
(0, import_path5.join)(projectRoot, "Library", "PackageCache")
|
|
6579
8891
|
];
|
|
6580
8892
|
for (const searchPath of searchPaths) {
|
|
6581
|
-
if (!(0,
|
|
8893
|
+
if (!(0, import_fs5.existsSync)(searchPath)) {
|
|
6582
8894
|
continue;
|
|
6583
8895
|
}
|
|
6584
8896
|
const editorFolders = findEditorFolders(searchPath, 3);
|
|
@@ -6616,16 +8928,24 @@ function installSkill(skill, target, global) {
|
|
|
6616
8928
|
const baseDir = global ? getGlobalSkillsDir(target) : getProjectSkillsDir(target);
|
|
6617
8929
|
const skillDir = (0, import_path5.join)(baseDir, skill.dirName);
|
|
6618
8930
|
const skillPath = (0, import_path5.join)(skillDir, target.skillFileName);
|
|
6619
|
-
(0,
|
|
6620
|
-
(0,
|
|
8931
|
+
(0, import_fs5.mkdirSync)(skillDir, { recursive: true });
|
|
8932
|
+
(0, import_fs5.writeFileSync)(skillPath, skill.content, "utf-8");
|
|
8933
|
+
if ("additionalFiles" in skill && skill.additionalFiles) {
|
|
8934
|
+
const additionalFiles = skill.additionalFiles;
|
|
8935
|
+
for (const [relativePath, content] of Object.entries(additionalFiles)) {
|
|
8936
|
+
const fullPath = (0, import_path5.join)(skillDir, relativePath);
|
|
8937
|
+
(0, import_fs5.mkdirSync)((0, import_path5.dirname)(fullPath), { recursive: true });
|
|
8938
|
+
(0, import_fs5.writeFileSync)(fullPath, content, "utf-8");
|
|
8939
|
+
}
|
|
8940
|
+
}
|
|
6621
8941
|
}
|
|
6622
8942
|
function uninstallSkill(skill, target, global) {
|
|
6623
8943
|
const baseDir = global ? getGlobalSkillsDir(target) : getProjectSkillsDir(target);
|
|
6624
8944
|
const skillDir = (0, import_path5.join)(baseDir, skill.dirName);
|
|
6625
|
-
if (!(0,
|
|
8945
|
+
if (!(0, import_fs5.existsSync)(skillDir)) {
|
|
6626
8946
|
return false;
|
|
6627
8947
|
}
|
|
6628
|
-
(0,
|
|
8948
|
+
(0, import_fs5.rmSync)(skillDir, { recursive: true, force: true });
|
|
6629
8949
|
return true;
|
|
6630
8950
|
}
|
|
6631
8951
|
function installAllSkills(target, global) {
|
|
@@ -7153,8 +9473,8 @@ function cleanupLockFiles() {
|
|
|
7153
9473
|
let cleaned = 0;
|
|
7154
9474
|
for (const lockFile of LOCK_FILES) {
|
|
7155
9475
|
const lockPath = (0, import_path6.join)(tempDir, lockFile);
|
|
7156
|
-
if ((0,
|
|
7157
|
-
(0,
|
|
9476
|
+
if ((0, import_fs6.existsSync)(lockPath)) {
|
|
9477
|
+
(0, import_fs6.unlinkSync)(lockPath);
|
|
7158
9478
|
console.log(`Removed: ${lockFile}`);
|
|
7159
9479
|
cleaned++;
|
|
7160
9480
|
}
|
|
@@ -7190,12 +9510,12 @@ function handleCompletion(install, shellOverride) {
|
|
|
7190
9510
|
}
|
|
7191
9511
|
const configPath = getShellConfigPath(shell);
|
|
7192
9512
|
const configDir = (0, import_path6.dirname)(configPath);
|
|
7193
|
-
if (!(0,
|
|
7194
|
-
(0,
|
|
9513
|
+
if (!(0, import_fs6.existsSync)(configDir)) {
|
|
9514
|
+
(0, import_fs6.mkdirSync)(configDir, { recursive: true });
|
|
7195
9515
|
}
|
|
7196
9516
|
let content = "";
|
|
7197
|
-
if ((0,
|
|
7198
|
-
content = (0,
|
|
9517
|
+
if ((0, import_fs6.existsSync)(configPath)) {
|
|
9518
|
+
content = (0, import_fs6.readFileSync)(configPath, "utf-8");
|
|
7199
9519
|
content = content.replace(
|
|
7200
9520
|
/\n?# >>> uloop completion >>>[\s\S]*?# <<< uloop completion <<<\n?/g,
|
|
7201
9521
|
""
|
|
@@ -7209,7 +9529,7 @@ ${startMarker}
|
|
|
7209
9529
|
${script}
|
|
7210
9530
|
${endMarker}
|
|
7211
9531
|
`;
|
|
7212
|
-
(0,
|
|
9532
|
+
(0, import_fs6.writeFileSync)(configPath, content + lineToAdd, "utf-8");
|
|
7213
9533
|
} else {
|
|
7214
9534
|
const evalLine = `eval "$(uloop completion --shell ${shell})"`;
|
|
7215
9535
|
const lineToAdd = `
|
|
@@ -7217,7 +9537,7 @@ ${startMarker}
|
|
|
7217
9537
|
${evalLine}
|
|
7218
9538
|
${endMarker}
|
|
7219
9539
|
`;
|
|
7220
|
-
(0,
|
|
9540
|
+
(0, import_fs6.writeFileSync)(configPath, content + lineToAdd, "utf-8");
|
|
7221
9541
|
}
|
|
7222
9542
|
console.log(`Completion installed to ${configPath}`);
|
|
7223
9543
|
if (shell === "powershell") {
|