Steering behaviour basics
Keywords:
tutorial,
steering,
behaviour
Author(s): Peter Eschler
Date: 2006-06-06
Summary: This tutorial shows you how to use steering behaviours to add some life to your world.
What are steering behaviours?
Citing Craig Reynolds GDC 1999 paper:
Steering behaviours are a solution for one requirement of autonomous characters in animation and games: the ability to navigate around their world in a life-like and improvisational manner.
By combining predefined behaviours a variety of autonomous systems can be simulated. The basics of steering behaviours are described in Craig Reynolds paper and there are plenty of other resources out on the web (e.g. www.steeringbehaviors.de) - just google for "steering behaviours". Make sure you have read and understood the basic principles (vehicles with behaviours) as the rest of this tutorial focuses on how to use them with Instant Player.
The steering sytem
We start with creating a SteeringSystem node and giving it a nice name:
Code: The steering system.
<?xml version="1.0" encoding="UTF-8"?> <X3D profile='Immersive'> <Scene> <SteeringSystem DEF='steerSystem'> </SteeringSystem> </Scene> </X3D>
The parameters of the steering system will be discussed later. First we'll insert some vehicles into our system.
Adding vehicles
Within a steering system one or more vehicles represent autonomous agent(s) parameterized with behaviours. The vehicle class used in Instant Player is based on a point-mass approximation which allows for a simple physically-based model (for example, a point mass has velocity (linear momentum) but no moment of inertia (rotational momentum)).
Adding a vehicle to the steering system looks like this:
Code: Adding two vehicles to the system
<SteeringSystem DEF='steerSystem'> <SteeringVehicle DEF='vehicle1' maxSpeed='2' maxForce='4' /> <SteeringVehicle DEF='vehicle2' maxSpeed='6' maxForce='12' /> </SteeringSystem>
Warning: A SteeringVehicle can only be a child of exactly one SteeringSystem at a time. Re- USEing a vehicle in another system is not supported and will lead to undefined results.
Parameterizing the vehicle
A vehicle has a few attributes, which can be read and set at any time:
- mass :
- the mass of the vehicle
- radius :
- the radius of the vehicle (used for obstacle and neighbour avoidance)
- maxSpeed :
- the maximum speed of the vehicle
- maxForce :
- the maximum force of the vehicle
There two additional inputOutput fields named useFixedY and fixedY. If useFixedY is true the value of fixedY is used as the y-component of the vehicles position. By setting a fixed value the vehicle can be constrained to stay on a fixed plane. If you dynamically route values to the field more interesting effects are possible.(e.g. terrain following). If useFixedY is false the value of fixedY is ignored.
Adding behaviours to the vehicles
After our steering system is equipped with some vehicles we need to add behaviour(s) to them, otherwise they won't do anything (which isn't very interesting). The available behaviours are:
- SeekBehaviour :
- A Seek behaviour acts to steer the character towards a specified position in global space.
- FleeBehaviour :
- Flee is the inverse of seek and acts to steer the character away from the target.
- AvoidNeighborBehaviour :
- Tries to keep characters which are moving in arbitrary directions from running into each other.
- AvoidObstaclesBehaviour :
- Gives a character the ability to avoid obstacles.
- EvasionBehaviour :
- Evasion is similar to Flee except that the menace (target) is another moving character.
- PursuitBehaviour :
- Pursuit is similar to Seek except that the quarry (target) is another moving character.
- WanderBehaviour :
- Wander is a type of random steering.
Code: Adding behaviours to the vehicles
<SteeringSystem DEF='steerSystem'> <SteeringVehicle DEF='vehicle1' maxSpeed='2' maxForce='4' mass='1.4'> <SeekBehaviour DEF='seekBehaviour' factor='1.0' containerField='behaviours' /> <WanderBehaviour DEF='wanderBehaviour' factor='0.2' containerField='behaviours' /> </SteeringVehicle> <SteeringVehicle DEF='vehicle2' maxSpeed='2' maxForce='4' mass='1.1' > <PursuitBehaviour DEF='pursuitBehaviour' containerField='behaviours' > <SteeringVehicle USE='vehicle1' containerField='quarry' /> </PursuitBehaviour> </SteeringVehicle> </SteeringSystem>
The example above shows a SteeringSystem that contains two vehicles. The vehicles contain different behaviours.
The first vehicle is called vehicle1 and contains two behaviours: a SeekBehaviour and a WanderBehaviour. That way vehicle1 is seeking the target while wandering a little (seek has a factor of 1.0 while wander has 0.1). The target of the SeekBehaviour is not specified explicitly so the default value (0,0,0) will be used. This results in vehicle1 seeking around the origin.
The vehicle2 only contains a PursuitBehaviour which is parameterized to pursue vehicle1. This results in vehicle2 following vehicle1.
Updating the vehicles
In order to run the steering behaviour simulation the vehicle's update field has to be called continously. This could be achieved by connecting a TimeSensor.time field to the update field. In practice the SteeringSystem does this job for you. Instead of connecting the TimeSensor to every single vehicle, you simply connect it to the SteeringSystem.time field which calls the update field on all it's child vehicles.
Code: Connecting a timer to the system
<TimeSensor DEF='timeSensor' loop='true' /> <ROUTE fromNode='timeSensor' fromField='time' toNode='steerSystem' toField='time' />
I don't see anything!?
Right. Until now we have setup a SteeringSystem, inserted some vehicles and added behaviours to them. But there is nothing to see!
That's because the SteeringSystem is a simulation node, that is: it has no visual output. All it does is simulating an autonomous behaviour by calculating a new position and orientation for every vehicle. So after each simulation step triggered by the TimeSensor connected to a SteeringSystem's time field the vehicles of the system contain a new position and orientation in their translation and rotation fields.
It's up to you what to do with these values. The most common practice is connecting the simulated position and rotation to a ComponentTransform which is the parent of a subgraph containing the geometry which visually represents the vehicle.
Moving some boxes
As an example we will add two boxes representing our vehicles.
Code: Adding and moving boxes
<ComponentTransform DEF='trans1'> <Shape> <Box containerField='geometry' size='0.1 0.1 0.1' /> <Appearance><Material diffuseColor='1 0 0' /></Appearance> </Shape> </ComponentTransform> <ComponentTransform DEF='trans2'> <Shape> <Box containerField='geometry' size='0.1 0.1 0.1' /> <Appearance><Material diffuseColor='0 0 1' /></Appearance> </Shape> </ComponentTransform> <ROUTE fromNode='vehicle1' fromField='translation_changed' toNode='trans1' toField='translation' /> <ROUTE fromNode='vehicle2' fromField='translation_changed' toNode='trans2' toField='translation' /> <ROUTE fromNode='vehicle1' fromField='rotation_changed' toNode='trans1' toField='rotation' /> <ROUTE fromNode='vehicle2' fromField='rotation_changed' toNode='trans2' toField='rotation' />
Debugging vehicles and behaviours
A vehicle offers more attributes which are read-only and can be used for displaying the internal state of the vehicle in the case of debugging.
Vehicle's outputOnly fields
- speed :
- The current speed of the vehicle (which is the length of the velocity vector).
- velocity :
- The current velocity vector of the vehicle.
- forward :
- The current forward vector of the vehicle.
- seekForce :
- The current seek force of the vehicle (a null vector if no SeekBehaviour is used).
- avoidObstaclesForce :
- The current avoid obstacle force of the vehicle (a null vector if no AvoidObstacles behaviour is used).