Web Engine Docs
Preparing documentation
Use the search bar to quickly find any topic
Preparing documentation
Use the search bar to quickly find any topic
Reynolds-style steering behaviors for creating realistic autonomous agent movement with seek, flee, wander, obstacle avoidance, and flocking.
Steering behaviors create natural-looking movement by calculating steering forces that guide agents toward their goals while avoiding obstacles and other agents.
Seek, Flee, Arrive, Pursue, and Evade behaviors for fundamental agent motion.
Wander, Obstacle Avoidance, Hide, Path Following, and Containment for advanced navigation.
Separation, Alignment, Cohesion, and combined Flocking for realistic group movement.
SteeringCombiner for weighted blending and priority-based selection with zero-GC design.
Steer toward a target at maximum speed:
import { SteeringBehaviors, SteeringAgent, SteeringTarget } from '@web-engine-dev/core/engine/ai';const agent: SteeringAgent = {position: Transform.position[eid],velocity: Velocity.linear[eid],maxSpeed: 5.0,maxForce: 10.0,radius: 0.5,mass: 1.0,};const target: SteeringTarget = {position: targetPosition,};const result = SteeringBehaviors.seek(agent, target);if (result.active) {// Apply steering forceVelocity.linear[eid][0] += result.force.x * delta;Velocity.linear[eid][1] += result.force.y * delta;Velocity.linear[eid][2] += result.force.z * delta;}
Steer away from a threat:
const threat: SteeringTarget = {position: enemyPosition,};// Flee with panic distance (only flee if within 10 units)const result = SteeringBehaviors.flee(agent, threat, panicDistance: 10.0);if (result.active) {// Apply force to move awayapplyForce(eid, result.force, delta);}
Seek with deceleration near the target to avoid overshooting:
const target: SteeringTarget = {position: destinationPosition,};// Arrive with slowing radius of 2 unitsconst result = SteeringBehaviors.arrive(agent, target, slowingRadius: 2.0);if (result.active) {applyForce(eid, result.force, delta);}// Agent will slow down smoothly as it approaches the target
Intercept moving targets by predicting their future position:
// Pursue a moving targetconst target: SteeringTarget = {position: Transform.position[targetEid],velocity: Velocity.linear[targetEid], // Required for prediction};const pursueResult = SteeringBehaviors.pursue(agent,target,lookAheadTime: 0 // 0 = auto-calculate based on speeds);// Evade a moving threatconst evadeResult = SteeringBehaviors.evade(agent,threat,lookAheadTime: 1.0, // Look ahead 1 secondpanicDistance: 15.0 // Only evade within 15 units);
Random exploration with smooth, natural-looking movement:
import { WanderConfig } from '@web-engine-dev/core/engine/ai';const wanderConfig: WanderConfig = {circleDistance: 2.0, // Distance of wander circle from agentcircleRadius: 1.0, // Radius of the wander circleangleChange: 0.3, // Maximum angle change per update};// Wander requires entity ID for per-agent state trackingconst result = SteeringBehaviors.wander(agent, eid, wanderConfig);if (result.active) {applyForce(eid, result.force, delta);}// Clean up wander state when entity is destroyedSteeringBehaviors.clearWanderState(eid);
Navigate around obstacles using feeler rays:
import { SteeringObstacle } from '@web-engine-dev/core/engine/ai';// Define obstaclesconst obstacles: SteeringObstacle[] = [{ position: new THREE.Vector3(5, 0, 5), radius: 1.0 },{ position: new THREE.Vector3(-3, 0, 8), radius: 1.5 },];// Avoid obstacles with 5 unit look-aheadconst result = SteeringBehaviors.obstacleAvoidance(agent,obstacles,lookAhead: 5.0);if (result.active) {// High priority - apply immediatelyapplyForce(eid, result.force, delta);}
Follow a sequence of waypoints:
import { SteeringPath } from '@web-engine-dev/core/engine/ai';const path: SteeringPath = {points: [new THREE.Vector3(0, 0, 0),new THREE.Vector3(5, 0, 5),new THREE.Vector3(10, 0, 5),new THREE.Vector3(10, 0, 10),],looped: false, // Whether to loop back to startradius: 1.0, // Waypoint proximity threshold};let currentIndex = 0;function updatePathFollowing() {const { result, nextIndex } = SteeringBehaviors.pathFollowing(agent,path,currentIndex);currentIndex = nextIndex;if (result.active) {applyForce(eid, result.force, delta);} else {// Path completeonPathComplete();}}
Find cover behind obstacles from a threat:
const threat: SteeringTarget = {position: enemyPosition,};const obstacles: SteeringObstacle[] = [{ position: new THREE.Vector3(5, 0, 0), radius: 2.0 },{ position: new THREE.Vector3(-5, 0, 5), radius: 1.5 },];// Find hiding spot 2 units behind obstaclesconst result = SteeringBehaviors.hide(agent,threat,obstacles,hideDistance: 2.0);if (result.active) {// Moves to hiding spot behind nearest obstacleapplyForce(eid, result.force, delta);}// Falls back to flee if no hiding spots found
Keep agents within defined boundaries:
const min = new THREE.Vector3(-50, 0, -50);const max = new THREE.Vector3(50, 10, 50);const result = SteeringBehaviors.containment(agent,min,max,margin: 5.0 // Start steering when within 5 units of edge);if (result.active) {// Steer back toward centerapplyForce(eid, result.force, delta);}
Avoid crowding neighbors:
// Get nearby agentsconst neighbors: SteeringAgent[] = getNearbyAgents(eid, radius: 5.0);// Separate from neighbors within 2 unitsconst result = SteeringBehaviors.separation(agent,neighbors,radius: 2.0);
Match velocity direction with neighbors:
// Align with neighbors within 5 unitsconst result = SteeringBehaviors.alignment(agent,neighbors,radius: 5.0);
Move toward the center of the group:
// Move toward group center (neighbors within 5 units)const result = SteeringBehaviors.cohesion(agent,neighbors,radius: 5.0);
import { FlockingConfig } from '@web-engine-dev/core/engine/ai';const config: FlockingConfig = {separationWeight: 1.5, // Avoid crowdingalignmentWeight: 1.0, // Match headingcohesionWeight: 1.0, // Move to centerseparationRadius: 2.0, // Personal spaceneighborRadius: 5.0, // Group awareness};const result = SteeringBehaviors.flocking(agent, neighbors, config);if (result.active) {// Apply combined flocking forceapplyForce(eid, result.force, delta);}
Combine multiple behaviors with weights:
import { SteeringCombiner, SteeringPriority } from '@web-engine-dev/core/engine/ai';const combiner = new SteeringCombiner();// Add behaviors with weightsconst seek = SteeringBehaviors.seek(agent, target);combiner.add('seek', SteeringPriority.Normal, 1.0, seek.force, seek.active);const separation = SteeringBehaviors.separation(agent, neighbors, 2.0);combiner.add('separation', SteeringPriority.Normal, 0.5, separation.force, separation.active);const wander = SteeringBehaviors.wander(agent, eid);combiner.add('wander', SteeringPriority.Low, 0.3, wander.force, wander.active);// Calculate weighted blendconst finalForce = combiner.calculateWeightedBlend(agent.maxForce);// Apply forceapplyForce(eid, finalForce, delta);// Clear for next framecombiner.clear();
Higher priority behaviors override lower ones:
const combiner = new SteeringCombiner();// Critical priority: obstacle avoidanceconst avoidance = SteeringBehaviors.obstacleAvoidance(agent, obstacles, 5.0);combiner.add('avoidance', SteeringPriority.Critical, 1.0, avoidance.force, avoidance.active);// High priority: flee from threatsconst flee = SteeringBehaviors.flee(agent, threat, 10.0);combiner.add('flee', SteeringPriority.High, 1.0, flee.force, flee.active);// Normal priority: seek targetconst seek = SteeringBehaviors.seek(agent, target);combiner.add('seek', SteeringPriority.Normal, 1.0, seek.force, seek.active);// Calculate prioritized force (higher priority takes precedence)const finalForce = combiner.calculatePrioritized(agent.maxForce);applyForce(eid, finalForce, delta);combiner.clear();
import { Steering, initSteering, setSteeringBehaviors, SteeringFlags } from '@web-engine-dev/core/engine/ai';// Add steering componentaddComponent(world, Steering, eid);// Initialize with default valuesinitSteering(eid);// Configure steering parametersSteering.maxSpeed[eid] = 5.0;Steering.maxForce[eid] = 10.0;Steering.arriveRadius[eid] = 1.0;Steering.slowingRadius[eid] = 2.0;// Enable behaviorssetSteeringBehaviors(eid, SteeringFlags.Seek | SteeringFlags.Separation);
import { SteeringSystem } from '@web-engine-dev/core/engine/ai';// In your game loopconst steeringSystem = SteeringSystem(world);function gameLoop(delta: number, time: number) {// SteeringSystem automatically applies steering forcessteeringSystem(delta, time);}// Set path for path followingimport { setAgentPath } from '@web-engine-dev/core/engine/ai';setAgentPath(eid, waypoints, looped: false);// Clear path when doneimport { clearAgentPath } from '@web-engine-dev/core/engine/ai';clearAgentPath(eid);
Steering behaviors use pre-allocated vectors to avoid garbage collection:
Use spatial indexing for efficient neighbor lookups:
import { getAISpatialIndex } from '@web-engine-dev/core/engine/ai';const spatialIndex = getAISpatialIndex();// Query nearby entities efficientlyconst neighbors = spatialIndex.queryRadius(position,radius: 10.0,maxResults: 32);// Convert to SteeringAgent arrayconst steeringAgents: SteeringAgent[] = neighbors.map(neighbor => ({position: Transform.position[neighbor.eid],velocity: Velocity.linear[neighbor.eid],maxSpeed: 5.0,maxForce: 10.0,radius: 0.5,mass: 1.0,}));
// Throttle steering updates for distant agentsconst updateInterval = distanceToCamera < 20 ? 0.0 : 0.1;Steering.updateInterval[eid] = updateInterval;// SteeringSystem will automatically throttle updates
import {SteeringBehaviors,SteeringCombiner,SteeringPriority,SteeringAgent,getAISpatialIndex,} from '@web-engine-dev/core/engine/ai';function updateSteering(eid: number, delta: number) {// Build agentconst agent: SteeringAgent = {position: Transform.position[eid],velocity: Velocity.linear[eid],maxSpeed: 5.0,maxForce: 10.0,radius: 0.5,mass: 1.0,};const combiner = new SteeringCombiner();// Get obstaclesconst obstacles = getObstaclesNearAgent(eid);// Critical: obstacle avoidanceconst avoidance = SteeringBehaviors.obstacleAvoidance(agent, obstacles, 5.0);combiner.add('avoidance', SteeringPriority.Critical, 1.0, avoidance.force, avoidance.active);// High: containmentconst bounds = getBounds();const containment = SteeringBehaviors.containment(agent, bounds.min, bounds.max, 5.0);combiner.add('containment', SteeringPriority.High, 1.0, containment.force, containment.active);// Normal: seek target or wanderconst targetEid = getBlackboardValue(AIAgent.blackboardId[eid], 'targetEid');if (targetEid) {const target = {position: Transform.position[targetEid],velocity: Velocity.linear[targetEid],};const pursue = SteeringBehaviors.pursue(agent, target);combiner.add('pursue', SteeringPriority.Normal, 1.0, pursue.force, pursue.active);} else {const wander = SteeringBehaviors.wander(agent, eid);combiner.add('wander', SteeringPriority.Normal, 1.0, wander.force, wander.active);}// Calculate final forceconst force = combiner.calculatePrioritized(agent.maxForce);// Apply forceVelocity.linear[eid][0] += force.x * delta;Velocity.linear[eid][1] += force.y * delta;Velocity.linear[eid][2] += force.z * delta;// Clamp velocity to max speedconst vx = Velocity.linear[eid][0];const vy = Velocity.linear[eid][1];const vz = Velocity.linear[eid][2];const speed = Math.sqrt(vx * vx + vy * vy + vz * vz);if (speed > agent.maxSpeed) {const scale = agent.maxSpeed / speed;Velocity.linear[eid][0] *= scale;Velocity.linear[eid][1] *= scale;Velocity.linear[eid][2] *= scale;}combiner.clear();}