Skip to content

Collider Debug Visualization

When physics colliders don't line up with your meshes, you get invisible walls, objects falling through floors, and a lot of confused swearing at your monitor. SAGE wraps BabylonJS's PhysicsViewer into a three-level API that lets you visualize colliders with a single line of code or a single line of YAML.

Three ways to use it

ApproachBest for
Config-driven (debugCollider in entity defs / YAML)Always-on during development
Entity methods (entity.showCollider())Runtime toggling, inspector tools
Low-level manager (gameEngine.debug.colliders)Custom debug UIs, fine-grained control

Most of the time you want the config-driven approach. Drop debugCollider: true into your entity definition and forget about it until you ship.

Config-driven: debugCollider

Add debugCollider to an entity definition, a level spawn, or an entity node config. SAGE applies it automatically when the entity is created during level loading.

In entity definitions (TypeScript)

typescript
const soldier : GameEntityDefinition = {
    type: 'soldier',
    defaultState: { /* ... */ },
    behaviors: [ SoldierBehavior ],
    debugCollider: true,                    // all colliders, default color
};

Other forms:

typescript
debugCollider: '#ff0000'                    // all colliders in red
debugCollider: { head: '#ff0000', body: '#00ff00' }  // per-node colors
debugCollider: { head: '#ff0000', shield: false }    // show head, explicitly hide shield

In level config (YAML)

Level config overrides the entity definition. This means you can leave debugCollider off in your entity definitions and turn it on for specific levels or spawns without touching TypeScript.

yaml
name: battlefield
scene: /assets/levels/battlefield.glb
physics: true

# Override for all soldiers in this level
entities:
    soldier:
        debugCollider: '#00ff00'

# Override for a specific spawn point
spawns:
    player_spawn:
        entity: player
        debugCollider: true

The priority chain: spawn/entity config in YAML > entity definition debugCollider > nothing.

Entity methods: showCollider() / hideCollider()

These convenience methods on GameEntity find all physics nodes in the entity's node tree and delegate to the debug manager. Useful when you want runtime control — a debug key binding, an inspector panel, etc.

showCollider()

typescript
entity.showCollider();                     // all colliders, default color
entity.showCollider(true);                 // same (useful for config-driven patterns)
entity.showCollider('#ff0000');            // all colliders in red
entity.showCollider('head');              // single node by name
entity.showCollider('head', '#ff0000');   // single node by name, in red
entity.showCollider({                     // per-node control
    head: '#ff0000',
    body: '#00ff00',
    shield: false,
});

The node names (like 'head') correspond to the node names in your scene file. In Blender, that's the object name in the outliner.

hideCollider()

typescript
entity.hideCollider();                    // hide all
entity.hideCollider('head');              // hide one by name

Example: Toggle colliders with a key binding

typescript
let collidersVisible = false;

gameEngine.managers.inputManager.on('action:toggleColliders', () =>
{
    collidersVisible = !collidersVisible;

    for(const entity of gameEngine.managers.entityManager.getAllEntities())
    {
        if(collidersVisible)
        {
            entity.showCollider('#00ff00');
        }
        else
        {
            entity.hideCollider();
        }
    }
});

Low-level: ColliderDebugManager

The manager lives at gameEngine.debug.colliders and operates on individual TransformNode references. This is what the entity methods delegate to under the hood.

typescript
const debugColliders = gameEngine.debug.colliders;

// Show a node's collider
debugColliders.showColliderForNode(node);
debugColliders.showColliderForNode(node, '#ff0000');

// Hide it
debugColliders.hideColliderForNode(node);

// Hide everything
debugColliders.hideAll();

// Check if a node's collider is currently visible
debugColliders.isShown(node);

// Clean up all debug visualization
debugColliders.dispose();

Imports

typescript
import { ColliderDebugManager } from '@skewedaspect/sage';
import type { ColliderDebugConfig } from '@skewedaspect/sage';

ColliderDebugConfig is the union type accepted by debugCollider and showCollider():

typescript
type ColliderDebugConfig = boolean | string | Record<string, string | false>;

How it works

A few details worth knowing:

  • Lazy initialization. The underlying PhysicsViewer is not created until the first call to showColliderForNode(). If you never use debug visualization, there is zero overhead.

  • Utility layer rendering. Debug meshes render on a separate UtilityLayerRenderer, so they don't interfere with raycasts, picking, or your scene's render pipeline.

  • Material caching. Color materials are created once per hex string and reused. Showing 50 colliders in #ff0000 creates one material, not 50.

  • Colors are hex strings. Use #RRGGBB format. Named CSS colors (red, green) are not supported — they will be passed directly to Color3.FromHexString() and throw.

  • Node tree walking. entity.showCollider() recursively finds all nodes with a physicsBody in the entity's node hierarchy. This covers compound physics setups where colliders are on child nodes, not the root mesh.

Released under the MIT License.