-
Notifications
You must be signed in to change notification settings - Fork 407
Behaviors
Behaviors follow the Factory Pattern. Please read about the factory pattern to learn about creation, declaration, and extending behaviors.
See also: API Reference on Behaviors
Behaviors (or Behaviours) are the "how" of a physics simulation. They are rules applied to the world that act on bodies during every timestep to simulate specific physical laws. A world without any behaviors will act as an infinite, frictionless vacuum. (No behaviors are included in the core library, they are available as extensions)
The most familiar example of a behavior is adding "gravity" to a simulation. What is most often meant by "gravity" is a constant acceleration in the downward (positive "y") direction. Because of the frequent need for this, a "constant-acceleration" behavior is available as an extension.
Example:
// add some gravity
var gravity = Physics.behavior('constant-acceleration', {
acc: { x : 0, y: 0.0004 } // this is the default
});
world.add( gravity );
// later... flip the world upside down!
gravity.setAcceleration({ x: 0, y: -0.0004 });
Some behaviors act as "detectors", which don't modify bodies directly. Instead they detect specific events and announce them to the world's pubsub system so other behaviors can take appropriate actions. One example of this is collision detection and response. There are separate behaviors for collision detection, collision response, and even so-called "sweep and prune" optimization algorithms.
By default, behaviors will apply to every body added to the world.
Most behaviors have a .applyTo()
method that can be used to control which bodies the behavior is applied to. The method accepts an array of bodies.
Example:
var boxes = [];
// add bodies to the boxes array...
// only apply gravity to the bodies in the boxes array
var accel = Physics.behavior('constant-acceleration')
.applyTo( boxes );
world.add( accel );
Hint: Queries can help collect sub-sets of bodies easily!
The most basic form of a behavior simply involves creating a .behave()
method. If a behavior includes a .behave()
method, it will automatically get subscribed to the world's integrate:positions
event. The integrate:positions
event is the last stage of integration, and a good time to apply accelerations to bodies.
The
.behave()
method is speed-critical, and therefore, usage of Scratchpads is strongly recommended if creating temporary objects.
The .behave()
method will be passed an event object which includes: .bodies
array containing the world's bodies, and .dt
a number representing the timestep size. The behave method should call this.getTargets()
to get the specific bodies this behavior is applied to (which may or may not be all bodies in the world). The constant-acceleration
behavior is a good example of this basic implementation:
// ...
// Apply an acceleration to each body in the world
behave: function( data ){
var bodies = this.getTargets();
for ( var i = 0, l = bodies.length; i < l; ++i ){
bodies[ i ].accelerate( this._acc );
}
}
// ...
In more complex situations, it may be necessary to subscribe to a different part of the integration phase, or subscribe multiple callbacks. To do this, it is necessary to override the methods .connect()
and .disconnect()
where subscribing/unsubscribing can be completely controlled.
The body-collision-detection
behavior is a good example of this. The subscription depends on configuration options:
// ...
// connect to a world
connect: function( world ){
if ( this.options.checkAll ){
world.on( 'integrate:velocities', this.checkAll, this );
} else {
world.on( 'collisions:candidates', this.check, this );
}
},
// disconnect from a world (cleanup)
disconnect: function( world ){
if ( this.options.checkAll ){
world.off( 'integrate:velocities', this.checkAll, this );
} else {
world.off( 'collisions:candidates', this.check, this );
}
}
// ...
References for all behaviors can be found in the API Docs under Behaviors.