Walker
Walker is an optional movement helper for GameObjects. It can:
- move an object along a list of waypoints (patrol paths, NPC routes)
- loop those waypoints (cyclic) or stop at the end
- optionally do simple grid-based pathfinding to avoid solid obstacles
- draw debug visuals for waypoints and the computed path
Under the hood, Walker.tick() sets gameObject.speed every tick. The object’s normal tick() then applies that speed to its position.
Attaching a Walker
Create a walker and attach it to a game object:
import { GameObject, Vector, Walker } from "sliver-engine";
const npc = new GameObject("npc", new Vector(100, 100));
const walker = new Walker(
npc,
[new Vector(100, 100), new Vector(300, 100), new Vector(300, 300)],
2, // speed (pixels per tick)
true, // debug
true // cyclic (loop)
);
npc.setWalker(walker);
walker.start();
Notes:
- Waypoints are interpreted in scene/world space (they’re compared to
getScenePosition()). speedis per tick, not per second. Convert from px/s using1 / ctx.getTickRate()if needed (seeGame loop).- Debug drawing respects the scene offset (so it stays aligned if the camera moves).
Starting, stopping, and resetting
start(): activates movement (and requests path recalculation)toggle(): switches active/inactive (stops by zeroing object speed)reset(): resets indices/path but does not teleporthardReset(): resets and teleports the object to the first waypoint (if it exists)
walker.toggle();
walker.reset();
walker.hardReset();
Changing waypoints at runtime
walker.setWaypoints([a, b, c], true); // cyclic
setWaypoints also resets internal path state and stops the object momentarily (speed = 0) so the new route starts cleanly.
Completion callback
If ciclic is false, the walker stops after reaching the final waypoint and calls onComplete:
walker.setOnComplete(() => {
npc.sendMessage("npc:arrived", { name: npc.name });
});
Obstacle avoidance (pathfinding)
When enabled, the walker runs a lightweight grid A* search to route around obstacles.
walker.setPathfindingOptions({
avoidObstacles: true,
gridCellSize: 16,
recalculateEveryTicks: 30,
});
How obstacle avoidance works:
- Only solid hitboxes are considered (
hitbox.solid === true). - The walker builds a “proxy body” from the moving object’s solid hitboxes.
- It treats other active objects’ solid hitboxes in the same scene as obstacles.
- It searches on a 4-neighbor grid (up/down/left/right) and then simplifies the resulting path.
When paths recalculate
The walker can recalculate when:
- you call
requestPathRecalculation() recalculateEveryTickselapsesshouldRecalculatePath(ctx)returnstrue
walker.setPathfindingOptions({
avoidObstacles: true,
shouldRecalculatePath: ({ tick }) => tick % 10 === 0,
});
If there are no obstacles (or the object has no solid hitboxes), the walker falls back to moving directly toward the waypoint.
Performance knobs
For large scenes, tune:
maxExpandedNodes: cap the A* work per recalculationmaxSearchRadiusTiles: cap search distance (in tiles) around the start
walker.setPathfindingOptions({
avoidObstacles: true,
maxExpandedNodes: 5000,
maxSearchRadiusTiles: 64,
});
Debug drawing
If debug is true, the walker draws:
- waypoints (red circles) and waypoint links (red lines)
- the current computed path nodes (cyan circles/lines)
Walker debug rendering is called from the owning object’s render path, but it intentionally cancels object rotation so the path is drawn in world space.
Interaction with physics
Since Walker sets gameObject.speed, it composes naturally with Sliver physics:
- collisions can push the object off its ideal path; the walker will drop “already reached” nodes and continue
- if you also set speed elsewhere (your own
tickFn), last write wins—prefer one source of movement at a time
For physics details (hitboxes, solid vs trigger, impulses), see Physics.