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() updates gameObject.speed every tick, and the scene physics step integrates that velocity as world units per second afterward.
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)],
120, // movement speed in pixels per second
true, // debug
true // cyclic (loop)
);
npc.setWalker(walker);
walker.start();
Notes:
- Waypoints are interpreted in scene/world space (they’re compared to
getScenePosition()). speedis expressed in pixels per second, so values around80to140are a more typical starting point than1or2.- 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.
When no path is found
If obstacle avoidance is enabled and the walker cannot find a path, it throws by default. You can control that behavior:
walker.setPathfindingOptions({
avoidObstacles: true,
pathNotFoundBehavior: "snap", // "throw" | "stop" | "snap" | "continue"
snapTargetToEdgeDistance: 12,
});
throw: throw an error when no path is found.stop: stop the walker and leave the object in place.snap: if the waypoint is blocked, search for the nearest free target position withinsnapTargetToEdgeDistanceand path to that instead.continue: skip pathfinding and walk straight toward the waypoint.
For custom handling, use onPathNotFound:
walker.setPathfindingOptions({
avoidObstacles: true,
onPathNotFound: ({ goal }) => ({ behavior: "snap", goal }),
});
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.
If you want a larger obstacle-avoidance playground with draggable blockers, see Enemy patrol (Walker).
Pathfinding control
When obstacle avoidance is enabled, you can temporarily pause or cancel path updates without removing the walker:
walker.pausePathfinding();
walker.resumePathfinding();
walker.abortPathfinding();
pausePathfinding(): keep the current routed path, but stop recalculating new ones.resumePathfinding(): re-enable pathfinding and request a fresh recalculation.abortPathfinding(): clear the current path, zero movement, and leave pathfinding paused until you resume it.
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.
Example
For a full interactive walker demo with moving patrols, obstacle avoidance, and draggable blockers, see Enemy patrol (Walker).