chore(unity): 固定Unity CLI Loop本地包并移除UnitySkills依赖
This commit is contained in:
@@ -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
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2445f075f1af349fc8bf73259f1df493
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c5155f736569e440ea4679a66a5ee36b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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";
|
||||
```
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 39fd76f51419343c1bbc8844b9e1f45f
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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";
|
||||
```
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cd5beae8d98da49f590990720e8d86be
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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))}";
|
||||
```
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 13f19aa1f6ea44f119e9e253e14d731c
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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}";
|
||||
```
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6b5ad08d159fb4390911b484746791d1
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
```
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 86022f911600881458e40f35e562a5db
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
```
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0d40893521e758a4997b2352987ab719
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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}"))}";
|
||||
```
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 54bdd16e7f97c42198b472ac9bc4aa14
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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();
|
||||
```
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 84096b299132946e9ae4bba41cd210b0
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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";
|
||||
```
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aaaab94d718944441974c7a6da7c9e19
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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";
|
||||
```
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2092122bf65684e53a6d650bd4a61327
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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);
|
||||
```
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e93ff8749746b44fd8e6a12a6ba31eb7
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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";
|
||||
```
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8b3773a6fd47d4211b5a13faa4ef3b7c
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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)";
|
||||
```
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 44b713c36d7984259a4e9da6710ebe55
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user