Repository navigation
GameObject Manipulation
Methods for controlling GameObjects during tests: disable/enable, freeze physics, teleport, and toggle collisions.
The manipulation methods are async instance methods on Search. They wait up to searchTime (default 10 s) for the target and return a restoration token that can put the original state back:
using static ODDGames.BugpunchSdk.ActionExecutor;
// By search (on-screen or off-screen GameObjects)
await Name("Player").Freeze();
// By reflection path (the value must be a GameObject, Transform or Component)
await Reflect("GameManager.Instance.player").NoClip();Unlike UI actions, these find off-screen objects too, so they work on any scene GameObject, not just visible UI.
| Method | Returns | Description |
|---|---|---|
.Disable(searchTime) |
Task<Search.ActiveState> |
Deactivate a GameObject |
.Enable(searchTime) |
Task<Search.ActiveState> |
Activate a GameObject (finds inactive objects) |
.Freeze(includeChildren, searchTime) |
Task<Search.FreezeState> |
Zero velocity + make Rigidbodies kinematic |
.Teleport(Vector3, searchTime) |
Task<Search.PositionState> |
Move the transform to a world position |
.NoClip(includeChildren, searchTime) |
Task<Search.ColliderState> |
Disable all colliders |
.Clip(includeChildren, searchTime) |
Task<Search.ColliderState> |
Enable all colliders |
includeChildren defaults to true; searchTime defaults to 10 seconds.
// Disable a GameObject
await Name("TutorialPanel").Disable();
// Enable an inactive GameObject
await Name("TutorialPanel").Enable();
// Via reflection path
await Reflect("GameManager.Instance.tutorialPanel").Disable();
// Custom timeout
await Name("TutorialPanel").Disable(searchTime: 5f);Stop physics simulation on Rigidbodies:
// Freeze an object and its children
await Name("AITruck").Freeze();
// Freeze only the root object
await Name("Player").Freeze(includeChildren: false);
// Via reflection path
await Reflect("GameManager.Instance.player").Freeze();Freeze sets, for Rigidbody:
isKinematic = truelinearVelocity = Vector3.zeroangularVelocity = Vector3.zero
and for Rigidbody2D, zero velocities and bodyType = Kinematic. Restore() puts back the original kinematic setting and velocities.
Move a transform instantly to a world position:
await Name("Player").Teleport(new Vector3(100, 0, 50));
// Via reflection path
await Reflect("GameManager.Instance.player").Teleport(Vector3.zero);Only transform.position is set; children keep their local offsets.
Toggle colliders for pass-through or collision:
// Disable all colliders (pass through objects)
await Name("Player").NoClip();
// Enable all colliders
await Name("Player").Clip();
// Only affect the root object, not children
await Name("Player").NoClip(includeChildren: false);Works with both Collider (3D) and Collider2D (2D).
Every method returns a token that captured the original state before modifying it.
var state = await Name("Player").NoClip();
// ... test code with colliders disabled ...
state.Restore();Tokens implement IDisposable:
using (await Name("Player").NoClip())
{
// Colliders disabled here
await Click(Name("Button"));
}
// Colliders restoredvar targetPosition = new Vector3(10, 0, 10);
using (await Name("AITruck").Freeze())
using (await Name("Player").NoClip())
{
// AI frozen, player has no collision
await Name("Player").Teleport(targetPosition);
}
// Both states restored (the teleport is not undone)| Member | Description |
|---|---|
.Restore() |
Restore the original state (idempotent) |
.Dispose() |
Same as Restore()
|
.Count |
Number of affected components (ActiveState, FreezeState, ColliderState) |
var state = await Name("Vehicle").Freeze();
Debug.Log($"Froze {state.Count} rigidbodies");ActionExecutor also has static wrappers — Disable(search), Enable(search), Freeze(search), Teleport(search, position), NoClip(search), Clip(search) — that forward to the instance methods. Their searchTime defaults to -1, meaning ActionExecutor.DefaultSearchTime; pass it explicitly to wait longer or shorter:
await Freeze(Name("AITruck"), searchTime: 10f);
await Teleport(Name("Player"), new Vector3(100, 0, 50), searchTime: 10f);// Disable tutorial UI without clicking through it
await Name("TutorialPanel").Disable();
await Click(Name("StartGame"));var spawnPoint = new Vector3(0, 0, 20);
using (await Name("EnemySpawner").Freeze())
{
// Position the player without AI interference
await Name("Player").Teleport(spawnPoint);
await Wait(1f);
}
// AI resumesusing (await Name("Player").NoClip())
{
await Name("Player").Teleport(new Vector3(500, 0, 500));
}
// Collisions restoredusing System.Threading.Tasks;
using NUnit.Framework;
using UnityEngine;
using ODDGames.BugpunchSdk;
using static ODDGames.BugpunchSdk.ActionExecutor;
[TestFixture]
public class AITests
{
[Test]
public async Task TestWithFrozenAI()
{
var frozenAI = await Name("AIManager").Freeze();
try
{
// ... test code ...
}
finally
{
frozenAI.Restore();
}
}
}The methods throw TimeoutException if a search target isn't found within searchTime, and InvalidOperationException if a reflection path doesn't resolve to a GameObject, Transform or Component:
// Shorter timeout for a faster failure
await Name("NonExistent").Disable(searchTime: 1f);When the object exists but has nothing to act on, the token reports Count = 0:
var state = await Name("StaticObject").Freeze();
Debug.Log(state.Count); // 0 if there are no RigidbodiesBasic Filters
Proximity
Hierarchy
Spatial
Ordering
Modifiers
Combining
Reflection
- Test Actions
- Click Actions
- Text Input
- Drag Actions
- Gesture Input
- Wait and Find
- Assertions
- GameObject Manipulation