Guides Physics 2D Queries 2D Contact Queries

Physics 2D Queries 2 min read Updated Apr 2026

2D Contact Queries

Contact queries answer a different question from casts and callbacks.

They do not ask:

  • what would I hit?
  • what just entered?

They ask:

What am I touching right now?

That makes them useful for grounded checks, wall checks, overlapping hazard logic, and any other situation where current contact state matters more than a single collision event.

Contact query overview

Available Methods

Lenga exposes contact queries on:

  • BoxCollider2D
  • CircleCollider2D
  • Rigidbody2D

Shared methods:

  • isTouching(...)
  • getContacts(...)

Additional rigidbody helper:

  • isGrounded(...)

Use isTouching(...) for Fast Yes-or-No Checks

This is the lightest option when you only need an answer, not the full contact list.

use Lenga\Engine\Core\BoxCollider2D;

$collider = $gameObject->getComponent(BoxCollider2D::class);

if ($collider?->isTouching(false)) {
    // Something solid is touching this collider right now.
}

That is a good fit for:

  • "am I inside a trigger?"
  • "is this hazard already touching the player?"
  • "is this body standing on something?"

Use getContacts(...) When You Need Details

getContacts(...) returns Collision2D objects, so you can inspect who you touched and how.

$contacts = $collider?->getContacts(false) ?? [];

foreach ($contacts as $contact) {
    $otherName = $contact->otherGameObject?->name ?? 'Unknown';
    $normal = $contact->normal;

    Debug::info(
        'Touching ' . $otherName
        . ' with normal ' . $normal->x . ', ' . $normal->y
    );
}

This is where contact queries become more than a boolean. They let you inspect the actual contact state and decide what to do next.

Rigidbody2D::isGrounded(...)

Grounded checks are common enough that Rigidbody2D exposes them directly.

use Lenga\Engine\Core\Rigidbody2D;

$body = $gameObject->getComponent(Rigidbody2D::class);

if ($body?->isGrounded()) {
    // Allow jump
}

You can also tune how strict the slope support should be:

if ($body?->isGrounded(0.75, false)) {
    // Require a flatter supporting surface
}

This is especially useful for:

  • platformer jump gating
  • landing logic
  • checking whether a character is on a steep slope

Collider Queries vs Rigidbody Queries

Use collider queries when you care about one specific shape.

Use rigidbody queries when you care about the whole body and the contacts that body currently owns.

Example:

$box = $gameObject->getComponent(BoxCollider2D::class);
$body = $gameObject->getComponent(Rigidbody2D::class);

$colliderTouching = $box?->isTouching(false) ?? false;
$bodyGrounded = $body?->isGrounded() ?? false;

Both are valid. They just answer slightly different questions.

Trigger Filtering

By default, contact queries include triggers.

If you want only solid contacts:

$touchingSolid = $body?->isTouching(false);

If you want triggers included:

$touchingAnything = $body?->isTouching(true);

Layer Filtering

Use a layer mask when you only care about some contacts.

use Lenga\Engine\Core\Physics2D;

$groundMask = Physics2D::layerMask(3); // for example: World layer

if ($body?->isGrounded(0.5, false, $groundMask)) {
    // Grounded only against the layers we consider ground
}

This keeps contact logic focused and prevents unrelated overlaps from polluting gameplay decisions.

Good Fits for Contact Queries

Contact queries are especially useful for:

  • grounded movement
  • ladder or climb zone checks
  • melee weapons that stay active for a short window
  • hazards that should damage while the player remains inside them
  • editor/debug tooling that needs to inspect current overlap state

A Good Rule of Thumb

Use callbacks when you care about the exact moment contact starts or ends.

Use contact queries when you care about the current state of contact every frame.

Use casts and overlap queries when you want to probe ahead or search an area.