Ketsji
Version: $Id: index.html,v 1.2 2001/08/21 11:06:31 nzc Exp $
Module owner: Nzc ([email protected])
Index
- 1. Overview
- 2. Functionality
- 2.1. Top level view
- 2.2. Sensors
- 2.3. Controllers
- 2.4. Actuators
- 3. Implementation details
1. Overview
The gamelogic provides a kind of simple eyes and hands on a game
scene. Bricks are associated with objects, and are linked together per
object. At this moment, the hierarchy is rather flat. There are two
ways of extending the logic in a graphical way: opening up controllers
and making the internals small state-engines themselves, and embedding
sca-chains inside state transition diagrams or petri nets.
Extending the gamelogic is in general an easy task (getting the brick
to perform its specific functionality might not be, of
course...). Adding a brick, including Blender interface and
conversions should not cost more than a day.
2. Functionality
The gamelogic is organised along MVC lines. There are three categories
of bricks: sensors, controllers, and actuators. A frame holds them
together.
2.1. Top level view
- Sensors are tested once per frame. Each sensor is tests for input
exactly once, but there is no ordering in this. A sensor only
generates an event. Sensors can only be connected to controllers.
- Controllers receive pulses from sensors. When all inputs trigger
(that is, fire an event) in the prescribed way, the controller
performs its actions. Again, no guarantees on evaluation order is
given. A controller can access the sensors, in order to extract some
information there. It can also access actuators, to manipulate the
settings of the actuator. The controller rules determine how and which
actuators receive an event. A controller is connected to sensors and
actuators.
- Actuators effect changes in the game world: they interfere with
object locations, object motion, properties, sounds, etc. An actuator
only listens to controller pulses. An actuator can only be connected
to a controller.
The minimal useful setup consists of a sensor and a python
controller. If 'plain' logic is used, the minimal setup requires also
an actuator. The sensor observes some aspect of the gameworld. The
controller decides what to do with this event (a Python controller
could interact with the gameworld in unexpected ways, of course). The
actuator affects an aspect of the gameworld. Everything else is there
to make this possible.
- Each sensor registers to an event manager. Per sensor type, there
is a separate manager. This is done to provide each sensor with some
type-specific callbacks, without recombining all that in a single
event manager. All managers are held together by the top-level logic
manager (this one holds a list of specific managers). Each KX scene
has a top-level logic manager. Controllers and actuators fall
directly under the top-level logic manager. This manager also stores
the sensor->controller and controller->actuator mappings.
- The top-level manager handles the passing of events down the
logic brick chain: only the top-level manager knowns about the links
between the bricks. In principle, it would be possible to change them
on the fly, but at this moment, that is not supported. The
Python-controller scripts can consult the top-level logic manager to
get references to sensors and actuators. This allows the script to
obtain extra information from the sensors, or to change sensing
properties or actuator details.
For each frame, the top-level manager does the following
- Process all sensors.
- Determine which controllers need to fire.
- Evaluate all controllers.
- Determine which actuators to fire.
- Evaluate all actuators.
There is some internal bookkeeping around these things that is local
for the top-level logic manager.
The only ordering guarantee in the evaluation of the logic bricks is
the ordering of the steps above. That is something to keep in mind
when checking what the expected behaviour is. For sensors and
controllers, it is not possible to manipulate evaluation order within
a single frame by 'smart' ordering of the bricks (on the other hand,
you should consider the benefits this gives you). The effect of this
behaviour is that the logic system behaves fully
concurrently. Actuators can remain active for more than one
frame. They are evaluated in the order in which they appear in the UI,
top first, and working down from there. There is a list of currently
active actuators, to which newly triggered (activated) actuators can
be added. An actuator makes its own decision te be removed from the
active-list: the list of actuators is tested at the start of a frame
update. Each actuator can decide to 'switch off', or to remain active
for another round.
2.2. Sensors
Each sensor has an Evaluate() function, that is called when the event
managers processes the sensors. The sensors can fire positive and
negative events. The basic operation mode is going to fire a positive
event when a sensor triggers, and firing again, this time a negative
event, when the sensor condition stops. Most sensors will keep firing
on in between frames as well, but the details on this differ per
sensor. The event type managers determine the exact activation rules
per sensor type. The pulse behaviour can be inverted. Sensors can also
be set to keep firing only at every nth frame (pulse frequency).
2.3. Controllers
A controller has some logic rules. These can be as simple as "AND all
inputs" and may be full-blown Python scripts on the other. A
controller fires when it receives events on all its inputs. The firing
behaviour is simpler: either everything or nothing. (So, in brief, a
controller is a function f: bool -> bool, with f(false) = false
predefined. We would like to have a f: bool^n->bool^n, but that is
something for later). Python controllers can ofcourse circumvent this
evaluation mechanism by directly addressing actuators. This can
interfere with high-level settings. For this reason, the frame update
must be split (see implementation details). Each controller has a
Trigger() method that is called to evaluate the controller rules.
2.4. Actuators
An actuator starts doing its action when any attached controller fires
an event. The evaluation order of actuators is taken from the user
interface: top first, then working down. An actuator can remain active
for multiple consecutive frames. After being triggered, it can behave
like a small autonomous machine. It has to indicate itself that its
current action has finished, and that it wants to become dormant
again. The actions take place in the actuator-evaluation phase; the
actuator cannot interrupt the normal control flow of the game. All
actuator actions must be structured in such a way that an actuator
always leaves behind a consistent gamestate. Each actuator has an
Update() function that is called when the actuator needs to do its
thing.
3. Implementation details
- Gamelogic heavily uses Expressions. Many things are done with
properties, and these are based on the Expressions library. See the
Expressions documentation for the details on how to use these.
- When the top-level logic manager is asked for a level update, it
is actually asked for a beginFrame, an updateFrame and an
endFrame. This is done since Python controllers can affect the scene
hierarchy in unexpected ways. After the Python scripts have run, the
KX engine syncs the hierarchy before the logic is allowed to finish
the frame. Actuators have to resync the hierarchy themselves.
- Any pure-logic brick (that is any brick that does not need
physics, graphics, sound, etc...) will be an extension to the existing
SCA_... object hierarchy. If external modules are required, the brick
must be placed in KX. It will still extend the SCA_ tree,
through. This is important to keep the overall structure of the game
engine intact.
- The converter has three functions for converting the logic brick
types from implementation to something to do with the game engine. Be
careful not to mix dependencies here. Mappings between Blender types
and SCA or KX tpyes must be concentrated in these conversion
functions. This is important to keep the overall structure of the game
engine intact.
- Sometimes, several bricks from the UI (can) map to a single brick
class. The adverse may also be true: conceptually similar things are
implementation-wise very different. Examples are the collision, touch
and near sensors, and the edit object actuator and the mouse
sensor. Converting a creative set of logic bricks can remove the need
to create a new one (or at least it gives a decent prototype).
- Each brick should have a clear, distinct task. The specific
behaviour and the generic logic brick behaviour must remain properly
separated. This is important to keep the overall structure of the game
engine intact.
- All logic bricks are Python-prepared. There is a python
header-define, and some macros that help declaring proper python
headers. You can copy the details from any other brick, or look at the
defines in Expression/Value.h. The online-documentation is important
here! Make doc strings!