Vector2 arrive(Vector2 target) const noexcept { Vector2 toTarget = target - position; float distance = Vector2Length(toTarget); if (distance < 0.1f) return ZERO; // If already at target, no force needed float speed = globalConfig.max_speed; // Apply gradual deceleration within the arrive radius if (distance < globalConfig.arrive_radius) { speed = globalConfig.max_speed * (distance / globalConfig.arrive_radius); } Vector2 desiredVelocity = Vector2Normalize(toTarget) * speed; return (desiredVelocity - velocity) * globalConfig.arrive_weight; } Vector2 pursuit(const Boid& target) const noexcept { Vector2 toTarget = target.position - position; float distance = Vector2Length(toTarget); // Estimate time to intercept based on distance and relative speed float lookAheadTime = distance / (globalConfig.max_speed + Vector2Length(target.velocity)); // Predict future target position Vector2 futurePosition = target.position + (target.velocity * lookAheadTime); // Seek the predicted position instead of current position return seek(futurePosition); } Vector2 evade(const Boid& pursuer) const noexcept { Vector2 toPursuer = pursuer.position - position; float distance = Vector2Length(toPursuer); // Compute lookahead time based on both speeds float lookAheadTime = distance / (globalConfig.max_speed + Vector2Length(pursuer.velocity)); // Predict future position of the pursuer Vector2 futurePosition = pursuer.position + (pursuer.velocity * lookAheadTime); // Steer away from the predicted position return flee(futurePosition); } struct Config { float wander_distance = 50.0f; // Distance ahead of the boid to project the wander circle float wander_radius = 25.0f; // Size of the wander circle float wander_jitter = 0.2f; // How much the wander angle changes each frame float wander_weight = 0.5f; // Steering force weight for wander behavior }; float wanderAngle = 0.0f; // Persistent wandering angle Vector2 wander() noexcept { // Step 1: Project the wander circle ahead of the boid Vector2 circleCenter = Vector2Normalize(velocity) * globalConfig.wander_distance; // Step 2: Adjust the wander angle slightly wanderAngle += unit_range() * globalConfig.wander_jitter; // Small random variation // Step 3: Compute displacement on the wander circle (ensuring uniform distribution) Vector2 displacement = { cos(wanderAngle) * globalConfig.wander_radius, sin(wanderAngle) * globalConfig.wander_radius }; // Step 4: Calculate the final wander target Vector2 wanderTarget = position + circleCenter + displacement; // Step 5: Seek the wander target return seek(wanderTarget) * globalConfig.wander_weight; }