Skip to main content

Ray

Ray's are a useful tool for testing if geometry intersects a line of sight with Collider.rayCast. You can think of them as a point in space with a direction.

Examples where ray casting can be useful

  • Custom physics implementations (platformers, slopes, etc)
  • Line of sight AI
  • Proximity detection
warning

If you start a raycast from inside an Actor it will hit that Actor first unless you explicitly exclude it. See Raycast Options below.

Scene Testing​

It is possible to test against all actors in a Scene at once.

typescript
// given a scene reference
const game = new ex.Engine({...});
game.start();
const ray = new ex.Ray(ex.vec(100, 100), ex.Vector.Right);
game.currentScene.physics.rayCast(ray, {...});
// or in a custom scene
class MyScene extends ex.Scene {
someRayTestMethod() {
const ray = new ex.Ray(ex.vec(100, 100), ex.Vector.Right);
this.physics.rayCast(ray, {...});
}
}
typescript
// given a scene reference
const game = new ex.Engine({...});
game.start();
const ray = new ex.Ray(ex.vec(100, 100), ex.Vector.Right);
game.currentScene.physics.rayCast(ray, {...});
// or in a custom scene
class MyScene extends ex.Scene {
someRayTestMethod() {
const ray = new ex.Ray(ex.vec(100, 100), ex.Vector.Right);
this.physics.rayCast(ray, {...});
}
}

Scene Raycast Options​

Excalibur's raycast can be optionally configured in a variety of ways. By default if no options are provided to the rayCast(ray, ...) the options will be

typescript
const hits = scene.physics.rayCast(ray, {
searchAllColliders: true,
collisionGroup: CollisionGroup.All,
collisionMask: CollisionGroup.All.mask,
ignoreCollisionGroupAll: false,
maxDistance: Infinity,
filter: null,
})
typescript
const hits = scene.physics.rayCast(ray, {
searchAllColliders: true,
collisionGroup: CollisionGroup.All,
collisionMask: CollisionGroup.All.mask,
ignoreCollisionGroupAll: false,
maxDistance: Infinity,
filter: null,
})

Search All Colliders​

By default the raycast will not stop after the first hit is found, if you'd like to only get the first hit set searchAllColliders: false

Max Distance​

Distance is in terms of pixels, this is the maximum length along the ray to search for colliders.

Hit Filtering​

Using the filter option you can do complex filtering logic to reject or accept potential hits.

typescript
scene.physics.rayCast(ray, {
filter: (potentialHit: RayCastHit) => {
// return true to accept the hit
// return false to reject the hit
return true;
}
})
typescript
scene.physics.rayCast(ray, {
filter: (potentialHit: RayCastHit) => {
// return true to accept the hit
// return false to reject the hit
return true;
}
})

Collision Mask​

The collision mask it a bit mask with a 1 in the place for each collision group category you'd like to consider in the raycast. For example this is useful if all your enemies share a collision group category. See collision group documentation for more information.

You may want to pair this with ignoreCollisionGroupAll: true to avoid default actors which collide with everything including specific rayCast masks.

typescript
const playerGroup = new ex.CollisionGroup('player', 0b0001, ~0b0001);
const enemyGroup = new ex.CollisionGroup('enemy', 0b0010, ~0b0010);
...
// this raycast will only actors with the 1 in the second place, so enemyGroup
const enemyHits = scene.physics.rayCast(ray, {
collisionMask: 0b0010;
});
// this raycast will only actors with the 1 in the first and second place, so playerGroup and enemyGroup
const playerAndEnemyHits = scene.physics.rayCast(ray, {
collisionMask: 0b0011;
});
typescript
const playerGroup = new ex.CollisionGroup('player', 0b0001, ~0b0001);
const enemyGroup = new ex.CollisionGroup('enemy', 0b0010, ~0b0010);
...
// this raycast will only actors with the 1 in the second place, so enemyGroup
const enemyHits = scene.physics.rayCast(ray, {
collisionMask: 0b0010;
});
// this raycast will only actors with the 1 in the first and second place, so playerGroup and enemyGroup
const playerAndEnemyHits = scene.physics.rayCast(ray, {
collisionMask: 0b0011;
});

Collision Group​

The collisionGroup searches for actors that match a specific collision group.

You may want to pair this with ignoreCollisionGroupAll: true to avoid default actors which collide with everything including specific rayCast groups.

Specific Actor Geometry Testing​

If you have a specific actor or geometry that you know you will test against, all Collider types implement rayCast(ray, [maxDistance]).

typescript
const actor = new ex.Actor({
pos: ex.vec(200, 100),
width: 100,
height: 100,
color: ex.Color.Red,
})
const ray = new ex.Ray(ex.vec(100, 100), ex.Vector.Right)
// Ray cast against the collider geometry, returns a point if intersects or null if not
const point = actor.collider.get().rayCast(ray)
// Vector(150, 100)
typescript
const actor = new ex.Actor({
pos: ex.vec(200, 100),
width: 100,
height: 100,
color: ex.Color.Red,
})
const ray = new ex.Ray(ex.vec(100, 100), ex.Vector.Right)
// Ray cast against the collider geometry, returns a point if intersects or null if not
const point = actor.collider.get().rayCast(ray)
// Vector(150, 100)