Interactive Swarm Space

A behavior is a cause and effect relation between parameters.

This page is a reference for all built-in behaviors as well as a small tutorial on how to create your own behaviors.

Behaviors are added to a swarm using the following syntax:

SWARM_NAME.addBehavior ( BEHAVIOR_NAME,
                         BEHAVIOR_CLASS ( INPUT_PARAMETERS, OUTPUT_PARAMETERS ));

SWARM_NAME = the name of an existing swarm
BEHAVIOR_NAME = an arbitrary but unique name
BEHAVIOR_CLASS = the behavior class (see the compilation of all built-in behaviors below)
INPUT_PARAMETERS = a list of the names of all input parameters (separated by spaces) as a string
OUTPUT_PARAMETERS = a list of the names of all output parameters (separated by spaces) as a string

Most behaviors can be further specified by additional parameters (so-called "internal parameters"). These parameters are automatically created after a behavior has been added and can subsequently be set using the following syntax:

SWARM_NAME.set ( INTERNAL_PARAMETER_NAME, VALUE );

INTERNAL_PARAMETER_NAME = the internal parameter name is always preceded by its behavior's name, delimited by an underscore character (e.g. acceleration_maxAngularAcceleration for an AccelerationBehavior named "acceleration").


Built-in Behaviors

For each behavior there is a list of all input and output parameters along with the number of dimensions. (Typically "n-dim" could be replaced with "3-dim", because that's the standard number of dimensions for realistic flocking behavior. However we would like to keep this open, so that even a string theorist can get his fix and create 11-dimensional swarms...).




How To Create Your Own Behaviors

Now we will show you how to implement your own behavior class. This includes as well, creating and using your own parameters (if your behavior needs additional ones).

Please remind yourself of the relation between agents, parameters and behaviors:

  1. A swarm consists of agents (or can be treated as an agent itself)
  2. Agents have a set of parameters
  3. Agents have a set of behaviors
  4. Parameters can have n dimensions (in most cases one or three)
  5. Parameters serve as input to and output from a behavior
  6. Agents can share parameters with other agents by placing their parameters in a neighborhood space in order to find e.g. the three closest parameters to itself.

First of all, make sure you have the ISOFlock SVN code downloaded (details in Installation). Open your iso_flock project. You will notice, that all built-in behaviors in this page, can be found in a folder called behavior. However, we recommend you create your own folder (something like "my behaviors"), otherwise you will likely forget code you've written yourself and even erase it accidentally.

As you can observe in any of these behavior source code files, they all inherit from the Behavior base class:

class MyNewBehavior : public Behavior

which gives them a set of methods to use as is and another set of methods to re-implement yourself (aka programming interface):

First and foremost, of course the constructors:

// create behavior
// input and output parameters are space separated

MyNewBehavior(const base::String& pInputParameterString,
              const base::String& pOutputParameterString);

// create behavior
// pAgent = agent this behavior belongs to
// pBehaviorName = name of behavior
// pInputParameterString = input parameter string (parameters are space separated)
// pOutputParameterString = output paramater string (parameters are space separated)

MyNewBehavior(Agent* pAgent, const base::String& pBehaviorName,
              const base::String& pInputParameterString,
              const base::String& pOutputParameterString) throw (FlockException);

These are of course the means by which you will later instantiate a behavior as part of your flock.

The first constructor is actually only a blueprint from which you can copy behaviors to different agents using the create() method:

virtual Behavior* create(const base::String& pBehaviorName, Agent* pAgent) 
		const  throw (FlockException);

This will be used if you create swarms of agents, so that the swarm itself can copy the behavior for all its agents.

However you can of course use the second constructor and pass a target agent to the behavior at the point of instantiation.

By far the most important method of your behavior class is the act() method:

virtual void act(); 

This method will be called every simulation step. It calculates the output parameters of your behavior using the input parameters as source. Since this is the one method you will spend of your time programming, we will first explain the rest of the methods and types you need to be concerned with.

In the protected body, you will keep the pointers to the parameters you need in your behavior. Let's look for example at the parameter pointers of AccelerationBehavior, one of the built-in behaviors:

Parameter* mMassPar;        // mass parameter (input)
Parameter* mVelocityPar;    // velocity parameter (input)
Parameter* mForcePar;       // force parameter (input)
Parameter* mAcceleration;   // acceleration parameter (output)
Parameter* mMaxLinearAccelerationPar;   // maximum linear
                                        // acceleration parameter (internal)
Parameter* mMaxAngularAccelerationPar;  // maximum angular
                                        // acceleration parameter (internal)

The way these parameter pointers are being set correctly goes like this: In your constructor you provided the class with a set of input parameters in the form of a string holding the parameter names (e.g. "mass velocity force"). The behavior base class will parse these strings and create a vector of mInputParameters and mOutputParameters. You then read the content of these vectors to your parameter pointers:

mMyFirstInputParam* = mInputParameters[0];  
mMySecondInputParam* = mInputParameters[1];
...

If you need additional internal parameters, you have to create them yourself in your constructor:

 mMyInternalParameter = createInternalParameter("PARAMETER_NAME",
                              math::Vector<real>(NUM_OF_DIMENSIONS, DEFAULT_VALUE)); 

Internal Parameters are parameters created and used only by your behavior. However, since they are parameters as well, nothing will actually keep you from using them as input for other behaviors.

Okay, time to do some acting!

For your act() method to run as fast as possible, it's a good idea, to first of all create local references of your parameters, so that you have to access the parameter object only once every act() method call:

real& param1 = mMyFirstInputParam->value(); // 1-dimensional 
math::Vector<real>& param2 = mMySecondInputParam->values(); // n-dimensional 

The above example depicts this process with a one-dimensional parameter (e.g. "mass") and a three-dimensional parameter (e.g. "position" in a 3D space). Do the same thing for all output and internal parameters.

In some cases for output parameters, you need the output of your previous act() call while at the same time already filling that specific output parameter with new values. This is why we do not only supply a values() method for parameters, but also a backupValues() method, which will not be overwritten immediately.

math::Vector<real>& param3 = mMyOutputParam->backupValues();

Now, all that's left to do is you personal monkey voodoo magic using the local references we just created. Write your output directly to these references as well.


Pitfalls in Writing an Act Algorithm

1) Keep it fast! There is a Pitfalls section at the bottom of "Create your own units", that will give you some hints, to keep your code fast. In short: no casts, no unnecessary computation, avoid division, minimize conditional structures.

2) Test it for boundary conditions.


Last updated: May 15, 2024