Skip to content

Picking

Entity picking answers a scene question in entity terms: what does this ray, this screen position, or this camera's forward direction hit, and which entity, if any, owns what it hit. Picking is NodeSimulation's, reached through engine.simulation.

typescript
import type { EntityPickFilter, EntityPickResult } from '@skewedaspect/sage';

The three picks

MethodPicks along
pickEntityWithRay(ray, filter?)A given Ray
pickEntity(x, y, filter?)The ray a screen position projects into the scene
pickEntityForward(camera, range, filter?)A camera's forward direction, out to range

All three answer with an EntityPickResult or undefined, and all three resolve and filter the same way.

typescript
const hit = simulation.pickEntityForward(camera, 5, { tags: [ 'interactive' ] });

if(hit !== undefined && hit.entity.hasBehavior(InteractBehavior))
{
    showPrompt(hit.entity.ops.promptText());
}
typescript
scene.onPointerDown = (event, pickInfo) =>
{
    const hit = simulation.pickEntity(scene.pointerX, scene.pointerY);

    if(hit !== undefined)
    {
        console.log(hit.entity.name, hit.point, hit.distance);
    }
};

Resolving a hit to an entity

Babylon's own pick answers with a mesh, not an entity. Resolving the mesh to the entity that owns it walks the mesh's ancestors, the mesh itself, then its parent, then that parent's parent, until one entity's own node matches, checked against every NodeEntity the simulation holds. The walk answers with nothing when the chain runs out without a match, such as level geometry with no entity of its own.

The walk keeps no lookup of its own in step with spawn and despawn, and writes nothing to any node. The same walk resolves a fresh node, a pooled one, and an adopted one alike.

A node the walk crosses before reaching a match is not itself the answer: a turret's own node resolves to the turret, not to the ship carrying it, because the walk stops at the first NodeEntity it matches.

A despawned poolable entity's node is disabled while it waits in the pool, and Babylon's pick already excludes a disabled node.

Filtering

typescript
interface EntityPickFilter
{
    tags ?: readonly string[];
}

tags is a list an entity's own tags must all hold for a pick to answer with it. An entity the walk resolves to that fails the filter is the same as no hit at all; picking never tries whatever the same ray might hit behind it. Omitting the filter matches every entity the walk resolves to.

The pick result

typescript
interface EntityPickResult
{
    entity : NodeEntity;
    mesh : AbstractMesh;
    point : Vector3;
    distance : number;
    normal : Vector3;
}
FieldMeaning
entityThe resolved NodeEntity
meshThe Babylon mesh actually hit
pointThe hit point in world space
distanceThe distance to it
normalThe surface normal there, or the zero vector when Babylon cannot compute one

entity is a plain NodeEntity. Narrow it with instanceof against a class, or with hasBehavior.

Not in this release

A multi-result pick, answering with every entity along a ray or under a screen position rather than only the nearest, is not built.

Released under the MIT License.