Skip to content

GameObject Manipulation

ODD Games edited this page Oct 14, 2026 · 5 revisions

GameObject Manipulation

Methods for controlling GameObjects during tests: disable/enable, freeze physics, teleport, and toggle collisions.

Overview

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.

Methods

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 / Enable

// 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);

Freeze

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 = true
  • linearVelocity = Vector3.zero
  • angularVelocity = Vector3.zero

and for Rigidbody2D, zero velocities and bodyType = Kinematic. Restore() puts back the original kinematic setting and velocities.

Teleport

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.

NoClip / Clip

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).

Restoration Tokens

Every method returns a token that captured the original state before modifying it.

Manual Restore

var state = await Name("Player").NoClip();

// ... test code with colliders disabled ...

state.Restore();

Using Pattern (Auto-Restore)

Tokens implement IDisposable:

using (await Name("Player").NoClip())
{
    // Colliders disabled here
    await Click(Name("Button"));
}
// Colliders restored

Multiple States

var 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)

Token Members

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");

Static Helper Methods

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);

Common Patterns

Skip Tutorial

// Disable tutorial UI without clicking through it
await Name("TutorialPanel").Disable();
await Click(Name("StartGame"));

Freeze AI During Setup

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 resumes

Pass Through Obstacles

using (await Name("Player").NoClip())
{
    await Name("Player").Teleport(new Vector3(500, 0, 500));
}
// Collisions restored

Setup and Cleanup

using 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();
        }
    }
}

Error Handling

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 Rigidbodies

Related Pages

Clone this wiki locally