import { SteeringBehavior } from '../SteeringBehavior.js';
import { Vector3 } from '../../math/Vector3.js';
import { MathUtils } from '../../math/MathUtils.js';
const targetWorld = new Vector3();
const randomDisplacement = new Vector3();
/**
* This steering behavior produces a steering force that will give the
* impression of a random walk through the agent’s environment. The behavior only
* produces a 2D force (XZ).
*
* @author {@link https://github.com/Mugen87|Mugen87}
* @augments SteeringBehavior
*/
class WanderBehavior extends SteeringBehavior {
/**
* Constructs a new wander behavior.
*
* @param {Number} radius - The radius of the wander circle for the wander behavior.
* @param {Number} distance - The distance the wander circle is projected in front of the agent.
* @param {Number} jitter - The maximum amount of displacement along the sphere each frame.
*/
constructor( radius = 1, distance = 5, jitter = 5 ) {
super();
/**
* The radius of the constraining circle for the wander behavior.
* @type {Number}
* @default 1
*/
this.radius = radius;
/**
* The distance the wander sphere is projected in front of the agent.
* @type {Number}
* @default 5
*/
this.distance = distance;
/**
* The maximum amount of displacement along the sphere each frame.
* @type {Number}
* @default 5
*/
this.jitter = jitter;
this._targetLocal = new Vector3();
generateRandomPointOnCircle( this.radius, this._targetLocal );
}
/**
* Calculates the steering force for a single simulation step.
*
* @param {Vehicle} vehicle - The game entity the force is produced for.
* @param {Vector3} force - The force/result vector.
* @param {Number} delta - The time delta.
* @return {Vector3} The force/result vector.
*/
calculate( vehicle, force, delta ) {
// this behavior is dependent on the update rate, so this line must be
// included when using time independent frame rate
const jitterThisTimeSlice = this.jitter * delta;
// prepare random vector
randomDisplacement.x = MathUtils.randFloat( - 1, 1 ) * jitterThisTimeSlice;
randomDisplacement.z = MathUtils.randFloat( - 1, 1 ) * jitterThisTimeSlice;
// add random vector to the target's position
this._targetLocal.add( randomDisplacement );
// re-project this new vector back onto a unit sphere
this._targetLocal.normalize();
// increase the length of the vector to the same as the radius of the wander sphere
this._targetLocal.multiplyScalar( this.radius );
// move the target into a position wanderDist in front of the agent
targetWorld.copy( this._targetLocal );
targetWorld.z += this.distance;
// project the target into world space
targetWorld.applyMatrix4( vehicle.worldMatrix );
// and steer towards it
force.subVectors( targetWorld, vehicle.position );
return force;
}
/**
* Transforms this instance into a JSON object.
*
* @return {Object} The JSON object.
*/
toJSON() {
const json = super.toJSON();
json.radius = this.radius;
json.distance = this.distance;
json.jitter = this.jitter;
json._targetLocal = this._targetLocal.toArray( new Array() );
return json;
}
/**
* Restores this instance from the given JSON object.
*
* @param {Object} json - The JSON object.
* @return {WanderBehavior} A reference to this behavior.
*/
fromJSON( json ) {
super.fromJSON( json );
this.radius = json.radius;
this.distance = json.distance;
this.jitter = json.jitter;
this._targetLocal.fromArray( json._targetLocal );
return this;
}
}
//
function generateRandomPointOnCircle( radius, target ) {
const theta = Math.random() * Math.PI * 2;
target.x = radius * Math.cos( theta );
target.z = radius * Math.sin( theta );
}
export { WanderBehavior };