chore(unity): 固定Unity CLI Loop本地包并移除UnitySkills依赖

This commit is contained in:
Liubing\LB
2026-09-28 18:14:59 +08:00
parent 0eb43b2b40
commit c4ae02def9
952 changed files with 70137 additions and 11 deletions

View File

@@ -0,0 +1,31 @@
# execute-dynamic-code design notes
This directory owns the user-facing `execute-dynamic-code` tool contract.
The public contract must stay stable even when the internal compiler strategy changes.
## Fast path intent
- The preferred compilation path is Unity-bundled Roslyn.
- `SharedRoslynCompilerWorkerHost` keeps a warm compiler process so structural cache misses stay fast.
- The shared worker is an optimization only. It must not own response shaping, security validation, or tool-specific policy.
## Prewarm intent
- Auto prewarm exists to hide first-use compiler startup cost after server startup or recovery.
- Prewarm must go through the same runtime facade used by normal requests.
- Prewarm must be single-flight. It should never create a parallel execution lane or bypass runtime idleness checks.
- Prewarm is allowed to improve latency only. It must not change observable tool behavior.
## Fallback intent
- Preferred order is: shared Roslyn worker -> one-shot Roslyn -> AssemblyBuilder fallback.
- Fallback changes performance, not semantics.
- Any fallback from the ideal fast path is a maintainer-visible error condition and must be logged through `DynamicCompilationHealthMonitor`.
## Invariants
- Security validation always runs in Unity after compilation and before execution.
- In restricted mode, metadata validation and IL validation must both complete before `Assembly.Load`.
- Tool entry points and use cases depend on runtime-facing facades, not compiler backend details.
- Worker state is disposable. Domain reload or worker protocol failure must be handled by rebuilding state, not by preserving it.
- The compile backend may change, but `ExecuteDynamicCodeResponse` shape and user-facing behavior must remain stable.

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 3a32019de73d24a92b23228d40898acf
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,98 @@
using System.Collections.Generic;
namespace io.github.hatayama.uLoopMCP
{
/// <summary>
/// Response for dynamic code execution tool
/// Related classes: ExecuteDynamicCodeTool, ExecuteDynamicCodeSchema
/// </summary>
public class ExecuteDynamicCodeResponse : BaseToolResponse
{
private const bool EmitTimingsInJsonResponses = false;
/// <summary>Execution success flag</summary>
public bool Success { get; set; }
/// <summary>Execution result</summary>
public string Result { get; set; }
/// <summary>Log messages</summary>
public List<string> Logs { get; set; } = new();
/// <summary>Compilation errors</summary>
public List<CompilationErrorDto> CompilationErrors { get; set; } = new();
/// <summary>Error message (on failure)</summary>
public string ErrorMessage { get; set; }
/// <summary>Current security level</summary>
public string SecurityLevel { get; set; }
/// <summary>Error message (alias for ErrorMessage)</summary>
public string Error
{
get => ErrorMessage;
set => ErrorMessage = value;
}
/// <summary>
/// Code formatted for compilation
/// (After extracting/moving using statements and applying class/method wrapping)
/// Allows checking the actual compiled code during debugging
/// </summary>
public string UpdatedCode { get; set; }
/// <summary>
/// Summary of diagnostics (unique count, total count, first error brief)
/// </summary>
public string DiagnosticsSummary { get; set; }
/// <summary>
/// Structured diagnostics for rich clients (line/column/code/message/hint/suggestions)
/// </summary>
public List<CompilationErrorDto> Diagnostics { get; set; } = new();
/// <summary>
/// Lightweight internal timings for benchmark comparison.
/// </summary>
public List<string> Timings { get; set; } = new();
// Keep timings available in memory for diagnostics and readiness decisions
// while avoiding noisy default payloads for normal execute-dynamic-code users.
public bool ShouldSerializeTimings()
{
return EmitTimingsInJsonResponses;
}
}
/// <summary>
/// DTO for compilation error information
/// </summary>
public class CompilationErrorDto
{
/// <summary>Error message</summary>
public string Message { get; set; }
/// <summary>Line number</summary>
public int Line { get; set; }
/// <summary>Column number</summary>
public int Column { get; set; }
/// <summary>Compiler error code (e.g., CS0103)</summary>
public string ErrorCode { get; set; }
/// <summary>Optional hint for resolving the error</summary>
public string Hint { get; set; }
/// <summary>Suggested fixes (e.g., add using or qualify)</summary>
public List<string> Suggestions { get; set; } = new();
/// <summary>Context lines around the error with a caret pointer</summary>
public string Context { get; set; }
/// <summary>Pointer column for caret rendering (1-based)</summary>
public int PointerColumn { get; set; }
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 88dbc381725914ce488e21b9b27a2cb8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,29 @@
using System.Collections.Generic;
namespace io.github.hatayama.uLoopMCP
{
internal static class ExecuteDynamicCodeResponseTimingAugmenter
{
public static void AppendTimingEntries(
ExecuteDynamicCodeResponse response,
double mainThreadWaitMilliseconds,
double toolTotalMilliseconds,
double requestTotalMilliseconds)
{
if (response == null)
{
return;
}
if (response.Timings == null)
{
response.Timings = new List<string>();
}
response.Timings.Add($"[Perf] MainThreadWait: {mainThreadWaitMilliseconds:F1}ms");
response.Timings.Add($"[Perf] ToolTotal: {toolTotalMilliseconds:F1}ms");
response.Timings.Add($"[Perf] RequestTotal: {requestTotalMilliseconds:F1}ms");
response.Timings.AddRange(DynamicCodeStartupTelemetry.CreateTimingEntries());
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8683636c860b448cf93c98208c36946b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,65 @@
using System.Collections.Generic;
using System.ComponentModel;
namespace io.github.hatayama.uLoopMCP
{
/// <summary>
/// Parameter schema for dynamic code execution tool
/// Related classes: ExecuteDynamicCodeTool, ExecuteDynamicCodeResponse
/// </summary>
public class ExecuteDynamicCodeSchema : BaseToolSchema
{
/// <summary>C# code to execute</summary>
[Description(@"Editor automation only — no file I/O, no script authoring.
Direct statements only (no classes/namespaces/methods); return is optional (auto 'return null;' if omitted).
You may include using directives at the top; they are hoisted above the wrapper.
Example:
using UnityEngine;
var x = Mathf.PI;
return x;
Do:
- Prefab/material wiring (PrefabUtility)
- AddComponent + reference wiring (SerializedObject)
- Scene/hierarchy edits
Don’t:
- System.IO.* (File/Directory/Path)
- AssetDatabase.CreateFolder / file writes
- Create/edit .cs/.asmdef (use Terminal/IDE instead)
Need files/dirs? Run terminal commands.")]
public string Code { get; set; } = "";
/// <summary>Runtime parameters (advanced; usually unnecessary)</summary>
[EditorBrowsable(EditorBrowsableState.Advanced)]
[Description(@"Advanced, optional parameters passed to the snippet.
- For throwaway snippets, prefer defining locals directly in code
- Keys are not preserved; values map to parameters[""param0""], parameters[""param1""], ...
- Use when injecting UnityEngine.Object or sensitive values, or reusing a snippet with varying data
- Use object values supported by the Editor; prefer fully-qualified type names for ambiguous refs (e.g., UnityEngine.Object)
- Input type must be an OBJECT. Strings like ""{}"" are invalid.
- Do: omit 'Parameters' entirely, or use {}
- Don't: ""{}"" (string), ""{""a"":1}"" (string)
Examples:
OK: ""Parameters"": {}
OK: (omit the 'Parameters' property)
NG: ""Parameters"": ""{}""")]
public Dictionary<string, object> Parameters { get; set; } = new();
/// <summary>Compile only (do not execute)</summary>
[Description(@"Compile only (no execution).
- Uses Roslyn validation to surface diagnostics
- For new MonoBehaviours: create .cs → compile (mcp__uLoopMCP__compile with ForceRecompile=false) → ensure ErrorCount=0 → AddComponent")]
public bool CompileOnly { get; set; } = false;
[EditorBrowsable(EditorBrowsableState.Advanced)]
public bool YieldToForegroundRequests { get; set; } = false;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 96f1941ea53e64fbf9dca3d1b273ae72
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,47 @@
using System.Threading;
using System.Threading.Tasks;
namespace io.github.hatayama.uLoopMCP
{
/// <summary>
/// MCP Dynamic C# Code Execution Tool.
/// Delegates workflow orchestration to the dedicated execute-dynamic-code use case.
/// </summary>
[McpTool(Description = @"Execute C# code dynamically in Unity Editor for editor automation.
Use find-game-objects first for basic selected GameObject discovery or property inspection. Use this after built-in inspection tools are not enough or when you need to modify Unity state.
Direct statements only (no classes/namespaces/methods); return is optional (auto 'return null;' if omitted).
You may include using directives at the top; they are hoisted above the wrapper.
Example:
using UnityEngine;
var x = Mathf.PI;
return x;
Do:
- Prefab/material wiring (PrefabUtility)
- AddComponent + reference wiring (SerializedObject)
- Scene/hierarchy edits
Don't:
- System.IO.* (File/Directory/Path)
- AssetDatabase.CreateFolder / file writes
- Create/edit .cs/.asmdef (use Terminal/IDE instead)
Need files/dirs? Run terminal commands.
See examples at {project_root}/.claude/skills/unity-cli-loop/uloop-execute-dynamic-code/examples/")]
public class ExecuteDynamicCodeTool : AbstractUnityTool<ExecuteDynamicCodeSchema, ExecuteDynamicCodeResponse>
{
public override string ToolName => "execute-dynamic-code";
protected override async Task<ExecuteDynamicCodeResponse> ExecuteAsync(
ExecuteDynamicCodeSchema parameters,
CancellationToken cancellationToken)
{
IExecuteDynamicCodeUseCase useCase = await DynamicCodeServices.GetExecuteDynamicCodeUseCaseAsync();
return await useCase.ExecuteAsync(parameters, cancellationToken);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c0a0005c984014fe9ae1227c1e458432
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e4545560316c242f08f6034dc0c57177
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,87 @@
---
name: uloop-execute-dynamic-code
description: "Execute C# with Unity APIs when existing uloop tools cannot inspect or edit enough. Use for scene, prefab, SerializedObject, AssetDatabase refresh/.meta generation, menu, or PlayMode automation."
context: fork
---
# Task
Execute the following request using `uloop execute-dynamic-code`: $ARGUMENTS
For basic selected GameObject discovery or property inspection, use `find-game-objects --search-mode Selected` before this tool. Use this tool after the built-in inspection tools are not enough or when you need to modify Unity state.
## Workflow
1. Read the relevant reference file(s) from the Code Examples section below
2. Construct C# code based on the reference examples
3. For multiline snippets, write the C# statements to a temporary `.csx` file and execute `uloop execute-dynamic-code --code-file <path>`
4. Use `uloop execute-dynamic-code --code '<code>'` only for short one-line snippets
5. If execution fails, adjust code and retry
6. Report the execution result
## Parameters
- `--code '<code>'`: Inline C# statements to execute. Use this for one-line snippets only.
- `--code-file <path>`: Read C# statements from a UTF-8 file. Prefer this for multiline snippets, especially on PowerShell, because Windows `.cmd` shims can lose lines from multiline inline arguments before `uloop` receives them.
- **Shell quoting**: bash/zsh uses single quotes, for example `uloop execute-dynamic-code --code 'using UnityEngine; return Mathf.PI;'`. PowerShell single-quoted strings can contain normal double quotes, for example `uloop execute-dynamic-code --code 'Debug.Log("Hello!");'`.
- `--parameters {}` (advanced, optional): Pass an object when reusing a snippet with varying data or when keeping values outside the code. Values are exposed as `parameters["param0"]`, `parameters["param1"]`, and so on. Omit this flag for most snippets, and pass an object instead of a JSON string.
- `--compile-only true` (optional): Compile the snippet without executing it. Use this when you want Roslyn diagnostics before running new code.
## Code Rules
Write direct statements only — no class/namespace/method wrappers. Return is optional.
```csharp
using UnityEngine;
float x = Mathf.PI;
return x;
```
**Forbidden** — these will be rejected at compile time: `System.IO.*`, `AssetDatabase.CreateFolder`, creating/editing `.cs`/`.asmdef` files. Use terminal commands for file operations instead.
## Output
Returns JSON:
- `Success`: boolean — overall execution success
- `Result`: string — value of the snippet's `return` statement (empty when omitted)
- `Logs`: string[] — execution diagnostics from the dynamic-code runner, not Unity Console entries
- `CompilationErrors`: object[] — Roslyn diagnostics with `Message`, `Line`, `Column`, `ErrorCode`, optional `Hint` and `Suggestions`
- `ErrorMessage`: string — top-level failure summary (empty on success)
- `Error`: string — alias of `ErrorMessage`
- `SecurityLevel`: string — dynamic-code security level active for the request
- `UpdatedCode`: string|null — the wrapped form actually compiled (handy when debugging using-statement reordering)
- `DiagnosticsSummary`: string|null — compact summary when diagnostics are available
- `Diagnostics`: object[] — structured diagnostics; same shape as `CompilationErrors`, usually populated together with it
Use `uloop get-logs` to retrieve `Debug.Log`, `Debug.LogWarning`, and `Debug.LogError` messages emitted by the snippet. On `Success: false`, inspect `CompilationErrors` first. If empty, read `ErrorMessage` (and `Logs` for extra context) — the failure may be a runtime exception, security violation, cancellation, or an "execution in progress" rejection, all of which return empty `CompilationErrors`. Both EditMode and PlayMode are supported targets — the snippet runs in whichever mode the Editor is currently in.
## Code Examples by Category
For detailed code examples, refer to these files:
- **Prefab operations**: See [references/prefab-operations.md](references/prefab-operations.md)
- Create prefabs, instantiate, add components, modify properties
- **Material operations**: See [references/material-operations.md](references/material-operations.md)
- Create materials, set shaders/textures, modify properties
- **Asset operations**: See [references/asset-operations.md](references/asset-operations.md)
- Find/search assets, duplicate, move, rename, load
- **ScriptableObject**: See [references/scriptableobject.md](references/scriptableobject.md)
- Create ScriptableObjects, modify with SerializedObject
- **Scene operations**: See [references/scene-operations.md](references/scene-operations.md)
- Create/modify GameObjects, set parents, wire references, load scenes
- **Batch operations**: See [references/batch-operations.md](references/batch-operations.md)
- Bulk modify objects, batch add/remove components, rename, layer/tag/material replacement
- **Cleanup operations**: See [references/cleanup-operations.md](references/cleanup-operations.md)
- Detect broken scripts, missing references, unused materials, empty GameObjects
- **Undo operations**: See [references/undo-operations.md](references/undo-operations.md)
- Undo-aware operations: RecordObject, AddComponent, SetParent, grouping
- **Selection operations**: See [references/selection-operations.md](references/selection-operations.md)
- Get/set selection, multi-select, filter by type/editability
- **PlayMode automation (zsh)**: See [references/playmode-automation-zsh.md](references/playmode-automation-zsh.md)
- Click UI buttons, invoke methods, set fields, tool combination workflows for zsh users
- **PlayMode automation (PowerShell)**: See [references/playmode-automation-powershell.md](references/playmode-automation-powershell.md)
- Click UI buttons, invoke methods, set fields, tool combination workflows for PowerShell users
- **PlayMode UI controls**: See [references/playmode-ui-controls.md](references/playmode-ui-controls.md)
- InputField, Slider, Toggle, Dropdown, drag & drop simulation, list all UI controls
- **PlayMode inspection**: See [references/playmode-inspection.md](references/playmode-inspection.md)
- Scene info, game state via reflection, physics state, raycast checks, GameObject search, position/rotation

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 2445f075f1af349fc8bf73259f1df493
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c5155f736569e440ea4679a66a5ee36b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,194 @@
# Asset Operations
Code examples for AssetDatabase operations using `execute-dynamic-code`.
## Find Assets by Type
```csharp
using UnityEditor;
using System.Collections.Generic;
string[] prefabGuids = AssetDatabase.FindAssets("t:Prefab");
List<string> paths = new List<string>();
foreach (string guid in prefabGuids)
{
paths.Add(AssetDatabase.GUIDToAssetPath(guid));
}
return $"Found {paths.Count} prefabs";
```
## Find Assets by Name
```csharp
using UnityEditor;
using System.Collections.Generic;
string searchName = "Player";
string[] guids = AssetDatabase.FindAssets(searchName);
List<string> paths = new List<string>();
foreach (string guid in guids)
{
paths.Add(AssetDatabase.GUIDToAssetPath(guid));
}
return $"Found {paths.Count} assets matching '{searchName}'";
```
## Find Assets in Folder
```csharp
using UnityEditor;
using System.Collections.Generic;
string folder = "Assets/Prefabs";
string[] guids = AssetDatabase.FindAssets("t:Prefab", new[] { folder });
List<string> paths = new List<string>();
foreach (string guid in guids)
{
paths.Add(AssetDatabase.GUIDToAssetPath(guid));
}
return $"Found {paths.Count} prefabs in {folder}";
```
## Duplicate Asset
```csharp
using UnityEditor;
string sourcePath = "Assets/Materials/MyMaterial.mat";
string destPath = "Assets/Materials/MyMaterial_Backup.mat";
bool success = AssetDatabase.CopyAsset(sourcePath, destPath);
return success ? $"Copied to {destPath}" : "Copy failed";
```
## Move Asset
```csharp
using UnityEditor;
string sourcePath = "Assets/OldFolder/MyAsset.asset";
string destPath = "Assets/NewFolder/MyAsset.asset";
string error = AssetDatabase.MoveAsset(sourcePath, destPath);
return string.IsNullOrEmpty(error) ? $"Moved to {destPath}" : $"Error: {error}";
```
## Rename Asset
```csharp
using UnityEditor;
string assetPath = "Assets/Materials/OldName.mat";
string newName = "NewName";
string error = AssetDatabase.RenameAsset(assetPath, newName);
return string.IsNullOrEmpty(error) ? $"Renamed to {newName}" : $"Error: {error}";
```
## Rename Asset (Undo-supported)
```csharp
using UnityEditor;
// ObjectNames.SetNameSmart() supports Undo (AssetDatabase.RenameAsset() does NOT)
Object selected = Selection.activeObject;
if (selected == null)
{
return "No asset selected";
}
string oldName = selected.name;
ObjectNames.SetNameSmart(selected, "NewName");
AssetDatabase.SaveAssets();
return $"Renamed {oldName} to {selected.name}";
```
## Get Asset Path from Object
```csharp
using UnityEditor;
GameObject selected = Selection.activeGameObject;
if (selected == null)
{
return "No object selected";
}
string path = AssetDatabase.GetAssetPath(selected);
if (string.IsNullOrEmpty(path))
{
return "Selected object is not an asset (scene object)";
}
return $"Asset path: {path}";
```
## Load Asset at Path
```csharp
using UnityEditor;
string path = "Assets/Prefabs/Player.prefab";
GameObject asset = AssetDatabase.LoadAssetAtPath<GameObject>(path);
if (asset == null)
{
return $"Asset not found at {path}";
}
return $"Loaded: {asset.name}";
```
## Get All Assets of Type
```csharp
using UnityEditor;
string[] scriptGuids = AssetDatabase.FindAssets("t:MonoScript");
int count = 0;
foreach (string guid in scriptGuids)
{
string path = AssetDatabase.GUIDToAssetPath(guid);
if (path.StartsWith("Assets/"))
{
count++;
}
}
return $"Found {count} scripts in Assets folder";
```
## Check if Asset Exists
```csharp
using UnityEditor;
string path = "Assets/Prefabs/Player.prefab";
string guid = AssetDatabase.AssetPathToGUID(path);
bool exists = !string.IsNullOrEmpty(guid);
return exists ? $"Asset exists: {path}" : $"Asset not found: {path}";
```
## Get Asset Dependencies
```csharp
using UnityEditor;
string assetPath = "Assets/Prefabs/Player.prefab";
string[] dependencies = AssetDatabase.GetDependencies(assetPath, true);
return $"Asset has {dependencies.Length} dependencies";
```
## Refresh AssetDatabase
Use this after terminal-based asset file changes when Unity needs to import them and generate `.meta` files.
```csharp
using UnityEditor;
AssetDatabase.Refresh();
return "AssetDatabase refreshed";
```

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 39fd76f51419343c1bbc8844b9e1f45f
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,399 @@
# Batch Operations
Code examples for batch processing using `execute-dynamic-code`.
## Batch Modify Selected Objects
```csharp
using UnityEditor;
using System.Collections.Generic;
GameObject[] selected = Selection.gameObjects;
if (selected.Length == 0)
{
return "No GameObjects selected";
}
int undoGroup = Undo.GetCurrentGroup();
Undo.SetCurrentGroupName("Batch Modify");
foreach (GameObject obj in selected)
{
Undo.RecordObject(obj.transform, "");
obj.transform.localScale = Vector3.one * 2;
}
Undo.CollapseUndoOperations(undoGroup);
return $"Scaled {selected.Length} objects (Single undo step)";
```
## Edit Multiple Objects with SerializedObject
```csharp
using UnityEditor;
GameObject[] selected = Selection.gameObjects;
if (selected.Length == 0)
{
return "No GameObjects selected";
}
List<Transform> transforms = new List<Transform>();
foreach (GameObject obj in selected)
{
transforms.Add(obj.transform);
}
SerializedObject serializedObj = new SerializedObject(transforms.ToArray());
SerializedProperty positionProp = serializedObj.FindProperty("m_LocalPosition");
positionProp.vector3Value = Vector3.zero;
serializedObj.ApplyModifiedProperties();
return $"Reset position of {selected.Length} objects";
```
## Batch Add Component
```csharp
using UnityEditor;
GameObject[] selected = Selection.gameObjects;
if (selected.Length == 0)
{
return "No GameObjects selected";
}
int undoGroup = Undo.GetCurrentGroup();
Undo.SetCurrentGroupName("Batch Add Rigidbody");
int addedCount = 0;
foreach (GameObject obj in selected)
{
if (obj.GetComponent<Rigidbody>() == null)
{
Undo.AddComponent<Rigidbody>(obj);
addedCount++;
}
}
Undo.CollapseUndoOperations(undoGroup);
return $"Added Rigidbody to {addedCount} objects";
```
## Batch Process Assets with StartAssetEditing
```csharp
using UnityEditor;
string[] guids = AssetDatabase.FindAssets("t:Material", new[] { "Assets/Materials" });
if (guids.Length == 0)
{
return "No materials found";
}
AssetDatabase.StartAssetEditing();
try
{
int modified = 0;
foreach (string guid in guids)
{
string path = AssetDatabase.GUIDToAssetPath(guid);
Material mat = AssetDatabase.LoadAssetAtPath<Material>(path);
if (mat != null)
{
mat.color = Color.white;
EditorUtility.SetDirty(mat);
modified++;
}
}
AssetDatabase.SaveAssets();
return $"Reset color of {modified} materials";
}
finally
{
AssetDatabase.StopAssetEditing();
}
```
## Batch Rename GameObjects
```csharp
using UnityEditor;
GameObject[] selected = Selection.gameObjects;
if (selected.Length == 0)
{
return "No GameObjects selected";
}
int undoGroup = Undo.GetCurrentGroup();
Undo.SetCurrentGroupName("Batch Rename");
for (int i = 0; i < selected.Length; i++)
{
Undo.RecordObject(selected[i], "");
selected[i].name = $"Item_{i:D3}";
}
Undo.CollapseUndoOperations(undoGroup);
return $"Renamed {selected.Length} objects";
```
## Batch Set Layer
```csharp
using UnityEditor;
GameObject[] selected = Selection.gameObjects;
if (selected.Length == 0)
{
return "No GameObjects selected";
}
int layer = LayerMask.NameToLayer("Default");
int undoGroup = Undo.GetCurrentGroup();
Undo.SetCurrentGroupName("Batch Set Layer");
foreach (GameObject obj in selected)
{
Undo.RecordObject(obj, "");
obj.layer = layer;
}
Undo.CollapseUndoOperations(undoGroup);
return $"Set layer of {selected.Length} objects to Default";
```
## Batch Set Tag
```csharp
using UnityEditor;
GameObject[] selected = Selection.gameObjects;
if (selected.Length == 0)
{
return "No GameObjects selected";
}
int undoGroup = Undo.GetCurrentGroup();
Undo.SetCurrentGroupName("Batch Set Tag");
foreach (GameObject obj in selected)
{
Undo.RecordObject(obj, "");
obj.tag = "Enemy";
}
Undo.CollapseUndoOperations(undoGroup);
return $"Tagged {selected.Length} objects as Enemy";
```
## Batch Modify ScriptableObjects
```csharp
using UnityEditor;
string[] guids = AssetDatabase.FindAssets("t:ScriptableObject", new[] { "Assets/Data" });
if (guids.Length == 0)
{
return "No ScriptableObjects found";
}
int modified = 0;
foreach (string guid in guids)
{
string path = AssetDatabase.GUIDToAssetPath(guid);
ScriptableObject so = AssetDatabase.LoadAssetAtPath<ScriptableObject>(path);
if (so == null) continue;
SerializedObject serializedObj = new SerializedObject(so);
SerializedProperty prop = serializedObj.FindProperty("isEnabled");
if (prop != null)
{
prop.boolValue = true;
serializedObj.ApplyModifiedProperties();
EditorUtility.SetDirty(so);
modified++;
}
}
AssetDatabase.SaveAssets();
return $"Enabled {modified} ScriptableObjects";
```
## Batch Remove Component
```csharp
using UnityEditor;
GameObject[] selected = Selection.gameObjects;
if (selected.Length == 0)
{
return "No GameObjects selected";
}
int undoGroup = Undo.GetCurrentGroup();
Undo.SetCurrentGroupName("Batch Remove Rigidbody");
int removedCount = 0;
foreach (GameObject obj in selected)
{
Rigidbody rb = obj.GetComponent<Rigidbody>();
if (rb != null)
{
Undo.DestroyObjectImmediate(rb);
removedCount++;
}
}
Undo.CollapseUndoOperations(undoGroup);
return $"Removed Rigidbody from {removedCount} objects";
```
## Batch Set Static Flags
```csharp
using UnityEditor;
GameObject[] selected = Selection.gameObjects;
if (selected.Length == 0)
{
return "No GameObjects selected";
}
int undoGroup = Undo.GetCurrentGroup();
Undo.SetCurrentGroupName("Batch Set Static");
foreach (GameObject obj in selected)
{
Undo.RecordObject(obj, "");
GameObjectUtility.SetStaticEditorFlags(obj, StaticEditorFlags.BatchingStatic | StaticEditorFlags.OccludeeStatic);
}
Undo.CollapseUndoOperations(undoGroup);
return $"Set static flags on {selected.Length} objects";
```
## Batch Process with Progress Bar
```csharp
using UnityEditor;
string[] guids = AssetDatabase.FindAssets("t:Texture2D");
if (guids.Length == 0)
{
return "No textures found";
}
int processed = 0;
foreach (string guid in guids)
{
string path = AssetDatabase.GUIDToAssetPath(guid);
TextureImporter importer = AssetImporter.GetAtPath(path) as TextureImporter;
if (importer != null && importer.maxTextureSize > 1024)
{
importer.maxTextureSize = 1024;
importer.SaveAndReimport();
processed++;
}
if (processed % 10 == 0)
{
EditorUtility.DisplayProgressBar("Processing Textures", path, (float)processed / guids.Length);
}
}
EditorUtility.ClearProgressBar();
return $"Resized {processed} textures to max 1024";
```
## Batch Align Objects
```csharp
using UnityEditor;
GameObject[] selected = Selection.gameObjects;
if (selected.Length < 2)
{
return "Select at least 2 objects";
}
int undoGroup = Undo.GetCurrentGroup();
Undo.SetCurrentGroupName("Align Objects");
float startX = selected[0].transform.position.x;
float spacing = 2f;
for (int i = 0; i < selected.Length; i++)
{
Undo.RecordObject(selected[i].transform, "");
Vector3 pos = selected[i].transform.position;
pos.x = startX + (i * spacing);
selected[i].transform.position = pos;
}
Undo.CollapseUndoOperations(undoGroup);
return $"Aligned {selected.Length} objects with {spacing}m spacing";
```
## Batch Rename Assets (Undo-supported)
```csharp
using UnityEditor;
// ObjectNames.SetNameSmart() supports Undo (AssetDatabase.RenameAsset() does NOT)
Object[] selected = Selection.objects;
if (selected.Length == 0)
{
return "No assets selected";
}
for (int i = 0; i < selected.Length; i++)
{
string newName = $"{i:D3}_{selected[i].name}";
ObjectNames.SetNameSmart(selected[i], newName);
}
AssetDatabase.SaveAssets();
return $"Renamed {selected.Length} assets";
```
## Batch Replace Material
```csharp
using UnityEditor;
GameObject[] selected = Selection.gameObjects;
if (selected.Length == 0)
{
return "No GameObjects selected";
}
string materialPath = "Assets/Materials/NewMaterial.mat";
Material newMat = AssetDatabase.LoadAssetAtPath<Material>(materialPath);
if (newMat == null)
{
return $"Material not found at {materialPath}";
}
int undoGroup = Undo.GetCurrentGroup();
Undo.SetCurrentGroupName("Batch Replace Material");
int replaced = 0;
foreach (GameObject obj in selected)
{
MeshRenderer renderer = obj.GetComponent<MeshRenderer>();
if (renderer != null)
{
Undo.RecordObject(renderer, "");
renderer.sharedMaterial = newMat;
replaced++;
}
}
Undo.CollapseUndoOperations(undoGroup);
return $"Replaced material on {replaced} objects";
```

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: cd5beae8d98da49f590990720e8d86be
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,403 @@
# Cleanup Operations
Code examples for project cleanup operations using `execute-dynamic-code`.
## Detect Missing Scripts on GameObject
```csharp
using UnityEditor;
GameObject selected = Selection.activeGameObject;
if (selected == null)
{
return "No GameObject selected";
}
int missingCount = GameObjectUtility.GetMonoBehavioursWithMissingScriptCount(selected);
return $"{selected.name} has {missingCount} missing script(s)";
```
## Remove Missing Scripts from GameObject
```csharp
using UnityEditor;
GameObject selected = Selection.activeGameObject;
if (selected == null)
{
return "No GameObject selected";
}
int removedCount = GameObjectUtility.RemoveMonoBehavioursWithMissingScript(selected);
return $"Removed {removedCount} missing script(s) from {selected.name}";
```
## Scan Scene for Missing Scripts
```csharp
using UnityEditor;
GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
List<string> objectsWithMissing = new List<string>();
foreach (GameObject obj in allObjects)
{
int count = GameObjectUtility.GetMonoBehavioursWithMissingScriptCount(obj);
if (count > 0)
{
objectsWithMissing.Add($"{obj.name} ({count})");
}
}
if (objectsWithMissing.Count == 0)
{
return "No missing scripts found in scene";
}
return $"Objects with missing scripts: {string.Join(", ", objectsWithMissing)}";
```
## Remove All Missing Scripts from Scene
```csharp
using UnityEditor;
GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
int totalRemoved = 0;
int undoGroup = Undo.GetCurrentGroup();
Undo.SetCurrentGroupName("Remove All Missing Scripts");
foreach (GameObject obj in allObjects)
{
int removed = GameObjectUtility.RemoveMonoBehavioursWithMissingScript(obj);
totalRemoved += removed;
}
Undo.CollapseUndoOperations(undoGroup);
return $"Removed {totalRemoved} missing scripts from scene";
```
## Detect Missing References in Component
```csharp
using UnityEditor;
GameObject selected = Selection.activeGameObject;
if (selected == null)
{
return "No GameObject selected";
}
List<string> missingRefs = new List<string>();
Component[] components = selected.GetComponents<Component>();
foreach (Component comp in components)
{
if (comp == null) continue;
SerializedObject so = new SerializedObject(comp);
SerializedProperty prop = so.GetIterator();
while (prop.NextVisible(true))
{
if (prop.propertyType == SerializedPropertyType.ObjectReference)
{
if (prop.objectReferenceValue == null && prop.objectReferenceInstanceIDValue != 0)
{
missingRefs.Add($"{comp.GetType().Name}.{prop.name}");
}
}
}
}
if (missingRefs.Count == 0)
{
return "No missing references found";
}
return $"Missing references: {string.Join(", ", missingRefs)}";
```
## Scan Scene for Missing References
```csharp
using UnityEditor;
GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
List<string> results = new List<string>();
foreach (GameObject obj in allObjects)
{
Component[] components = obj.GetComponents<Component>();
foreach (Component comp in components)
{
if (comp == null) continue;
SerializedObject so = new SerializedObject(comp);
SerializedProperty prop = so.GetIterator();
while (prop.NextVisible(true))
{
if (prop.propertyType == SerializedPropertyType.ObjectReference)
{
if (prop.objectReferenceValue == null && prop.objectReferenceInstanceIDValue != 0)
{
results.Add($"{obj.name}/{comp.GetType().Name}.{prop.name}");
}
}
}
}
}
if (results.Count == 0)
{
return "No missing references found in scene";
}
return $"Missing references ({results.Count}): {string.Join(", ", results.Take(10))}...";
```
## Find Unused Materials in Project
```csharp
using UnityEditor;
using System.Collections.Generic;
string[] materialGuids = AssetDatabase.FindAssets("t:Material");
HashSet<string> usedMaterials = new HashSet<string>();
string[] prefabGuids = AssetDatabase.FindAssets("t:Prefab");
foreach (string guid in prefabGuids)
{
string path = AssetDatabase.GUIDToAssetPath(guid);
string[] deps = AssetDatabase.GetDependencies(path, true);
foreach (string dep in deps)
{
if (dep.EndsWith(".mat"))
{
usedMaterials.Add(dep);
}
}
}
// This scan only checks prefab dependencies. Verify scene and other asset
// references manually before deleting any reported materials.
List<string> unusedMaterials = new List<string>();
foreach (string guid in materialGuids)
{
string path = AssetDatabase.GUIDToAssetPath(guid);
if (!usedMaterials.Contains(path))
{
unusedMaterials.Add(path);
}
}
return $"Found {unusedMaterials.Count} materials not referenced by prefabs. Verify scene and other asset references manually before deleting.";
```
## Find Empty GameObjects
```csharp
using UnityEditor;
GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
List<string> emptyObjects = new List<string>();
foreach (GameObject obj in allObjects)
{
Component[] components = obj.GetComponents<Component>();
if (components.Length == 1 && obj.transform.childCount == 0)
{
emptyObjects.Add(obj.name);
}
}
if (emptyObjects.Count == 0)
{
return "No empty GameObjects found";
}
return $"Empty objects ({emptyObjects.Count}): {string.Join(", ", emptyObjects.Take(20))}";
```
## Find Duplicate Names in Hierarchy
```csharp
using UnityEditor;
GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
Dictionary<string, int> nameCounts = new Dictionary<string, int>();
foreach (GameObject obj in allObjects)
{
if (nameCounts.ContainsKey(obj.name))
{
nameCounts[obj.name]++;
}
else
{
nameCounts[obj.name] = 1;
}
}
List<string> duplicates = new List<string>();
foreach (KeyValuePair<string, int> kvp in nameCounts)
{
if (kvp.Value > 1)
{
duplicates.Add($"{kvp.Key} ({kvp.Value})");
}
}
if (duplicates.Count == 0)
{
return "No duplicate names found";
}
return $"Duplicate names: {string.Join(", ", duplicates.Take(15))}";
```
## Check for Broken Prefab Instances
```csharp
using UnityEditor;
GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
List<string> brokenPrefabs = new List<string>();
foreach (GameObject obj in allObjects)
{
if (PrefabUtility.IsPartOfPrefabInstance(obj))
{
GameObject prefabAsset = PrefabUtility.GetCorrespondingObjectFromSource(obj);
if (prefabAsset == null)
{
brokenPrefabs.Add(obj.name);
}
}
}
if (brokenPrefabs.Count == 0)
{
return "No broken prefab instances found";
}
return $"Broken prefab instances: {string.Join(", ", brokenPrefabs)}";
```
## Find Objects with Negative Scale
```csharp
using UnityEditor;
GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
List<string> negativeScale = new List<string>();
foreach (GameObject obj in allObjects)
{
Vector3 scale = obj.transform.localScale;
if (scale.x < 0 || scale.y < 0 || scale.z < 0)
{
negativeScale.Add($"{obj.name} ({scale})");
}
}
if (negativeScale.Count == 0)
{
return "No objects with negative scale found";
}
return $"Negative scale objects: {string.Join(", ", negativeScale.Take(10))}";
```
## Remove Empty Leaf GameObjects
```csharp
using UnityEditor;
GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
int undoGroup = Undo.GetCurrentGroup();
Undo.SetCurrentGroupName("Remove Empty Leaves");
int removedCount = 0;
foreach (GameObject obj in allObjects)
{
if (obj == null) continue;
Component[] components = obj.GetComponents<Component>();
if (components.Length == 1 && obj.transform.childCount == 0)
{
Undo.DestroyObjectImmediate(obj);
removedCount++;
}
}
Undo.CollapseUndoOperations(undoGroup);
return $"Removed {removedCount} empty leaf GameObjects";
```
## Find Large Meshes
```csharp
using UnityEditor;
string[] meshGuids = AssetDatabase.FindAssets("t:Mesh");
List<string> largeMeshes = new List<string>();
int threshold = 10000;
foreach (string guid in meshGuids)
{
string path = AssetDatabase.GUIDToAssetPath(guid);
Mesh mesh = AssetDatabase.LoadAssetAtPath<Mesh>(path);
if (mesh != null && mesh.vertexCount > threshold)
{
largeMeshes.Add($"{path} ({mesh.vertexCount} verts)");
}
}
if (largeMeshes.Count == 0)
{
return $"No meshes with more than {threshold} vertices found";
}
return $"Large meshes: {string.Join(", ", largeMeshes.Take(10))}";
```
## Validate Asset References
```csharp
using UnityEditor;
string[] guids = AssetDatabase.FindAssets("t:ScriptableObject", new[] { "Assets/Data" });
List<string> invalidRefs = new List<string>();
foreach (string guid in guids)
{
string path = AssetDatabase.GUIDToAssetPath(guid);
ScriptableObject so = AssetDatabase.LoadAssetAtPath<ScriptableObject>(path);
if (so == null) continue;
SerializedObject serializedObj = new SerializedObject(so);
SerializedProperty prop = serializedObj.GetIterator();
while (prop.NextVisible(true))
{
if (prop.propertyType == SerializedPropertyType.ObjectReference)
{
if (prop.objectReferenceValue == null && prop.objectReferenceInstanceIDValue != 0)
{
invalidRefs.Add($"{path}: {prop.name}");
}
}
}
}
if (invalidRefs.Count == 0)
{
return "All asset references are valid";
}
return $"Invalid references ({invalidRefs.Count}): {string.Join(", ", invalidRefs.Take(10))}";
```

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 13f19aa1f6ea44f119e9e253e14d731c
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,160 @@
# Material Operations
Code examples for Material operations using `execute-dynamic-code`.
## Create a New Material
```csharp
using UnityEditor;
Shader shader = Shader.Find("Standard");
Material mat = new Material(shader);
mat.name = "MyMaterial";
string path = "Assets/Materials/MyMaterial.mat";
AssetDatabase.CreateAsset(mat, path);
AssetDatabase.SaveAssets();
return $"Material created at {path}";
```
## Set Material Color
```csharp
using UnityEditor;
string matPath = "Assets/Materials/MyMaterial.mat";
Material mat = AssetDatabase.LoadAssetAtPath<Material>(matPath);
if (mat == null)
{
return $"Material not found at {matPath}";
}
mat.SetColor("_Color", new Color(1f, 0.5f, 0f, 1f));
EditorUtility.SetDirty(mat);
AssetDatabase.SaveAssets();
return "Material color set to orange";
```
## Set Material Properties (Float, Vector)
```csharp
using UnityEditor;
string matPath = "Assets/Materials/MyMaterial.mat";
Material mat = AssetDatabase.LoadAssetAtPath<Material>(matPath);
mat.SetFloat("_Metallic", 0.8f);
mat.SetFloat("_Glossiness", 0.6f);
mat.SetVector("_EmissionColor", new Vector4(1, 1, 0, 1));
EditorUtility.SetDirty(mat);
AssetDatabase.SaveAssets();
return "Material properties updated";
```
## Assign Texture to Material
```csharp
using UnityEditor;
string matPath = "Assets/Materials/MyMaterial.mat";
string texPath = "Assets/Textures/MyTexture.png";
Material mat = AssetDatabase.LoadAssetAtPath<Material>(matPath);
Texture2D tex = AssetDatabase.LoadAssetAtPath<Texture2D>(texPath);
if (mat == null)
{
return $"Material not found at {matPath}";
}
if (tex == null)
{
return $"Texture not found at {texPath}";
}
mat.SetTexture("_MainTex", tex);
EditorUtility.SetDirty(mat);
AssetDatabase.SaveAssets();
return $"Assigned {tex.name} to material";
```
## Assign Material to GameObject
```csharp
using UnityEditor;
string matPath = "Assets/Materials/MyMaterial.mat";
Material mat = AssetDatabase.LoadAssetAtPath<Material>(matPath);
GameObject selected = Selection.activeGameObject;
if (selected == null)
{
return "No GameObject selected";
}
Renderer renderer = selected.GetComponent<Renderer>();
if (renderer == null)
{
return "Selected object has no Renderer";
}
renderer.sharedMaterial = mat;
EditorUtility.SetDirty(selected);
return $"Assigned {mat.name} to {selected.name}";
```
## Enable/Disable Material Keywords
```csharp
using UnityEditor;
string matPath = "Assets/Materials/MyMaterial.mat";
Material mat = AssetDatabase.LoadAssetAtPath<Material>(matPath);
mat.EnableKeyword("_EMISSION");
mat.globalIlluminationFlags = MaterialGlobalIlluminationFlags.RealtimeEmissive;
EditorUtility.SetDirty(mat);
AssetDatabase.SaveAssets();
return "Emission enabled on material";
```
## Find All Materials Using a Shader
```csharp
using UnityEditor;
using System.Collections.Generic;
string shaderName = "Standard";
string[] guids = AssetDatabase.FindAssets("t:Material");
List<string> matchingMaterials = new List<string>();
foreach (string guid in guids)
{
string path = AssetDatabase.GUIDToAssetPath(guid);
Material mat = AssetDatabase.LoadAssetAtPath<Material>(path);
if (mat != null && mat.shader != null && mat.shader.name == shaderName)
{
matchingMaterials.Add(path);
}
}
return $"Found {matchingMaterials.Count} materials using {shaderName}";
```
## Duplicate Material
```csharp
using UnityEditor;
string sourcePath = "Assets/Materials/MyMaterial.mat";
string destPath = "Assets/Materials/MyMaterial_Copy.mat";
Material source = AssetDatabase.LoadAssetAtPath<Material>(sourcePath);
if (source == null)
{
return $"Material not found at {sourcePath}";
}
Material copy = new Material(source);
AssetDatabase.CreateAsset(copy, destPath);
AssetDatabase.SaveAssets();
return $"Material duplicated to {destPath}";
```

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 6b5ad08d159fb4390911b484746791d1
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,330 @@
# PlayMode Automation (PowerShell)
Code examples for runtime automation during Play mode using `execute-dynamic-code`.
These examples manipulate live scene objects while the game is running.
Shell command examples in this file target `PowerShell`.
## When to use dedicated mouse simulation tools instead
The examples in this file call UI handlers or runtime methods from C#.
That remains the better choice when you want targeted automation, direct state control, or a quick diagnostic path.
Use dedicated mouse tools only when the input route itself is part of what you need to verify.
Choose the tool based on what you are trying to validate:
| Scenario | Recommended tool | Why |
|----------|------------------|-----|
| Verify that a uGUI element responds through the real EventSystem pointer path | `simulate-mouse-ui` | Fires `PointerDown` / `PointerUp` / `PointerClick` / drag events through EventSystem raycasts instead of bypassing the UI input route. |
| Test gameplay that reads `Mouse.current`, button state, delta, or scroll | `simulate-mouse-input` | Injects Input System mouse state into `Mouse.current`, so game code can observe `wasPressedThisFrame`, movement delta, and scroll like player input. This assumes the project uses the New Input System (`Input System Package (New)` or `Both`). If that is not available in the target project, prefer `execute-dynamic-code` for a project-specific workaround instead of changing project settings just to use this tool. |
| Jump straight to a known button callback, invoke a method, inspect state, or set up a test precondition | `execute-dynamic-code` | Best when you intentionally want direct automation without reproducing the full input pipeline. |
| Drive custom runtime behavior that does not map cleanly to the built-in mouse tools | `execute-dynamic-code` | Lets you call project-specific methods, inspect scene objects, and prototype one-off flows immediately. |
In short: do not default everything to mouse simulation.
Use `execute-dynamic-code` for direct automation and diagnostics, and switch to `simulate-mouse-ui` or `simulate-mouse-input` when reproducing the real input path is the thing you need to test.
## PowerShell Quoting Notes
Use these patterns when you need shell-safe inline code:
### Double quotes inside C# strings
Single-quote the whole snippet and keep C# string literals unchanged.
```powershell
uloop execute-dynamic-code --code 'return "Hello from PowerShell";'
```
### Single quotes inside inline C# code
If the C# snippet itself contains a single quote, double it inside the PowerShell single-quoted string.
```powershell
uloop execute-dynamic-code --code 'char initial = ''A''; return initial.ToString();'
```
### JSON-like values passed via `--parameters`
Wrap the whole expression in single quotes so PowerShell passes the inner double quotes through unchanged.
```powershell
uloop execute-dynamic-code --code 'return parameters["param0"];' --parameters '{"param0":"Hello from PowerShell"}'
```
### Multi-line C# snippets
Use a here-string when the snippet spans multiple lines.
```powershell
$code = @'
using UnityEngine;
GameObject obj = GameObject.Find("Player");
if (obj == null) return "Player not found";
return obj.name;
'@
uloop execute-dynamic-code --code $code
```
You can combine multi-line code with multi-line parameters the same way.
```powershell
$code = @'
return parameters["param0"];
'@
$parameters = @'
{"param0":"Hello from PowerShell"}
'@
uloop execute-dynamic-code --code $code --parameters $parameters
```
## Click UI Button by Path
```csharp
using UnityEngine.UI;
Button btn = GameObject.Find("Canvas/StartButton")?.GetComponent<Button>();
if (btn == null) return "Button not found";
btn.onClick.Invoke();
return $"Clicked {btn.gameObject.name}";
```
## Click UI Button by Search
```csharp
using UnityEngine.UI;
using System.Linq;
Button[] buttons = Object.FindObjectsByType<Button>(FindObjectsSortMode.None);
Button target = buttons.FirstOrDefault(b => b.gameObject.name == "PlayButton");
if (target == null) return $"PlayButton not found. Available: {string.Join(", ", buttons.Select(b => b.gameObject.name))}";
target.onClick.Invoke();
return $"Clicked {target.gameObject.name}";
```
## Toggle GameObject Active State
```csharp
GameObject obj = GameObject.Find("Enemy");
if (obj == null) return "Enemy not found";
obj.SetActive(!obj.activeSelf);
return $"{obj.name} is now {(obj.activeSelf ? "active" : "inactive")}";
```
## Invoke Method on MonoBehaviour
```csharp
using System.Reflection;
GameObject player = GameObject.Find("Player");
if (player == null) return "Player not found";
MonoBehaviour script = player.GetComponent("PlayerController") as MonoBehaviour;
if (script == null) return "PlayerController not found";
MethodInfo method = script.GetType().GetMethod("TakeDamage");
if (method == null) return "TakeDamage method not found";
method.Invoke(script, new object[] { 10f });
return $"Invoked TakeDamage(10) on {player.name}";
```
## Set Field Value at Runtime
```csharp
using System.Reflection;
GameObject player = GameObject.Find("Player");
if (player == null) return "Player not found";
MonoBehaviour script = player.GetComponent("PlayerController") as MonoBehaviour;
if (script == null) return "PlayerController not found";
FieldInfo field = script.GetType().GetField("moveSpeed", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (field == null) return "moveSpeed field not found";
field.SetValue(script, 20f);
return $"Set moveSpeed to 20 on {player.name}";
```
## Move Player to Position
```csharp
GameObject player = GameObject.Find("Player");
if (player == null) return "Player not found";
Vector3 targetPos = new Vector3(5f, 0f, 10f);
player.transform.position = targetPos;
return $"Moved {player.name} to {targetPos}";
```
---
# Tool Combination Workflows
Examples of combining `execute-dynamic-code` with other uloop tools for multi-step PlayMode automation.
## find-game-objects → Click Button
Use `find-game-objects` to discover buttons with their hierarchy paths, then click one via `execute-dynamic-code`.
**Step 1**: Find all GameObjects with Button component
```powershell
uloop find-game-objects --required-components UnityEngine.UI.Button
```
**Step 2**: Click the target button using the path from Step 1
```csharp
using UnityEngine.UI;
// Use the path returned by find-game-objects (e.g. "Canvas/MainMenu/StartButton")
GameObject btnObj = GameObject.Find("Canvas/MainMenu/StartButton");
if (btnObj == null) return "Button not found at path";
Button btn = btnObj.GetComponent<Button>();
if (btn == null) return "No Button component";
btn.onClick.Invoke();
return $"Clicked {btnObj.name}";
```
## get-hierarchy → Navigate UI and Click
Use `get-hierarchy` to explore the UI tree structure, then target the right element.
**Step 1**: Get Canvas hierarchy to understand UI structure
```powershell
uloop get-hierarchy --root-path 'Canvas' --max-depth 3
```
**Step 2**: Based on the hierarchy JSON, click the desired button
```csharp
using UnityEngine.UI;
// Path identified from hierarchy output
GameObject btnObj = GameObject.Find("Canvas/SettingsPanel/AudioTab/MuteToggle");
if (btnObj == null) return "MuteToggle not found";
Toggle toggle = btnObj.GetComponent<Toggle>();
if (toggle != null)
{
toggle.isOn = !toggle.isOn;
return $"Toggled {btnObj.name} to {toggle.isOn}";
}
return "No Toggle component found";
```
## Execute Action → Screenshot to Verify
Run an action then capture a screenshot to visually confirm the result.
**Step 1**: Perform the action
```csharp
using UnityEngine.UI;
Button btn = GameObject.Find("Canvas/PlayButton")?.GetComponent<Button>();
if (btn == null) return "PlayButton not found";
btn.onClick.Invoke();
return "Clicked PlayButton";
```
**Step 2**: Capture Game View to verify the result
```powershell
uloop screenshot --window-name Game
```
## Execute Action → Check Logs for Side Effects
Run an action then inspect Unity Console logs to verify expected behavior.
**Step 1**: Clear console before the action
```powershell
uloop clear-console
```
**Step 2**: Perform the action
```csharp
using System.Reflection;
GameObject player = GameObject.Find("Player");
if (player == null) return "Player not found";
MonoBehaviour script = player.GetComponent("PlayerController") as MonoBehaviour;
if (script == null) return "PlayerController not found";
MethodInfo method = script.GetType().GetMethod("TakeDamage");
if (method == null) return "TakeDamage method not found";
method.Invoke(script, new object[] { 50f });
return "Invoked TakeDamage(50)";
```
**Step 3**: Check logs for expected output
```powershell
uloop get-logs --log-type Log --search-text 'damage'
```
## Full Automation: Play → Act → Capture → Stop
End-to-end test flow: start Play mode, perform actions, capture evidence, stop.
**Step 0**: Clear console to isolate this run
```powershell
uloop clear-console
```
**Step 1**: Start Play mode
```powershell
uloop control-play-mode --action Play
```
**Step 2**: Wait for scene initialization, then find and click a button
```csharp
using UnityEngine.UI;
using System.Linq;
// Scene may need a moment to initialize after Play starts
Button[] buttons = Object.FindObjectsByType<Button>(FindObjectsSortMode.None);
Button startBtn = buttons.FirstOrDefault(b => b.gameObject.name.Contains("Start"));
if (startBtn == null) return $"Start button not found. Available: {string.Join(", ", buttons.Select(b => b.gameObject.name))}";
startBtn.onClick.Invoke();
return $"Clicked {startBtn.gameObject.name}";
```
**Step 3**: Capture screenshot as evidence
```powershell
uloop screenshot --window-name Game
```
**Step 4**: Check logs for errors
```powershell
uloop get-logs --log-type Error
```
**Step 5**: Stop Play mode
```powershell
uloop control-play-mode --action Stop
```

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 86022f911600881458e40f35e562a5db
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,299 @@
# PlayMode Automation (zsh)
Code examples for runtime automation during Play mode using `execute-dynamic-code`.
These examples manipulate live scene objects while the game is running.
Shell command examples in this file target `zsh`-style usage.
## When to use dedicated mouse simulation tools instead
The examples in this file call UI handlers or runtime methods from C#.
That remains the better choice when you want targeted automation, direct state control, or a quick diagnostic path.
Use dedicated mouse tools only when the input route itself is part of what you need to verify.
Choose the tool based on what you are trying to validate:
| Scenario | Recommended tool | Why |
|----------|------------------|-----|
| Verify that a uGUI element responds through the real EventSystem pointer path | `simulate-mouse-ui` | Fires `PointerDown` / `PointerUp` / `PointerClick` / drag events through EventSystem raycasts instead of bypassing the UI input route. |
| Test gameplay that reads `Mouse.current`, button state, delta, or scroll | `simulate-mouse-input` | Injects Input System mouse state into `Mouse.current`, so game code can observe `wasPressedThisFrame`, movement delta, and scroll like player input. This assumes the project uses the New Input System (`Input System Package (New)` or `Both`). If that is not available in the target project, prefer `execute-dynamic-code` for a project-specific workaround instead of changing project settings just to use this tool. |
| Jump straight to a known button callback, invoke a method, inspect state, or set up a test precondition | `execute-dynamic-code` | Best when you intentionally want direct automation without reproducing the full input pipeline. |
| Drive custom runtime behavior that does not map cleanly to the built-in mouse tools | `execute-dynamic-code` | Lets you call project-specific methods, inspect scene objects, and prototype one-off flows immediately. |
In short: do not default everything to mouse simulation.
Use `execute-dynamic-code` for direct automation and diagnostics, and switch to `simulate-mouse-ui` or `simulate-mouse-input` when reproducing the real input path is the thing you need to test.
## zsh Quoting Notes
Use these patterns when you need shell-safe inline code:
### Double quotes inside C# strings
Single-quote the whole snippet and keep C# string literals unchanged.
```zsh
uloop execute-dynamic-code --code 'return "Hello from zsh";'
```
### Single quotes inside inline C# code
If the C# snippet itself contains a single quote, close and reopen the shell string with `'\''`.
```zsh
uloop execute-dynamic-code --code 'char initial = '\''A'\''; return initial.ToString();'
```
### JSON-like values passed via `--parameters`
Wrap the whole expression in single quotes so the shell does not interpret double quotes.
```zsh
uloop execute-dynamic-code --code 'return parameters["param0"];' --parameters '{"param0":"Hello from zsh"}'
```
## Click UI Button by Path
```csharp
using UnityEngine.UI;
Button btn = GameObject.Find("Canvas/StartButton")?.GetComponent<Button>();
if (btn == null) return "Button not found";
btn.onClick.Invoke();
return $"Clicked {btn.gameObject.name}";
```
## Click UI Button by Search
```csharp
using UnityEngine.UI;
using System.Linq;
Button[] buttons = Object.FindObjectsByType<Button>(FindObjectsSortMode.None);
Button target = buttons.FirstOrDefault(b => b.gameObject.name == "PlayButton");
if (target == null) return $"PlayButton not found. Available: {string.Join(", ", buttons.Select(b => b.gameObject.name))}";
target.onClick.Invoke();
return $"Clicked {target.gameObject.name}";
```
## Toggle GameObject Active State
```csharp
GameObject obj = GameObject.Find("Enemy");
if (obj == null) return "Enemy not found";
obj.SetActive(!obj.activeSelf);
return $"{obj.name} is now {(obj.activeSelf ? "active" : "inactive")}";
```
## Invoke Method on MonoBehaviour
```csharp
using System.Reflection;
GameObject player = GameObject.Find("Player");
if (player == null) return "Player not found";
MonoBehaviour script = player.GetComponent("PlayerController") as MonoBehaviour;
if (script == null) return "PlayerController not found";
MethodInfo method = script.GetType().GetMethod("TakeDamage");
if (method == null) return "TakeDamage method not found";
method.Invoke(script, new object[] { 10f });
return $"Invoked TakeDamage(10) on {player.name}";
```
## Set Field Value at Runtime
```csharp
using System.Reflection;
GameObject player = GameObject.Find("Player");
if (player == null) return "Player not found";
MonoBehaviour script = player.GetComponent("PlayerController") as MonoBehaviour;
if (script == null) return "PlayerController not found";
FieldInfo field = script.GetType().GetField("moveSpeed", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (field == null) return "moveSpeed field not found";
field.SetValue(script, 20f);
return $"Set moveSpeed to 20 on {player.name}";
```
## Move Player to Position
```csharp
GameObject player = GameObject.Find("Player");
if (player == null) return "Player not found";
Vector3 targetPos = new Vector3(5f, 0f, 10f);
player.transform.position = targetPos;
return $"Moved {player.name} to {targetPos}";
```
---
# Tool Combination Workflows
Examples of combining `execute-dynamic-code` with other uloop tools for multi-step PlayMode automation.
## find-game-objects → Click Button
Use `find-game-objects` to discover buttons with their hierarchy paths, then click one via `execute-dynamic-code`.
**Step 1**: Find all GameObjects with Button component
```zsh
uloop find-game-objects --required-components UnityEngine.UI.Button
```
**Step 2**: Click the target button using the path from Step 1
```csharp
using UnityEngine.UI;
// Use the path returned by find-game-objects (e.g. "Canvas/MainMenu/StartButton")
GameObject btnObj = GameObject.Find("Canvas/MainMenu/StartButton");
if (btnObj == null) return "Button not found at path";
Button btn = btnObj.GetComponent<Button>();
if (btn == null) return "No Button component";
btn.onClick.Invoke();
return $"Clicked {btnObj.name}";
```
## get-hierarchy → Navigate UI and Click
Use `get-hierarchy` to explore the UI tree structure, then target the right element.
**Step 1**: Get Canvas hierarchy to understand UI structure
```zsh
uloop get-hierarchy --root-path "Canvas" --max-depth 3
```
**Step 2**: Based on the hierarchy JSON, click the desired button
```csharp
using UnityEngine.UI;
// Path identified from hierarchy output
GameObject btnObj = GameObject.Find("Canvas/SettingsPanel/AudioTab/MuteToggle");
if (btnObj == null) return "MuteToggle not found";
Toggle toggle = btnObj.GetComponent<Toggle>();
if (toggle != null)
{
toggle.isOn = !toggle.isOn;
return $"Toggled {btnObj.name} to {toggle.isOn}";
}
return "No Toggle component found";
```
## Execute Action → Screenshot to Verify
Run an action then capture a screenshot to visually confirm the result.
**Step 1**: Perform the action
```csharp
using UnityEngine.UI;
Button btn = GameObject.Find("Canvas/PlayButton")?.GetComponent<Button>();
if (btn == null) return "PlayButton not found";
btn.onClick.Invoke();
return "Clicked PlayButton";
```
**Step 2**: Capture Game View to verify the result
```zsh
uloop screenshot --window-name Game
```
## Execute Action → Check Logs for Side Effects
Run an action then inspect Unity Console logs to verify expected behavior.
**Step 1**: Clear console before the action
```zsh
uloop clear-console
```
**Step 2**: Perform the action
```csharp
using System.Reflection;
GameObject player = GameObject.Find("Player");
if (player == null) return "Player not found";
MonoBehaviour script = player.GetComponent("PlayerController") as MonoBehaviour;
if (script == null) return "PlayerController not found";
MethodInfo method = script.GetType().GetMethod("TakeDamage");
if (method == null) return "TakeDamage method not found";
method.Invoke(script, new object[] { 50f });
return "Invoked TakeDamage(50)";
```
**Step 3**: Check logs for expected output
```zsh
uloop get-logs --log-type Log --search-text "damage"
```
## Full Automation: Play → Act → Capture → Stop
End-to-end test flow: start Play mode, perform actions, capture evidence, stop.
**Step 0**: Clear console to isolate this run
```zsh
uloop clear-console
```
**Step 1**: Start Play mode
```zsh
uloop control-play-mode --action Play
```
**Step 2**: Wait for scene initialization, then find and click a button
```csharp
using UnityEngine.UI;
using System.Linq;
// Scene may need a moment to initialize after Play starts
Button[] buttons = Object.FindObjectsByType<Button>(FindObjectsSortMode.None);
Button startBtn = buttons.FirstOrDefault(b => b.gameObject.name.Contains("Start"));
if (startBtn == null) return $"Start button not found. Available: {string.Join(", ", buttons.Select(b => b.gameObject.name))}";
startBtn.onClick.Invoke();
return $"Clicked {startBtn.gameObject.name}";
```
**Step 3**: Capture screenshot as evidence
```zsh
uloop screenshot --window-name Game
```
**Step 4**: Check logs for errors
```zsh
uloop get-logs --log-type Error
```
**Step 5**: Stop Play mode
```zsh
uloop control-play-mode --action Stop
```

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 0d40893521e758a4997b2352987ab719
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,151 @@
# PlayMode Inspection
Code examples for inspecting and verifying game state at runtime using `execute-dynamic-code`.
These examples read or check runtime state for debugging and automated testing.
## Get Active Scene Info
```csharp
using UnityEngine.SceneManagement;
Scene scene = SceneManager.GetActiveScene();
return $"Scene: {scene.name}, path: {scene.path}, buildIndex: {scene.buildIndex}, isLoaded: {scene.isLoaded}, rootCount: {scene.rootCount}";
```
## Check Loaded Scenes
```csharp
using UnityEngine.SceneManagement;
using System.Text;
int count = SceneManager.sceneCount;
StringBuilder sb = new StringBuilder();
sb.AppendLine($"Loaded scenes ({count}):");
for (int i = 0; i < count; i++)
{
Scene scene = SceneManager.GetSceneAt(i);
sb.AppendLine($" [{i}] {scene.name} (buildIndex={scene.buildIndex}, isLoaded={scene.isLoaded})");
}
return sb.ToString();
```
## Read Component Field via Reflection
```csharp
using System.Reflection;
GameObject target = GameObject.Find("Player");
if (target == null) return "Player not found";
MonoBehaviour script = target.GetComponent("PlayerController") as MonoBehaviour;
if (script == null) return "PlayerController not found";
FieldInfo hpField = script.GetType().GetField("hp", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (hpField == null) return "hp field not found";
object value = hpField.GetValue(script);
return $"Player hp = {value}";
```
## Read Multiple Fields via Reflection
```csharp
using System.Reflection;
using System.Text;
GameObject target = GameObject.Find("Player");
if (target == null) return "Player not found";
MonoBehaviour script = target.GetComponent("PlayerController") as MonoBehaviour;
if (script == null) return "PlayerController not found";
StringBuilder sb = new StringBuilder();
sb.AppendLine($"Fields on {script.GetType().Name}:");
FieldInfo[] fields = script.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (FieldInfo field in fields)
{
if (field.DeclaringType == script.GetType())
{
object value = field.GetValue(script);
sb.AppendLine($" {field.Name} ({field.FieldType.Name}) = {value}");
}
}
return sb.ToString();
```
## Read Property via Reflection
```csharp
using System.Reflection;
GameObject target = GameObject.Find("GameManager");
if (target == null) return "GameManager not found";
MonoBehaviour script = target.GetComponent("GameManager") as MonoBehaviour;
if (script == null) return "GameManager component not found";
PropertyInfo prop = script.GetType().GetProperty("Score", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (prop == null) return "Score property not found";
object value = prop.GetValue(script);
return $"Score = {value}";
```
## Check If GameObject Exists (Spawned/Destroyed)
```csharp
using System.Linq;
string targetName = "Enemy";
GameObject[] all = Object.FindObjectsByType<GameObject>(FindObjectsInactive.Include, FindObjectsSortMode.None);
GameObject[] matches = all.Where(g => g.name.Contains(targetName)).ToArray();
if (matches.Length == 0) return $"No GameObjects containing '{targetName}' found";
return $"Found {matches.Length} objects matching '{targetName}': {string.Join(", ", matches.Select(g => g.name))}";
```
## Verify Object Position/Rotation
```csharp
GameObject target = GameObject.Find("Player");
if (target == null) return "Player not found";
Transform t = target.transform;
return $"{target.name}: pos={t.position}, rot={t.rotation.eulerAngles}, scale={t.localScale}";
```
## Check Rigidbody State
```csharp
Rigidbody rb = GameObject.Find("TargetCube")?.GetComponent<Rigidbody>();
if (rb == null) return "Rigidbody not found on TargetCube";
return $"velocity={rb.velocity}, angularVelocity={rb.angularVelocity}, isKinematic={rb.isKinematic}, useGravity={rb.useGravity}, isSleeping={rb.IsSleeping()}";
```
## Raycast from Camera Center
```csharp
Camera cam = Camera.main;
if (cam == null) return "Main camera not found";
Ray ray = cam.ScreenPointToRay(new Vector3(Screen.width / 2f, Screen.height / 2f, 0));
if (Physics.Raycast(ray, out RaycastHit hit, 100f))
{
return $"Hit: {hit.collider.gameObject.name} at {hit.point}";
}
return "No hit";
```
## Find GameObjects by Tag
```csharp
using System.Linq;
string tag = "Enemy";
GameObject[] enemies = GameObject.FindGameObjectsWithTag(tag);
return $"Found {enemies.Length} objects with tag '{tag}': {string.Join(", ", enemies.Select(e => $"{e.name} at {e.transform.position}"))}";
```

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 54bdd16e7f97c42198b472ac9bc4aa14
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,190 @@
# PlayMode UI Controls
Code examples for manipulating UI controls at runtime using `execute-dynamic-code`.
These examples interact with Unity UI components while the game is running.
## Set InputField Text
```csharp
using UnityEngine.UI;
InputField input = GameObject.Find("Canvas/NameInput")?.GetComponent<InputField>();
if (input == null) return "InputField not found";
input.text = "Player1";
input.onEndEdit.Invoke(input.text);
return $"Set InputField text to: {input.text}";
```
## Set Slider Value
```csharp
using UnityEngine.UI;
Slider slider = GameObject.Find("Canvas/VolumeSlider")?.GetComponent<Slider>();
if (slider == null) return "Slider not found";
float oldValue = slider.value;
slider.value = 0.75f;
return $"Slider changed from {oldValue} to {slider.value}";
```
## Set Slider Value with Bounds Check
```csharp
using UnityEngine.UI;
using System.Linq;
Slider[] sliders = Object.FindObjectsByType<Slider>(FindObjectsSortMode.None);
Slider target = sliders.FirstOrDefault(s => s.gameObject.name == "HealthBar");
if (target == null) return $"HealthBar not found. Available: {string.Join(", ", sliders.Select(s => s.gameObject.name))}";
float targetValue = 0.5f;
float clamped = Mathf.Clamp(targetValue, target.minValue, target.maxValue);
target.value = clamped;
return $"Set {target.gameObject.name} to {clamped} (range: {target.minValue}-{target.maxValue})";
```
## Toggle On/Off
```csharp
using UnityEngine.UI;
Toggle toggle = GameObject.Find("Canvas/MuteToggle")?.GetComponent<Toggle>();
if (toggle == null) return "Toggle not found";
bool oldValue = toggle.isOn;
toggle.isOn = !toggle.isOn;
return $"Toggle changed from {oldValue} to {toggle.isOn}";
```
## Set Toggle to Specific Value
```csharp
using UnityEngine.UI;
using System.Linq;
Toggle[] toggles = Object.FindObjectsByType<Toggle>(FindObjectsSortMode.None);
Toggle target = toggles.FirstOrDefault(t => t.gameObject.name == "SoundToggle");
if (target == null) return $"SoundToggle not found. Available: {string.Join(", ", toggles.Select(t => t.gameObject.name))}";
target.isOn = true;
return $"Set {target.gameObject.name} to {target.isOn}";
```
## Select Dropdown Item by Index
```csharp
using UnityEngine.UI;
Dropdown dropdown = GameObject.Find("Canvas/DifficultyDropdown")?.GetComponent<Dropdown>();
if (dropdown == null) return "Dropdown not found";
if (dropdown.options.Count == 0) return "Dropdown has no options";
int targetIndex = 2;
if (targetIndex >= dropdown.options.Count)
return $"Index {targetIndex} out of range. Options count: {dropdown.options.Count}";
dropdown.value = targetIndex;
return $"Selected: {dropdown.options[targetIndex].text} (index {targetIndex})";
```
## Select Dropdown Item by Text
```csharp
using UnityEngine.UI;
using System.Linq;
Dropdown dropdown = GameObject.Find("Canvas/DifficultyDropdown")?.GetComponent<Dropdown>();
if (dropdown == null) return "Dropdown not found";
string targetText = "Hard";
int index = dropdown.options.FindIndex(o => o.text == targetText);
if (index < 0)
return $"'{targetText}' not found. Available: {string.Join(", ", dropdown.options.Select(o => o.text))}";
dropdown.value = index;
return $"Selected: {targetText} (index {index})";
```
## List All Dropdown Options
```csharp
using UnityEngine.UI;
using System.Text;
Dropdown dropdown = GameObject.Find("Canvas/DifficultyDropdown")?.GetComponent<Dropdown>();
if (dropdown == null) return "Dropdown not found";
StringBuilder sb = new StringBuilder();
sb.AppendLine($"Dropdown '{dropdown.gameObject.name}' - current: {dropdown.value}");
for (int i = 0; i < dropdown.options.Count; i++)
{
string marker = i == dropdown.value ? " [selected]" : "";
sb.AppendLine($" [{i}] {dropdown.options[i].text}{marker}");
}
return sb.ToString();
```
## Simulate Drag on UI Element
```csharp
using UnityEngine.EventSystems;
using System.Collections.Generic;
GameObject target = GameObject.Find("Canvas/DraggableItem");
if (target == null) return "DraggableItem not found";
PointerEventData pointerData = new PointerEventData(EventSystem.current)
{
position = new Vector2(Screen.width / 2f, Screen.height / 2f)
};
// Verify target handles drag events
if (!ExecuteEvents.CanHandleEvent<IDragHandler>(target))
return $"{target.name} does not handle drag events";
// Begin drag
ExecuteEvents.Execute(target, pointerData, ExecuteEvents.beginDragHandler);
// Move (simulate drag to a new position)
pointerData.position = new Vector2(Screen.width / 2f + 100f, Screen.height / 2f);
ExecuteEvents.Execute(target, pointerData, ExecuteEvents.dragHandler);
// End drag
ExecuteEvents.Execute(target, pointerData, ExecuteEvents.endDragHandler);
return $"Dragged {target.name} 100px to the right";
```
## List All UI Controls in Scene
```csharp
using UnityEngine.UI;
using System.Text;
StringBuilder sb = new StringBuilder();
Slider[] sliders = Object.FindObjectsByType<Slider>(FindObjectsSortMode.None);
Toggle[] toggles = Object.FindObjectsByType<Toggle>(FindObjectsSortMode.None);
Dropdown[] dropdowns = Object.FindObjectsByType<Dropdown>(FindObjectsSortMode.None);
InputField[] inputs = Object.FindObjectsByType<InputField>(FindObjectsSortMode.None);
ScrollRect[] scrolls = Object.FindObjectsByType<ScrollRect>(FindObjectsSortMode.None);
sb.AppendLine($"Sliders ({sliders.Length}):");
foreach (Slider s in sliders) sb.AppendLine($" {s.gameObject.name} = {s.value} ({s.minValue}-{s.maxValue})");
sb.AppendLine($"Toggles ({toggles.Length}):");
foreach (Toggle t in toggles) sb.AppendLine($" {t.gameObject.name} = {t.isOn}");
sb.AppendLine($"Dropdowns ({dropdowns.Length}):");
foreach (Dropdown d in dropdowns) sb.AppendLine($" {d.gameObject.name} = {(d.options.Count > 0 ? d.options[d.value].text : "(empty)")} (index {d.value})");
sb.AppendLine($"InputFields ({inputs.Length}):");
foreach (InputField i in inputs) sb.AppendLine($" {i.gameObject.name} = \"{i.text}\"");
sb.AppendLine($"ScrollRects ({scrolls.Length}):");
foreach (ScrollRect sr in scrolls) sb.AppendLine($" {sr.gameObject.name} pos={sr.normalizedPosition}");
return sb.ToString();
```

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 84096b299132946e9ae4bba41cd210b0
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,221 @@
# Prefab Operations
Code examples for Prefab operations using `execute-dynamic-code`.
## Create a Prefab from GameObject
```csharp
using UnityEditor;
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
cube.name = "MyCube";
string path = "Assets/Prefabs/MyCube.prefab";
PrefabUtility.SaveAsPrefabAsset(cube, path);
Object.DestroyImmediate(cube);
return $"Prefab created at {path}";
```
## Instantiate a Prefab
```csharp
using UnityEditor;
string prefabPath = "Assets/Prefabs/MyCube.prefab";
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
if (prefab == null)
{
return $"Prefab not found at {prefabPath}";
}
GameObject instance = (GameObject)PrefabUtility.InstantiatePrefab(prefab);
instance.transform.position = new Vector3(0, 1, 0);
return $"Instantiated {instance.name}";
```
## Add Component to Prefab
```csharp
using UnityEditor;
string prefabPath = "Assets/Prefabs/MyCube.prefab";
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
if (prefab == null)
{
return $"Prefab not found at {prefabPath}";
}
string assetPath = AssetDatabase.GetAssetPath(prefab);
using (PrefabUtility.EditPrefabContentsScope scope = new PrefabUtility.EditPrefabContentsScope(assetPath))
{
GameObject root = scope.prefabContentsRoot;
if (root.GetComponent<Rigidbody>() == null)
{
root.AddComponent<Rigidbody>();
}
}
return "Added Rigidbody to prefab";
```
## Modify Prefab Properties
```csharp
using UnityEditor;
string prefabPath = "Assets/Prefabs/MyCube.prefab";
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
if (prefab == null)
{
return $"Prefab not found at {prefabPath}";
}
using (PrefabUtility.EditPrefabContentsScope scope = new PrefabUtility.EditPrefabContentsScope(prefabPath))
{
GameObject root = scope.prefabContentsRoot;
root.transform.localScale = new Vector3(2, 2, 2);
MeshRenderer renderer = root.GetComponent<MeshRenderer>();
if (renderer != null)
{
renderer.receiveShadows = false;
}
}
return "Modified prefab properties";
```
## Wire SerializedField Reference in Prefab
```csharp
using UnityEditor;
string prefabPath = "Assets/Prefabs/Player.prefab";
string weaponPrefabPath = "Assets/Prefabs/Weapon.prefab";
GameObject weaponPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(weaponPrefabPath);
if (weaponPrefab == null)
{
return $"Asset not found at {weaponPrefabPath}";
}
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
if (prefab == null)
{
return $"Prefab not found at {prefabPath}";
}
using (PrefabUtility.EditPrefabContentsScope scope = new PrefabUtility.EditPrefabContentsScope(prefabPath))
{
GameObject root = scope.prefabContentsRoot;
MonoBehaviour script = root.GetComponent("PlayerController") as MonoBehaviour;
if (script == null)
{
return "PlayerController not found on prefab root";
}
SerializedObject so = new SerializedObject(script);
SerializedProperty prop = so.FindProperty("weaponPrefab");
if (prop == null)
{
return "Property 'weaponPrefab' not found";
}
prop.objectReferenceValue = weaponPrefab;
so.ApplyModifiedProperties();
}
return "Wired weaponPrefab reference in Player prefab";
```
## Wire Multiple References in Prefab
```csharp
using UnityEditor;
string prefabPath = "Assets/Prefabs/Enemy.prefab";
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
if (prefab == null)
{
return $"Prefab not found at {prefabPath}";
}
int wiredCount = 0;
using (PrefabUtility.EditPrefabContentsScope scope = new PrefabUtility.EditPrefabContentsScope(prefabPath))
{
GameObject root = scope.prefabContentsRoot;
MonoBehaviour script = root.GetComponent("EnemyController") as MonoBehaviour;
if (script == null)
{
return "EnemyController not found";
}
SerializedObject so = new SerializedObject(script);
SerializedProperty matProp = so.FindProperty("bodyMaterial");
Material mat = AssetDatabase.LoadAssetAtPath<Material>("Assets/Materials/EnemyBody.mat");
if (matProp != null && mat != null)
{
matProp.objectReferenceValue = mat;
wiredCount++;
}
SerializedProperty clipProp = so.FindProperty("deathSound");
AudioClip clip = AssetDatabase.LoadAssetAtPath<AudioClip>("Assets/Audio/Death.wav");
if (clipProp != null && clip != null)
{
clipProp.objectReferenceValue = clip;
wiredCount++;
}
so.ApplyModifiedProperties();
}
return $"Wired {wiredCount}/2 references in Enemy prefab";
```
## Find All Prefab Instances in Scene
```csharp
using UnityEditor;
using System.Collections.Generic;
string prefabPath = "Assets/Prefabs/MyCube.prefab";
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
if (prefab == null)
{
return $"Prefab not found at {prefabPath}";
}
List<GameObject> instances = new List<GameObject>();
foreach (GameObject obj in Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None))
{
if (PrefabUtility.GetCorrespondingObjectFromSource(obj) == prefab)
{
instances.Add(obj);
}
}
return $"Found {instances.Count} instances of {prefab.name}";
```
## Apply Prefab Overrides
```csharp
using UnityEditor;
GameObject selected = Selection.activeGameObject;
if (selected == null)
{
return "No GameObject selected";
}
if (!PrefabUtility.IsPartOfPrefabInstance(selected))
{
return "Selected object is not a prefab instance";
}
GameObject instanceRoot = PrefabUtility.GetOutermostPrefabInstanceRoot(selected);
if (instanceRoot == null)
{
return "Could not resolve prefab instance root";
}
PrefabUtility.ApplyPrefabInstance(instanceRoot, InteractionMode.UserAction);
return $"Applied overrides from {instanceRoot.name} to prefab";
```

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: aaaab94d718944441974c7a6da7c9e19
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,234 @@
# Scene Operations
Code examples for Scene and Hierarchy operations using `execute-dynamic-code`.
## Create GameObject
```csharp
GameObject obj = new GameObject("MyObject");
obj.transform.position = new Vector3(0, 1, 0);
return $"Created {obj.name}";
```
## Create UI GameObject (under Canvas)
```csharp
using UnityEngine.UI;
// UI objects require RectTransform, which is auto-added when parented to Canvas
GameObject canvas = GameObject.Find("Canvas");
if (canvas == null)
{
return "Canvas not found in scene";
}
GameObject uiObj = new GameObject("MyUIElement");
uiObj.transform.SetParent(canvas.transform, false);
uiObj.AddComponent<RectTransform>();
uiObj.AddComponent<Image>();
return $"Created UI element: {uiObj.name}";
```
## Create Primitive
```csharp
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
cube.name = "MyCube";
cube.transform.position = new Vector3(2, 0, 0);
return $"Created {cube.name}";
```
## Add Component to GameObject
```csharp
GameObject selected = Selection.activeGameObject;
if (selected == null)
{
return "No GameObject selected";
}
Rigidbody rb = selected.AddComponent<Rigidbody>();
rb.mass = 2f;
rb.useGravity = true;
return $"Added Rigidbody to {selected.name}";
```
## Find GameObject by Name
```csharp
GameObject obj = GameObject.Find("Player");
if (obj == null)
{
return "GameObject 'Player' not found";
}
return $"Found: {obj.name} at {obj.transform.position}";
```
## Find GameObjects by Tag
```csharp
GameObject[] enemies = GameObject.FindGameObjectsWithTag("Enemy");
return $"Found {enemies.Length} GameObjects with tag 'Enemy'";
```
## Set Parent
```csharp
GameObject child = GameObject.Find("Child");
GameObject parent = GameObject.Find("Parent");
if (child == null || parent == null)
{
return "Child or Parent not found";
}
child.transform.SetParent(parent.transform);
return $"Set {child.name}'s parent to {parent.name}";
```
## Get All Children
```csharp
GameObject parent = Selection.activeGameObject;
if (parent == null)
{
return "No GameObject selected";
}
List<string> children = new List<string>();
foreach (Transform child in parent.transform)
{
children.Add(child.name);
}
return $"Children: {string.Join(", ", children)}";
```
## Wire Component References
```csharp
using UnityEditor;
GameObject player = GameObject.Find("Player");
GameObject target = GameObject.Find("Target");
if (player == null || target == null)
{
return "Player or Target not found";
}
MonoBehaviour script = player.GetComponent("PlayerController") as MonoBehaviour;
if (script == null)
{
return "PlayerController not found on Player";
}
SerializedObject serializedScript = new SerializedObject(script);
SerializedProperty targetProp = serializedScript.FindProperty("target");
if (targetProp != null)
{
targetProp.objectReferenceValue = target.transform;
serializedScript.ApplyModifiedProperties();
return "Target reference wired";
}
return "Property 'target' not found";
```
## Load Scene (Editor)
```csharp
using UnityEditor.SceneManagement;
string scenePath = "Assets/Scenes/MainMenu.unity";
EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Single);
return $"Loaded scene: {scenePath}";
```
## Save Current Scene
```csharp
using UnityEditor.SceneManagement;
UnityEngine.SceneManagement.Scene scene = EditorSceneManager.GetActiveScene();
EditorSceneManager.SaveScene(scene);
return $"Saved scene: {scene.name}";
```
## Create New Scene
```csharp
using UnityEditor.SceneManagement;
UnityEngine.SceneManagement.Scene newScene = EditorSceneManager.NewScene(NewSceneSetup.DefaultGameObjects, NewSceneMode.Single);
return $"Created new scene: {newScene.name}";
```
## Get All Root GameObjects in Scene
```csharp
UnityEngine.SceneManagement.Scene scene = UnityEngine.SceneManagement.SceneManager.GetActiveScene();
GameObject[] roots = scene.GetRootGameObjects();
List<string> names = new List<string>();
foreach (GameObject root in roots)
{
names.Add(root.name);
}
return $"Root objects: {string.Join(", ", names)}";
```
## Destroy GameObject
```csharp
GameObject obj = GameObject.Find("OldObject");
if (obj == null)
{
return "GameObject not found";
}
Object.DestroyImmediate(obj);
return "GameObject destroyed";
```
## Duplicate GameObject
```csharp
GameObject selected = Selection.activeGameObject;
if (selected == null)
{
return "No GameObject selected";
}
GameObject copy = Object.Instantiate(selected);
copy.name = selected.name + "_Copy";
copy.transform.position = selected.transform.position + Vector3.right * 2;
return $"Created duplicate: {copy.name}";
```
## Set Active/Inactive
```csharp
GameObject obj = GameObject.Find("MyObject");
if (obj == null)
{
return "GameObject not found";
}
obj.SetActive(!obj.activeSelf);
return $"{obj.name} is now {(obj.activeSelf ? "active" : "inactive")}";
```
## Modify Transform
```csharp
GameObject selected = Selection.activeGameObject;
if (selected == null)
{
return "No GameObject selected";
}
selected.transform.position = new Vector3(0, 5, 0);
selected.transform.rotation = Quaternion.Euler(0, 45, 0);
selected.transform.localScale = new Vector3(2, 2, 2);
return "Transform modified";
```

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 2092122bf65684e53a6d650bd4a61327
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,216 @@
# ScriptableObject Operations
Code examples for ScriptableObject operations using `execute-dynamic-code`.
## Create ScriptableObject Instance
```csharp
using UnityEditor;
ScriptableObject so = ScriptableObject.CreateInstance<ScriptableObject>();
string path = "Assets/Data/MyData.asset";
AssetDatabase.CreateAsset(so, path);
AssetDatabase.SaveAssets();
return $"ScriptableObject created at {path}";
```
## Create Custom ScriptableObject
```csharp
using UnityEditor;
ScriptableObject so = ScriptableObject.CreateInstance("MyCustomSO");
if (so == null)
{
return "Type 'MyCustomSO' not found. Ensure the class exists.";
}
string path = "Assets/Data/MyCustomData.asset";
AssetDatabase.CreateAsset(so, path);
AssetDatabase.SaveAssets();
return $"Created {so.GetType().Name} at {path}";
```
## Modify ScriptableObject with SerializedObject
```csharp
using UnityEditor;
string path = "Assets/Data/MyData.asset";
ScriptableObject so = AssetDatabase.LoadAssetAtPath<ScriptableObject>(path);
if (so == null)
{
return $"Asset not found at {path}";
}
SerializedObject serializedObj = new SerializedObject(so);
SerializedProperty prop = serializedObj.FindProperty("myField");
if (prop != null)
{
prop.stringValue = "New Value";
serializedObj.ApplyModifiedProperties();
EditorUtility.SetDirty(so);
AssetDatabase.SaveAssets();
return "Property updated";
}
return "Property 'myField' not found";
```
## Set Int/Float/Bool Properties
```csharp
using UnityEditor;
string path = "Assets/Data/GameSettings.asset";
ScriptableObject so = AssetDatabase.LoadAssetAtPath<ScriptableObject>(path);
if (so == null)
{
return $"Asset not found at {path}";
}
SerializedObject serializedObj = new SerializedObject(so);
SerializedProperty intProp = serializedObj.FindProperty("maxHealth");
if (intProp != null) intProp.intValue = 100;
SerializedProperty floatProp = serializedObj.FindProperty("moveSpeed");
if (floatProp != null) floatProp.floatValue = 5.5f;
SerializedProperty boolProp = serializedObj.FindProperty("isEnabled");
if (boolProp != null) boolProp.boolValue = true;
serializedObj.ApplyModifiedProperties();
EditorUtility.SetDirty(so);
AssetDatabase.SaveAssets();
return "Properties updated";
```
## Set Reference Properties
```csharp
using UnityEditor;
string soPath = "Assets/Data/CharacterData.asset";
string prefabPath = "Assets/Prefabs/Player.prefab";
ScriptableObject so = AssetDatabase.LoadAssetAtPath<ScriptableObject>(soPath);
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
if (so == null)
{
return $"ScriptableObject not found at {soPath}";
}
if (prefab == null)
{
return $"Prefab not found at {prefabPath}";
}
SerializedObject serializedObj = new SerializedObject(so);
SerializedProperty prop = serializedObj.FindProperty("playerPrefab");
if (prop != null)
{
prop.objectReferenceValue = prefab;
serializedObj.ApplyModifiedProperties();
EditorUtility.SetDirty(so);
AssetDatabase.SaveAssets();
return "Reference set successfully";
}
return "Property not found";
```
## Set Array/List Properties
```csharp
using UnityEditor;
string path = "Assets/Data/ItemDatabase.asset";
ScriptableObject so = AssetDatabase.LoadAssetAtPath<ScriptableObject>(path);
if (so == null)
{
return $"Asset not found at {path}";
}
SerializedObject serializedObj = new SerializedObject(so);
SerializedProperty arrayProp = serializedObj.FindProperty("items");
if (arrayProp != null && arrayProp.isArray)
{
arrayProp.ClearArray();
arrayProp.InsertArrayElementAtIndex(0);
arrayProp.GetArrayElementAtIndex(0).stringValue = "Sword";
arrayProp.InsertArrayElementAtIndex(1);
arrayProp.GetArrayElementAtIndex(1).stringValue = "Shield";
serializedObj.ApplyModifiedProperties();
EditorUtility.SetDirty(so);
AssetDatabase.SaveAssets();
return "Array updated with 2 items";
}
return "Array property not found";
```
## Find All ScriptableObjects of Type
```csharp
using UnityEditor;
using System.Collections.Generic;
string typeName = "GameSettings";
string[] guids = AssetDatabase.FindAssets($"t:{typeName}");
List<string> paths = new List<string>();
foreach (string guid in guids)
{
paths.Add(AssetDatabase.GUIDToAssetPath(guid));
}
return $"Found {paths.Count} {typeName} assets";
```
## Duplicate ScriptableObject
```csharp
using UnityEditor;
string sourcePath = "Assets/Data/Template.asset";
string destPath = "Assets/Data/NewInstance.asset";
ScriptableObject source = AssetDatabase.LoadAssetAtPath<ScriptableObject>(sourcePath);
if (source == null)
{
return $"Source asset not found at {sourcePath}";
}
ScriptableObject copy = Object.Instantiate(source);
AssetDatabase.CreateAsset(copy, destPath);
AssetDatabase.SaveAssets();
return $"Duplicated to {destPath}";
```
## List All Properties of ScriptableObject
```csharp
using UnityEditor;
using System.Collections.Generic;
string path = "Assets/Data/MyData.asset";
ScriptableObject so = AssetDatabase.LoadAssetAtPath<ScriptableObject>(path);
if (so == null)
{
return $"Asset not found at {path}";
}
SerializedObject serializedObj = new SerializedObject(so);
SerializedProperty prop = serializedObj.GetIterator();
List<string> properties = new List<string>();
while (prop.NextVisible(true))
{
properties.Add($"{prop.name} ({prop.propertyType})");
}
return string.Join(", ", properties);
```

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: e93ff8749746b44fd8e6a12a6ba31eb7
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,302 @@
# Selection Operations
Code examples for Selection operations using `execute-dynamic-code`.
## Get Selected GameObjects
```csharp
using UnityEditor;
using System.Collections.Generic;
GameObject[] selected = Selection.gameObjects;
if (selected.Length == 0)
{
return "No GameObjects selected";
}
List<string> names = new List<string>();
foreach (GameObject obj in selected)
{
names.Add(obj.name);
}
return $"Selected: {string.Join(", ", names)}";
```
## Get Active (Last Selected) GameObject
```csharp
using UnityEditor;
GameObject active = Selection.activeGameObject;
if (active == null)
{
return "No active GameObject";
}
return $"Active: {active.name}";
```
## Set Selection Programmatically
```csharp
using UnityEditor;
GameObject obj = GameObject.Find("Player");
if (obj == null)
{
return "GameObject 'Player' not found";
}
Selection.activeGameObject = obj;
return $"Selected {obj.name}";
```
## Select Multiple GameObjects
```csharp
using UnityEditor;
GameObject[] enemies = GameObject.FindGameObjectsWithTag("Enemy");
if (enemies.Length == 0)
{
return "No enemies found";
}
Selection.objects = enemies;
return $"Selected {enemies.Length} enemies";
```
## Get Top-Level Transforms Only
```csharp
using UnityEditor;
using System.Collections.Generic;
Transform[] transforms = Selection.GetTransforms(SelectionMode.TopLevel);
if (transforms.Length == 0)
{
return "No transforms selected";
}
List<string> names = new List<string>();
foreach (Transform t in transforms)
{
names.Add(t.name);
}
return $"Top-level: {string.Join(", ", names)}";
```
## Get Deep Selection (Including Children)
```csharp
using UnityEditor;
Transform[] transforms = Selection.GetTransforms(SelectionMode.Deep);
if (transforms.Length == 0)
{
return "No transforms selected";
}
return $"Deep selection count: {transforms.Length}";
```
## Get Editable Objects Only
```csharp
using UnityEditor;
using System.Collections.Generic;
Transform[] transforms = Selection.GetTransforms(SelectionMode.Editable);
if (transforms.Length == 0)
{
return "No editable transforms selected";
}
List<string> names = new List<string>();
foreach (Transform t in transforms)
{
names.Add(t.name);
}
return $"Editable: {string.Join(", ", names)}";
```
## Get Selected Assets
```csharp
using UnityEditor;
using System.Collections.Generic;
Object[] selectedAssets = Selection.GetFiltered<Object>(SelectionMode.Assets);
if (selectedAssets.Length == 0)
{
return "No assets selected";
}
List<string> paths = new List<string>();
foreach (Object asset in selectedAssets)
{
paths.Add(AssetDatabase.GetAssetPath(asset));
}
return $"Assets: {string.Join(", ", paths)}";
```
## Get Selected Asset GUIDs
```csharp
using UnityEditor;
using System.Collections.Generic;
string[] guids = Selection.assetGUIDs;
if (guids.Length == 0)
{
return "No assets selected";
}
List<string> paths = new List<string>();
foreach (string guid in guids)
{
paths.Add(AssetDatabase.GUIDToAssetPath(guid));
}
return $"Selected assets: {string.Join(", ", paths)}";
```
## Select All Children of Selected Object
```csharp
using UnityEditor;
using UnityEngine;
using System.Collections.Generic;
GameObject parent = Selection.activeGameObject;
if (parent == null)
{
return "No GameObject selected";
}
List<GameObject> children = new List<GameObject>();
foreach (Transform child in parent.GetComponentsInChildren<Transform>())
{
if (child != parent.transform)
{
children.Add(child.gameObject);
}
}
if (children.Count == 0)
{
return "No children found";
}
Selection.objects = children.ToArray();
return $"Selected {children.Count} children";
```
## Filter Selection by Component
```csharp
using UnityEditor;
using System.Collections.Generic;
GameObject[] selected = Selection.gameObjects;
List<GameObject> withRigidbody = new List<GameObject>();
foreach (GameObject obj in selected)
{
if (obj.GetComponent<Rigidbody>() != null)
{
withRigidbody.Add(obj);
}
}
if (withRigidbody.Count == 0)
{
return "No objects with Rigidbody in selection";
}
Selection.objects = withRigidbody.ToArray();
return $"Filtered to {withRigidbody.Count} objects with Rigidbody";
```
## Check if Object is Selected
```csharp
using UnityEditor;
GameObject player = GameObject.Find("Player");
if (player == null)
{
return "Player not found";
}
bool isSelected = Selection.Contains(player);
return $"Player is {(isSelected ? "" : "not ")}selected";
```
## Clear Selection
```csharp
using UnityEditor;
Selection.activeObject = null;
return "Selection cleared";
```
## Select Objects by Layer
```csharp
using UnityEditor;
using System.Collections.Generic;
int layer = LayerMask.NameToLayer("UI");
if (layer == -1)
{
return "Layer 'UI' not found";
}
GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
List<GameObject> layerObjects = new List<GameObject>();
foreach (GameObject obj in allObjects)
{
if (obj.layer == layer)
{
layerObjects.Add(obj);
}
}
if (layerObjects.Count == 0)
{
return "No objects found on UI layer";
}
Selection.objects = layerObjects.ToArray();
return $"Selected {layerObjects.Count} objects on UI layer";
```
## Ping Object in Hierarchy/Project
```csharp
using UnityEditor;
GameObject obj = GameObject.Find("Player");
if (obj == null)
{
return "Player not found";
}
EditorGUIUtility.PingObject(obj);
return $"Pinged {obj.name} in Hierarchy";
```
## Focus on Selected Object in Scene View
```csharp
using UnityEditor;
if (Selection.activeGameObject == null)
{
return "No GameObject selected";
}
SceneView.FrameLastActiveSceneView();
return "Focused on selected object";
```

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 8b3773a6fd47d4211b5a13faa4ef3b7c
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,211 @@
# Undo Operations
Code examples for Undo-supported operations using `execute-dynamic-code`.
## Record Property Change (Undo.RecordObject)
```csharp
using UnityEditor;
GameObject selected = Selection.activeGameObject;
if (selected == null)
{
return "No GameObject selected";
}
Undo.RecordObject(selected.transform, "Move Object");
selected.transform.position = new Vector3(0, 5, 0);
return $"Moved {selected.name} (Undo available)";
```
## Record Multiple Objects
```csharp
using UnityEditor;
GameObject[] selectedObjects = Selection.gameObjects;
if (selectedObjects.Length == 0)
{
return "No GameObjects selected";
}
Object[] transforms = new Object[selectedObjects.Length];
for (int i = 0; i < selectedObjects.Length; i++)
{
transforms[i] = selectedObjects[i].transform;
}
Undo.RecordObjects(transforms, "Move Multiple Objects");
foreach (GameObject obj in selectedObjects)
{
obj.transform.position += Vector3.up * 2;
}
return $"Moved {selectedObjects.Length} objects (Undo available)";
```
## Complete Object Undo (For Complex Changes)
```csharp
using UnityEditor;
GameObject selected = Selection.activeGameObject;
if (selected == null)
{
return "No GameObject selected";
}
Undo.RegisterCompleteObjectUndo(selected, "Complete Object Change");
selected.name = "RenamedObject";
selected.layer = LayerMask.NameToLayer("Default");
selected.tag = "Untagged";
return $"Modified object completely (Undo available)";
```
## Add Component with Undo
```csharp
using UnityEditor;
GameObject selected = Selection.activeGameObject;
if (selected == null)
{
return "No GameObject selected";
}
Rigidbody rb = Undo.AddComponent<Rigidbody>(selected);
rb.mass = 2f;
rb.useGravity = true;
return $"Added Rigidbody to {selected.name} (Undo available)";
```
## Set Parent with Undo
```csharp
using UnityEditor;
GameObject child = GameObject.Find("Child");
GameObject parent = GameObject.Find("Parent");
if (child == null || parent == null)
{
return "Child or Parent not found";
}
Undo.SetTransformParent(child.transform, parent.transform, "Set Parent");
return $"Set {child.name}'s parent to {parent.name} (Undo available)";
```
## Create GameObject with Undo
```csharp
using UnityEditor;
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
cube.name = "UndoableCube";
cube.transform.position = new Vector3(0, 1, 0);
Undo.RegisterCreatedObjectUndo(cube, "Create Cube");
return $"Created {cube.name} (Undo available)";
```
## Destroy GameObject with Undo
```csharp
using UnityEditor;
GameObject obj = GameObject.Find("ObjectToDelete");
if (obj == null)
{
return "GameObject not found";
}
Undo.DestroyObjectImmediate(obj);
return "GameObject destroyed (Undo available)";
```
## Named Undo Group
```csharp
using UnityEditor;
GameObject selected = Selection.activeGameObject;
if (selected == null)
{
return "No GameObject selected";
}
Undo.SetCurrentGroupName("Complex Transform Operation");
Undo.RecordObject(selected.transform, "");
selected.transform.position = Vector3.zero;
selected.transform.rotation = Quaternion.identity;
selected.transform.localScale = Vector3.one;
return "Reset transform (Single undo step)";
```
## Collapse Multiple Operations into One Undo
```csharp
using UnityEditor;
int undoGroup = Undo.GetCurrentGroup();
Undo.SetCurrentGroupName("Batch Operation");
GameObject cube1 = GameObject.CreatePrimitive(PrimitiveType.Cube);
cube1.name = "Cube1";
Undo.RegisterCreatedObjectUndo(cube1, "");
GameObject cube2 = GameObject.CreatePrimitive(PrimitiveType.Cube);
cube2.name = "Cube2";
cube2.transform.position = Vector3.right * 2;
Undo.RegisterCreatedObjectUndo(cube2, "");
GameObject cube3 = GameObject.CreatePrimitive(PrimitiveType.Cube);
cube3.name = "Cube3";
cube3.transform.position = Vector3.right * 4;
Undo.RegisterCreatedObjectUndo(cube3, "");
Undo.CollapseUndoOperations(undoGroup);
return "Created 3 cubes (Single undo step)";
```
## Modify ScriptableObject with Undo
```csharp
using UnityEditor;
string path = "Assets/Data/GameSettings.asset";
ScriptableObject so = AssetDatabase.LoadAssetAtPath<ScriptableObject>(path);
if (so == null)
{
return $"Asset not found at {path}";
}
Undo.RecordObject(so, "Modify Settings");
SerializedObject serializedObj = new SerializedObject(so);
SerializedProperty prop = serializedObj.FindProperty("maxHealth");
if (prop != null)
{
prop.intValue = 200;
serializedObj.ApplyModifiedProperties();
}
return "Modified ScriptableObject (Undo available)";
```
## Modify Material with Undo
```csharp
using UnityEditor;
string path = "Assets/Materials/MyMaterial.mat";
Material mat = AssetDatabase.LoadAssetAtPath<Material>(path);
if (mat == null)
{
return $"Material not found at {path}";
}
Undo.RecordObject(mat, "Change Material Color");
mat.color = Color.red;
return "Changed material color (Undo available)";
```

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 44b713c36d7984259a4e9da6710ebe55
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: