Skip to content

Search.Modifiers

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

Search Modifiers

Modify search behavior with predicates, negation, OR, and availability options.

Overview

Modifiers let you:

  • Filter by component property values
  • Filter by custom GameObject conditions
  • Negate conditions
  • Combine searches with OR logic
  • Include inactive or off-screen elements

Predicate Filters

With<T>

Filter by a component on the matched object. Objects without a T don't match.

await Click(Type<Slider>().With<Slider>(s => s.value > 0.5f));

await TextInput(
    Type<TMP_InputField>().With<TMP_InputField>(f => string.IsNullOrEmpty(f.text)),
    "new value"
);

await Click(Type<Toggle>().With<Toggle>(t => t.isOn));

Where

Filter by an arbitrary GameObject predicate.

await Click(Type<Button>().Where(go => go.CompareTag("Important")));
await Click(Type<Button>().Where(go => go.layer == LayerMask.NameToLayer("UI")));

Interactable

Keep only objects with a Selectable (Button, Toggle, Slider, InputField, …) whose interactable is true. Objects with no Selectable are dropped.

await Click(Type<Button>().Interactable());
await ClickSlider(Type<Slider>().Interactable().First(), 0.5f);

// Equivalent to:
await Click(Type<Button>().With<Selectable>(s => s.interactable));

This reads Selectable.interactable only; it does not consider a parent CanvasGroup.

Visible

Keep only objects whose screen centre is on screen and that overlap their parent ScrollRect viewport (if any). See Search.Spatial.

RequiresReceiver

Keep only objects where an EventSystem raycast at their screen position hits a handler of the given interface types. See Search.Spatial.

Negation

Not

Negate the next condition in the chain.

await Click(Type<Button>().Not.HasAncestor("DisabledPanel"));
await Click(Name("Item*").Not.InRegion(ScreenRegion.TopCenter));
await Click(Type<Button>().Not.Name("*Cancel*"));

Not only negates the immediately following condition:

// NOT under "Disabled", but IS interactable
await Click(Type<Button>().Not.HasAncestor("Disabled").Interactable());

// Negate several conditions with several Nots
await Click(Type<Button>().Not.HasAncestor("Panel1").Not.HasAncestor("Panel2"));

Put Not directly before the condition you mean. Ordering, selection (Skip/Take/First/Last) and Include* calls can't be negated — a Not in front of them carries over to the next condition instead.

Combining Searches

Or

Match elements that satisfy either search.

await Click(Name("OK").Or(Name("Confirm")));
await Click(Text("Yes").Or(Text("Accept")));

await Click(
    Type<Button>().HasAncestor("Panel1")
        .Or(Type<Button>().HasAncestor("Panel2"))
);

Or returns a new search. Include* options are kept if either side set them; ordering/Skip/Take come from the left-hand search. Chain any further conditions after Or(...) — they apply to the combined result.

For simple alternatives, the | pattern syntax is shorter:

await Click(Text("OK|Confirm|Yes"));
await Click(Name("Button*|Btn*"));

Availability Options

IncludeInactive

Also search GameObjects that are not active in the hierarchy.

var panel = await Find<RectTransform>(Name("HiddenPanel").IncludeInactive());
var allItems = await FindAll<RectTransform>(Name("Item*").IncludeInactive());

IncludeOffScreen

Also return objects whose screen bounds are empty or entirely off screen — including objects with no RectTransform, enabled Renderer or enabled Collider (managers, cameras, empty parents).

var manager = await Find<Transform>(Name("GameManager").IncludeOffScreen());

Non-interactable elements need no opt-in — searches already return them. Use .Interactable() to require interactable elements, or With<Selectable>(s => !s.interactable) to find disabled ones. See Availability Filtering.

Examples

Form Validation

// First empty field (top-to-bottom)
var emptyField = await Find<TMP_InputField>(
    Type<TMP_InputField>()
        .With<TMP_InputField>(f => string.IsNullOrEmpty(f.text))
        .First()
);

// Wait for Submit to become clickable, then click
await WaitFor(Name("Submit").Interactable(), 10f);
await Click(Name("Submit"));

Testing Disabled States

var button = await Find<Button>(Name("SubmitButton"));
Assert.IsFalse(button.interactable, "Submit should be disabled when form is empty");

var disabledButtons = await FindAll<Button>(Type<Button>().With<Button>(b => !b.interactable));

Hidden Panels

var hiddenPanels = await FindAll<RectTransform>(
    Name("*Panel").IncludeInactive().Where(go => !go.activeSelf)
);

var panel = await Find<RectTransform>(Name("SettingsPanel").IncludeInactive());
panel.gameObject.SetActive(true);

Complex Filters

var lowSliders = await FindAll<Slider>(
    Type<Slider>()
        .HasAncestor("Settings")
        .Not.HasAncestor("DisabledSection")
        .With<Slider>(s => s.value < 0.5f)
        .Interactable()
);

// Random clickable button that isn't close/cancel (reproducible with SetRandomSeed)
await RandomClick(
    Type<Button>()
        .Interactable()
        .Not.Name("*Close*")
        .Not.Name("*Cancel*")
        .Not.HasDescendant(Text("X"))
);

Related Pages

Clone this wiki locally