Character Simulation with ScriptX
A general-purpose framework for dynamic behavior
By Assaf Reznik
Dr. Dobb's Journal
Filmore T. Goose is an obsessively neat bird, the Felix Unger of the animal
world, who continuously picks up after fellow animals. Irma La Sheep is
a good-hearted quadruped, always ready to knit a sweater as a gift for any
animal that happens to pass by. These are two of the characters in Playfarm,
a prototype multimedia title for children implemented in ScriptX from Kaleida
Labs. ScriptX is a platform-independent, object-oriented development environment
for creating multimedia applications (see the accompanying text box, "Introducing
Software Tools for the Professional Programmer
Volume 19, Issue 13, November 1994
Playfarm consists of a virtual farm environment populated with some interesting
and truly autonomous characters:
A scene from Playfarm. The hand at the bottom of the image is an intelligent-cursor
object, part of the user-representation component.
Every character in the Playfarm simulation runs in its own thread and can
interact with the other characters in a rich, complex, and unpredictable
manner. Playfarm's open-ended structure allows you to dynamically add new
characters into the environment while it is running. This article describes
the architecture and implementation of Playfarm, which was designed in a
general-purpose way so that its design and code can be reused in creating
Many current multimedia titles are structured as a series of film clips
stitched together in a static way that sometimes provides a convincing illusion
of dynamic behavior - even though every sequence has been planned, scripted,
and burned into code before execution time. However, we wanted our characters
to interact in a more dynamic fashion, rather than in the usual, predetermined
way. At each clock tick, characters sense, process, and react to their surroundings.
Their reaction depends both on the state of the environment at a given time,
and on their own internal state. This free-form response is implemented
by a character's "activity engine," discussed later.
A related design goal was to create characters with real personalities which
reveal themselves over time. Behavior must be continuous rather than "action
bites." Characters respond to the environment and to the user. When
Playfarm props and characters are added, removed, or otherwise changed,
characters must continue to interact with their surroundings in a manner
consistent with their personalities.
Another goal was to make the environment extensible, creating an experience
that users could modify and control, if so inclined. An additional goal
was to create a general-purpose, content-independent model for character-based
simulation. By creating an abstract model that is decoupled from the presentation
layer, this model can be used in other contexts. Our approach is guided
by the well-known Model-View-Controller (MVC) paradigm, first used in Smalltalk-80
and now incorporated into modern class libraries, including ScriptX.
The Playfarm architecture consists of three components: simulation environment,
character and prop, and user representation. The user-representation component
is the simplest, consisting of a geometric space equal in size to the screen
display, and an intelligent cursor object.
The simulation-environment component is a general-purpose framework for
a character-based simulation. It is divided into the presentation space
and the model space.
The layered structure of the Playfarm project distinguishes the modeling
space from the presentation space.
The presentation space is roughly analogous to the View portion of the Smalltalk
MVC. It contains all visible objects (except for the cursor, which belongs
to the user-representation component), as well as objects that manipulate
the visual aspects of the environment - such as the Panner object,
which is responsible for panning or moving around the model space. Only
the section of the farm that is visible at a given time is part of the presentation
space. By contrast, the model space covers the whole span of the virtual
The character-and-prop component is, as you might expect, the set of classes
used to instantiate characters and props. Props are inanimate objects in
the space, while characters have animated behavior. The next figure illustrates
the structure of a Playfarm character, which consists of an animation object
and a character shell.
The structure of a Playfarm character, which spans both modelingand presentation
The animation object contains all the raw animation sequences for a character.
Each of these sequences is named so that the model object can refer to it.
Whenever the model makes an animation request, the name of the requested
animation sequence is added to the animation queue and then played in order.
The character shell contains the character's repertoire of activities and
the logic describing the relationships between this set of activities and
factors such as time and mood (which in itself has a relationship to the
environment and to other characters). These relationships are modeled in
the character's "activity engine." Excerpts from the Playfarm
source code are shown in this listing.
The Model Space
Playfarm maps the virtual farm onto a simulation-modeling space. This is
a geometric mapping. It consists of a web of triangles whose nodes form
a series of equilateral triangles:
From each node, a character can potentially move in six directions: right,
down/right, down/left, left, up/left, and up/right.
This kind of grid has several advantages over other designs. Unlike a traditional
rectangular grid (with four directions of movement), it is not immediately
obvious to the user that characters can only move in six possible directions.
Also, unlike a three-dimensional model, the flat structure of the Playfarm
grid significantly reduces the complexity of character-animation sequences:There
are only two basic character orientations (left- and right-facing), obviating
the need for four views of a character (back, front, left, right).
Although the farm is presented on a vertical (visually upright) screen from
a frontal view point, the mapping is of an aerial view. Each row, then,
represents a different z-plane in the virtual farm. This is translated to
the presentation layer by setting the z instance variables of corresponding
presenters in each row to reflect the depth. Thus we can achieve a two-and-a-half-dimensional
The class Grid consists of nodes which are instances of class Cell
(both are available electronically). It contains the behavior objects of
Playfarm - that is, prop shells and character shells. The Cell class,
among other things, implements a basic set of services for moving from one
cell to any of its six neighbors, for adding and deleting objects from a
cell, and for accessing a cell's contents and that of its neighboring cells.
The Character Component
In designing a Playfarm character, the first step is creating some animation
sequences. You can do this with tools such as Macromedia Director. Cells
created with Director must be converted to ScriptX's internal object representation,
Bento, using ScriptX's import facility. Bento is an object-storage format
designed at Apple and used in other software systems such as OpenDoc and
the Taligent Frameworks. There is a Bento file associated with each character.
Next, these individual frames must be combined into a meaningful, named
sequence. The Playfarm function makeFrameSequence takes a Bento file,
a direction in which a character moves while performing the animation, the
number of nodes to move (0 or 1 in Playfarm), the bitmaps associated with
that sequence, and an optional sound argument. This function returns a FrameSequence
object that, when properly attached to a movement controller, animates through
all cells of the animation and smoothly moves the character in the specified
direction at a velocity synchronized with the flip animation. All sequences
in Playfarm are created such that they either start and end at the same
location or move the animated object exactly one cell. For more details
on the Animation class, see the
source code listing.
In the class SheepPresenter For Irma, sequences are defined for walking,
facing, turning (all these in the six possible directions), as well as for
knitting, eating, complaining, and so on. Then the animation object is told
what the transition sequences are - in this case, turnLeft and turnRight
- and which sequences a character can go to from each transition. The model-character
object is unaware of the names of transition sequences; they are abstracted
from the behavior layer. Thus, when the behavior component of the character
decides to walk left when the character is walking right, the animation
object contains the logic that a transition sequence needs to be played
before the character can walk left. This completes the presentation aspect
of the character.
The class Shell is included in the
source code listing, along with its derivative CharacterShell.
Together, these two encapsulate the behavior aspect of a character: attributes,
current state, direction, relation to the model grid, and relation to a
target object with which it might be choreographed. The Shell class
and its subclasses provide services for keeping model shells and shell presenters
in sync with each other. The Shell class also provides services for
adding new shells, with their presenters, to the scene. To create the behavior
shell for Irma, we must inherit from CharacterShell. This wires the
sheep object to the model space and to its presenter.
The meat, if you will, of the sheep shell is its logical actions. The sheep
presenter's animation sequences are combined into actions that make sense
by defining character actions in the init method for the Sheep
object. These actions rely on action classes, which provide the general
building blocks for creating meaningful activities for characters. Each
action instance is one atomic thing a character does, such as "walk
left" or "knit a sweater." Combining these atomic actions
into sequences and branches results in interesting behaviors and coherent
interactions between objects in the system. Actions are the wires that connect
a character's activity engine with the model space and the animation class.
Actions are combined together by two mechanisms: nested actions and action
sequences. In the simplest case, an action simply corresponds to one animation
sequence. The animation object (sheep presenter) is referred to by the targetAction
instance variable of the Action object. But targetAction can
also refer to a nested action or action sequence. In this case, when the
action is invoked, it will invoke yet another action after doing some processing
of its own. Sequencing several actions together in a meaningful context
creates an activity.
Irma possesses a wander-around activity, a knit-sweater activity, and a
complain activity. complainActivity, for example, is a sequence of
three wander actions and a random action, which is nested. Sixty percent
of time that complainActivity is invoked, it calls complainAction
(the rest of the time it returns with no effect).
The Character Engine
The last step in creating a character is to install character activities
into the activity engine. The approach described here was influenced by
Patie Maes' work at MIT Media Lab. The character engine is the main driver
of the character component. Character-level decisions define the overall
expression of characters. For example, a character engine may make a character
decide to sleep when it is tired, clean when it is anxious, and knit sweaters
when it encounters a friend. The character engine understands schedules
(biological clocks), moods, and the character's sense of the environment.
It weighs all these different values and makes changes in its activities
The character engine models a character's traits and then influences the
choice of activities that the character undertakes over time. Each activity
is associated with a weight. This weight changes dynamically through the
experience. Weights are influenced by factors such as the schedule, motivations,
and reactions to the environment. For example, consider a dog character
whose activity repertoire consists of eat, sleep, run around, and chase
a cat. Each of these activities is assigned an initial weight. Then, if
the dog is old and lazy, we create a schedule that favors sleeping and eating
activities most hours of the day (or what ever time unit is abstracted by
the schedule). The tiredness motivation goes up each time the character
ends up running or chasing a cat.
Schedules link the weights of certain behaviors to the passage of time.
One example is a 24-hour clock that influences a different behavior for
each hour of the day. In our dog example, the schedule may read: eat at
8 a.m., run around at 9, chase cat at 10, eat at 11, and so on. Possible
patterns can be seasonal, circadian, or simple rhythms. At each cycle of
the activity engine, appropriate activity for that time is selected. Then,
that weight associated with it is added to the active weight for that activity.
Strengtheners can strengthen an activity or a schedule. For example, if
the dog chases a cat whenever one is in the vicinity, we can use a strengthener
on the schedule to check if the current activity is the scheduled one. If
it isn't, the strengthener will magnify the scheduled activity even more,
thereby enforcing the schedule.
Motivations are externally attached personality factors of the character.
This is how a character can pick up the feeling of a locale or experience.
Motivations are usually modeled as range values that can be lowered or raised
- for example, our dog has a loner motivation, which is triggered by a sensor
of too_much_commotion around our dog. Whenever the sensor reads that
the dog is not alone (that is, other characters are around), the loner motivation
is pushed higher. In this way the dog might choose behavior based on its
desire to be alone.
Reactions are sets of activities triggered directly by events (signaled
by sensors). This allows characters to be unconditionally responsive to
events no matter what their mood is at the time; for example, when hit by
Importing a Character
One of Playfarm's most attractive features is that it allows end users to
import characters and props at run time. After a user spends some time with
Playfarm, it can evolve into the user's own creation.
The process of developing and importing an independent character is very
simple: You must provide the system with the presenter definition, the character-shell
definition, and the object store containing the media. These steps have
all been described here. The only remaining steps are to put the new character
in its initial location, add it to the scene, and start its activity engine.
Assaf is a senior multimedia designer at Kaleida Labs, where he developed
Playfarm with Lisa Lopuck, Mike Powers, and other engineers. He can be contacted
Copyright 1994 Assaf Reznik, email@example.com
Reprinted by permission. For more information contact: Dr Dobbs Journal,
411 Borel Avenue Suite 100, San Mateo CA 94402
Playfarm code: Copyright 1994 Kaleida Labs, Inc.