Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/sanchedev/tiny-engine/llms.txt

Use this file to discover all available pages before exploring further.

RayCast projects an infinitely thin line from its world-space position in the direction of direction (a relative endpoint vector) and reports the first Collider it intersects whose groups match collidesWith. Unlike the spatial-hash-based broad phase used by Collider, raycasts query the collision system’s registered collider list directly, checking intersection geometry on every frame. RayCast extends Node2D, so its origin is determined by its world-space position, which inherits from any parent Transform nodes.

Usage

import { Vector2 } from 'tiny-engine'
import { useRefNode, useEvent } from 'tiny-engine/hooks'
import { PrimaryNode } from 'tiny-engine/nodes/enum'

function EnemyDetector() {
  const ray = useRefNode(PrimaryNode.RayCast)

  useEvent(ray, 'colliderEntered', (collider) => {
    console.log('Enemy in sight:', collider)
  })

  useEvent(ray, 'colliderExited', () => {
    console.log('Enemy left sight')
  })

  return (
    <ray-cast
      ref={ray}
      direction={new Vector2(200, 0)}
      collidesWith={['enemy']}
    />
  )
}

Props

direction
VectorLike | SignalGetter<VectorLike>
required
The relative endpoint of the ray from its origin. The ray starts at the node’s world-space position and ends at position + direction. A Vector2(200, 0) casts 200 pixels to the right. Reactive when a SignalGetter is passed.
collidesWith
string[]
required
The collider groups this ray checks against. Only colliders whose group set contains at least one of these strings will be detected. Converted to a deduplicated array at construction — immutable afterwards.
position
VectorLike | SignalGetter<VectorLike>
Local position offset for the ray’s origin. Defaults to Vector2.ZERO, meaning the ray starts at the parent transform’s position.
ref
NodeRef<PrimaryNode.RayCast>
A ref created by useRefNode(PrimaryNode.RayCast). Use it to call getCollider() at any time and inspect the currently detected collider.
id
string | symbol
Optional node identifier. Must match [a-zA-Z][a-zA-Z0-9-_]* when a string.
zIndex
number
Draw order among siblings. Defaults to 0.

Events

Subscribe with useEvent. Events fire on both the RayCast node and the detected Collider node.
Event nameCallback signatureDescription
colliderEntered(collider: Collider) => voidFires on the frame the ray first intersects a collider.
colliderExited(collider: Collider) => voidFires on the frame the ray no longer intersects the previous collider.
started() => voidFires once after the node registers with the collision system.
updated(delta: number) => voidFires every frame during the update cycle.
drawed(delta: number) => voidFires every frame during the draw cycle.
destroyed() => voidFires once when the node is removed.
colliderEntered and colliderExited emit on both the RayCast node and the affected Collider node. You can listen on either side depending on which component owns the logic.

Runtime methods and properties

MemberType / SignatureDescription
getCollider()() => Collider | nullReturns the currently detected collider, or null if the ray is clear.
directionVector2Read/write. The relative ray endpoint. Modifying this updates the ray’s direction next frame.
collidesWithstring[]Read-only. The groups this ray detects.
lengthnumberRead-only. Euclidean length of the direction vector.

Debug visualisation

Set testOptions.showRayCasts = true to draw rays as red lines with arrowheads during development.
import { GameConfig } from 'tiny-engine'

GameConfig.testOptions.showRayCasts = true

Complete example — enemy with line-of-sight attack

import { Vector2 } from 'tiny-engine'
import { useSignal, useRefNode, useEvent, useComputed } from 'tiny-engine/hooks'
import { PrimaryNode } from 'tiny-engine/nodes/enum'

const ENEMY_TEXTURE = await loadTexture('/assets/enemy.png')

function Enemy() {
  const ray = useRefNode(PrimaryNode.RayCast)
  const [attacking, setAttacking] = useSignal(false)

  useEvent(ray, 'colliderEntered', (collider) => {
    if (collider.group.has('player')) {
      setAttacking(true)
    }
  })

  useEvent(ray, 'colliderExited', () => {
    setAttacking(false)
  })

  return (
    <transform>
      <sprite
        textureId={ENEMY_TEXTURE}
        sourceSize={[24, 24]}
        modulate={useComputed(() => attacking() ? [1, 0.2, 0.2, 1] : [1, 1, 1, 1])}
      />
      <collider
        shape={shapes.rectangle(24, 24)}
        group={['enemy']}
        collidesWith={['player']}
      />
      {/* Cast 150 px forward to detect the player */}
      <ray-cast
        ref={ray}
        direction={new Vector2(150, 0)}
        collidesWith={['player']}
      />
    </transform>
  )
}

Example — polling getCollider() imperatively

You can also poll the current collider without subscribing to events:
const ray = useRefNode(PrimaryNode.RayCast)

useEvent(ray, 'updated', () => {
  const target = ray.node.getCollider()
  if (target) {
    aimAt(target.globalPosition)
  }
})

return (
  <ray-cast
    ref={ray}
    direction={new Vector2(0, -300)}
    collidesWith={['platform']}
  />
)
Raycasts query the full list of registered colliders each frame, bypassing the spatial hash. If you have hundreds of colliders, prefer Collider-to-Collider overlap events for broad detection, and use RayCast only for precise line-of-sight checks.
  • Collision guide — how the collision system works, spatial hashing, and groups
  • Collider — area-based overlap detection
  • Transform — parent node that sets the ray’s world-space origin
  • useRefNode — obtain a ref to call getCollider() imperatively
  • useEvent — subscribe to colliderEntered and colliderExited

Build docs developers (and LLMs) love