breve Documentation: version 1.9 | ||
---|---|---|
<< previous | Chapter 2. "steve" Language Reference | next >> |
All objects in the simulated world correspond to programming objects in steve. In order to define an agent in the simulated world, you'll start by constructing a programming object, or class. This class will serve as a template which defines the agent's behaviors. This section describes how to construct and use these classes.
The section Defining Classes (Section 2.4.1) describes how to define an empty class.
All classes have two major components: methods, which define a class's behavior and variables which define the data that the class can hold. This data can be used both to store information about the state of the agent, or information required for computation. The section Defining Instance Variables (Section 2.4.2) details how variables can be added to objects, while the section Defining Class Methods (Section 2.4.3) shows how method are defined.
Two special methods are critical for an agents behavior: one that
gets called automatically when the agent is created, init
, and another which is run automatically at every step of the simulation,
iterate
. These methods, and a few other special methods
are discussed in the section Special Method
Names (Section 2.4.4)
Even after the class is defined, it will still not be present in the simulation. This is because a class is nothing more than a "template" for an agent. In order to bring agents into the simulation, you must use the template to create instances of the class. The section on Creating and Destroying Instances (Section 2.4.5) describes how instances of classes are created and destroyed.
When building a class, you typically don't start from scratch—instead, you make the new class the child of an existing class. This is called creating a subclass. By subclassing a class, the new class will inherit all of the parent's methods and variables. This approach means that most of the difficult work will already be done by an existing breve class, and we can simply override and customize the behaviors that we need to.
For example, if we're building an object which will move through the 3D world, we'd like to have an object that understands the relationship between position, velocity and acceleration. Instead of implementing such a class ourselves, we can subclass Mobile.tz which is included with breve. Our custom subclass will contain the custom behaviors we desire, while the parent class takes care of the details.
When building a class, you must first decide the class name and its parent class. The parent class is the class from which the new class will inherit its behaviors. Classes which are to be used primarily for computation and do not require any special inherited behaviors, will typically use the generic root class Object. Classes which move around the world will inherit behaviors from Mobile, while immobile objects in the world will inherit behaviors from Stationary. A full list of classes is available in the appendix (Appendix A).
An empty class is simply defined by the following steve code:
parent_class_name : class_name { } |
(aka alias_name)
after the class name.As an example of defining a class, both with and without an alias, consider a class
called myMobile
which will be a child of the class
Mobile
:
# first without the alias... Mobile : myMobile { } # now with the alias... Mobile : myMobile (aka myMobiles) { } |
This code above defines an empty class with no variables and no methods. This means that it will behave exactly as its parent class does. The next step is to customize the class' behavior by adding in methods and variables.
An instance variable is a variable associated with a class. Each instance of the class will have it's own private copies of the class' instance variables.
Once the empty class declaration has been written, variables can be added using the
heading + variables
, followed by the list of instance variables.
Variables are listed in the format
variable_name (variable_type).
The variable name must start with a letter, but afterwards may contain any alphanumeric characters, as well as the characters _ and -.
Multiple variables of the same type can also be declared on the same line:
variable1, variable2, variable3, ... (variableType). |
As an example, we'll add some variables to the simple class we created in the previous section:
Mobile : myMobile { + variables: myInt, myOtherInt (int). myObject (object). myFloat (float). } |
The most simple method call in steve is a call to a method which takes no arguments. The definition of such an method is simple. Inside the definition of instanceName, we create a line:
+ to methodName: |
To define an method which takes arguments we will need the keyword,
variable name and type of each argument. The keyword identifies the variable
when it is being called, while the variable name is how the variable will be
referenced from within the method. Finally, the type is simply the type of
variable that will be passed in. The layout of this information is
keyword variable_name (type)
, such that a method which
takes one
variable could be defined by the following line:
+ to set-velocity to-value theValue (float): |
+ to set-rotation of-joint theJoint (Object) to-value theValue (float): |
The code associated with the second method would then use the variables
theJoint
and theValue
: "of-joint"
and "to-value" are not actual variables, but instead the keywords which
indicate which variables follows.
The calling convention of these methods is simple. After the instance name
and method name, we give a list of keywords and values to be passed in.
The order of the keyword/value pairs does not affect how the code is
executed, though it may effect the readability of the code. The following
lines call the set-rotation
method which we defined above:
# the following lines are equivalent myObject set-rotation of-joint myJoint to-value 200. myObject set-rotation to-value 200 of-joint myJoint. |
Methods may also have local variables associated with them. These variable definitions look just like class variable definitions, except that they follow after the method definition line, and not after the variable definition line. Method variables are automatically initialized to zero every time the method is called. Variable declarations in a method must precede all statements in the method.
For example, here is a simple method which uses local variables:
+ to find-closest-creature in creatureList (list): item (object). closestItem (object). distance (float). # we start with a unreasonably high "closestDistance" so that # we are sure to find something closer. closestDistance = 1000000. foreach item in creatureList: { distance = |(self get-location) - (item get-location)|. if distance < closestDistance: { closestItem = item. closestDistance = distance. } } return closestItem. |
![]() | For developer use only | |
---|---|---|
When examining the internal classes included with the breve distribution, you might notice some methods defined using a minus sign instead of a plus sign:
|
Certain method names have special meaning in steve, in that they are called automatically
by the simulation at special times. These methods, in particular init
and iterate
are critical, as they are the entry-point into how your
agents are initialized and how they will behave. These special method names are outlined
below:
init
, if it exists, is called automatically when a class is instantiated. The method is called not only for the class being instantiated, but also for its superclass and all other ancestors up to the root object. Though you should implement an init method for your class which will set up the instance when the class is instantiated, the init method should never be called manually.
iterate
, if it exists, is called automatically during every iteration of the breve engine. If your class must perform a task during each iteration, then you may choose to implement an iterate method. The order in which the objects in the simulation are iterated cannot be controlled—if you need to control the order in which actions are performed, consider using iterate in conjunction with the post-iterate method described below.
Unlike the init
and destroy
methods, iterate
is not automatically called for the superclasses of an instance. This means that your iterate method must call super iterate if you wish to incorporate the parent's iterate method. This is absolutely necessary for subclasses of Control.
post-iterate
, if it exists, is called automatically during every iteration of the breve engine after the iterate methods for all objects have been called. It is occasionally desirable to perform an action during each iteration, which requires data produced during that very same iteration from other objects. If this action is to be performed in the iterate method, than object A cannot be certain that object B has been iterated yet (and vice-versa). To solve this problem, objects may implement a post-iterate method which is automatically called after all objects have been iterated. The PatchLife demo uses this technique.
destroy
, if it exists, is called automatically when a class is being freed. As with the init method, destroy is automatically called for all superclasses as well. If your class needs to perform certain tasks before being destroyed, you should implement this method. However, as with init, this method should never be called manually.
Creating a new instance of an object is called instantiating the object. Instantiating in steve is done using the new command. Instantiation creates a single new instance if no number is given, or as many objects as you want by preceding the command with a number. The syntax follows:
new object_type. number new object_type. |
If a single object is created, it is returned as an object type. If several are created, they are returned as a list. For example:
myBird (object). myBugs (list). myBird = new Bird. myBugs = 100 new Bugs. |
The method init is called automatically for a class and all of its superclasses when the class is instantiated.
Destroying instances is simply accomplished using the command free
:
free instance. |
free
accepts both single instances and lists of instances. If an instance frees itself, then execution of the code is stopped immediately, as though the free
command
was followed by a return
.
When an object is freed, the destroy
method is automatically called for the instance. Prior to version 1.9, destroy
would automatically be called for all superclasses. This is no longer the case.
<< previous | breve Documentation table of contents | next >> |
The Controller Object | up | Types in "steve" |