In this section the basic ideas and details behind CEL are described and explained.
What are Entities?
Every object in the game is represented by an entity. The entity system allows the game programmer (using CEL) to speak to game objects in a consistant manner. Examples of entities are: the player, the world itself, creatures, a candle, objects in general, items, ... It is important to note that entities don't have to represent visual objects. An entity can be a part of a story line (i.e. a quest) that the player has to solve.
CEL provides a Physical Layer to interface with Crystal Space and do complex calculations (physics, gravity, ...), and a Behaviour Layer to take care of higher-level game functionality (game logic), using the Physical Layer as its lower level API as appropriate.
Physical Layer Property Classes are implemented to handle significant number crunching tasks and to handle any direct interactions with Crystal Space game objects, such as meshes, cameras, keyboard input, and so forth.
Behaviour Layer classes will handle game logic, strategy, event handling, etc. by calling the Property Classes as needed (and also getting events from Property Classes) and should never need to call Crystal Space structures directly (although this is still possible and useful in some cases).
Here is another way to look at this:
- Physical Layer (or PL): This layer controls the mechanics of the game. It controls the hard constraints that entities have while interacting with the world and other entities. To do this Property Classes are used (more on that later). The PL also has the notion of events or messages. The Physical Layer is written in C++.
- Behaviour Layer (or BL): This layer controls how entities behave within the constraints put forth by the Physical Layer. This layer is responsible for building entities by attaching the appropriate Property Classes and setting them up (i.e. controlling the attributes of such Property Classes). This layer will also react to events or messages from the PL. A large part of the BL can be written in some scripting language (like Python) since this is the most dynamic aspect of the entity system. Note that the Behaviour Layer can be written in Python or in C++. Python is fully optional.
In addition to the two layers there is also a library of general property classes that are useful for most games.
One of the most important concepts in CEL is the notion of a Property Class. Attached to every entity we have a list of property classes. A property class describes a specific kind of behaviour or attribute set for an entity. All the property classes together define what the entity really is. This will become clearer if we give a list of the current implemented property classes:
Essentially it is a piece of code (like a C++ class) that you can attach to an entity. They can send out messages.
- pcworld.region: A region represents an area in the engine. It usually contains some sectors with geometry although it is also possible to make regions containing only mesh factories or materials.
- pcworld.zonemanager: The zone manager is a more powerful version of pcregion. It supports dynamic loading for big worlds.
- pccamera.standard: A camera represents what the player sees on screen. A camera is usually in first-person or third-person mode.
- pcinput.standard: This property class represents input from the keyboard, mouse, or joystick. An entity that has this property class attached to it will get messages when input is generated.
- pctools.inventory: The inventory property class contains a set of other entities. An entity which has this property class is capable of containing other entities. This property class can have several attributes that put some limit on what the inventory can contain. The pctools.inventory.characteristics property class is used to define limits for entities (see below). Common limits are weight, size, number of inventory slots, ... but the system is not limited to those. The system is hierarchical. For example, a box entity can have an inventory so that you can put objects in that box. If the player picks up the box then the box including the contents will be in the player inventory. All objects in the world (except for the world itself) belong to some inventory. This is to avoid dangling references. The inventory system goes to great lengths to insure that the maximum capacity of entities inside is not exceeded. This even works for nested inventories. i.e. if the player holds a box and the weight of the box plus contents is near the limit of what the player can hold then you will not be able to add another item to the box.
- pctools.inventory.characteristics: This property class defines the characteristics that some object has in relation to the inventory system. For example, with this property class you can say that the weight of an entity is 10.
- pcobject.mesh: This property class represents a mesh from the engine. Use this if you want a visual representation for an entity.
- pcobject.mesh.select: If you want this entity to receive messages when other meshes (or this mesh) are selected you can use this property class. It is very configurable and even allows you to drag objects along a 3D plane.
- pcmove.solid: If your mesh has a solid representation that needs to be used for collision detection or physics than it must have this property class.
- pcmove.linear: This is the movement system. If you want an entity to move around, have proper collision detection and gravity then you should use this class.
- pcmove.actor.analog and pcmove.actor.standard: These property classes can be used for the main actor. They help synchronise camera, movement system, and animation on the model. The first does multi-directional movement like in platform and action games, whereas the second does turn and move forward movement which is useful for certain cases.
- pc2d.tooltip: This is a convenient property class that allows you to have tooltips. In some games this can be useful for showing information about the actor on which the cursor is resting.
- pctools.timer: Use this property class to get messages when a specific time has passed. Can also repeat.
- pctools.properties: This is a general property class to store properties with an entity. These properties are otherwise not used. It is simply a way to associate data with an entity.
- pclogic.spawn: A property class that represents a spawn point where creatures or other entities can be created.
- pclogic.trigger: A property class that sends messages to the behaviour as soon as some other entity enters a trigger area. Can be useful for doors that open when a player arrives or something.
- pc2d.billboard: This can be used for 2D images on top of 3D world or even for making full 2D games.
- pclogic.quest: This is a powerful and general quest system using a state machine. This is not only useful for RPG games but also for FPS games where a quest can be as simple as 'player presses button' -> 'light goes on'.
- pcmove.pathfinder: The path finder can be used by creatures that are interested in finding out how to move to some location through the level. This property class works together with pcmove.steer in order to actually move the entity around until it reaches the correct destination. This property class also generates events when the destination is reached or the destination cannot be reached for some reason.
- and more...
Several more property classes are planned like:
- pclocator: The locator is used to keep track of the position of another entity. There can be multiple instances of locator attached to one entity if that entity is interested in more than one entity. When a locator is attached to an entity the entity will receive events when the other entity moves, becomes visible, becomes invisible, or is destroyed. Only the last event is immediate. The others are only calculated every second (default: this is configurable for any locator). The locator is very useful in combination with the pcpathfinder above.
This is only the beginning. More property classes will follow as the game system is further refined. The property class system is a very dynamic and flexible system which allows us to dynamically change properties of entities on the fly. It is possible to add/remove any of the above property classes at any time. This is something that is very hard to do if we were to use the traditional inheritance based entity system.
Property classes are created by getting their property class factory. These are usually implemented in a plugin. You load the plugin at startup of your game and you then create property classes for your entities from that plugin when you need them.
The Physical Layer (PL)
As explained above the PL describes the constraints on interactions between entities and also constraints about what an entity can do. The idea is that you attach property classes to an entity. Every property class defines some specific physical attribute or constraint for an entity. The collection of all property classes attached to some entity define what the entity really is. Property classes can be dynamically added or removed from a given entity. Usually the BL will do that.
One important note with this entity system is that one usually doesn't subclass entities. There is only one type of entity and that is provided by the PL. To control behaviour of entities one adds the appropriate property classes instead. The core of CEL doesn't have property class implementations but only provides interfaces which allows a game to create property class implementations. But CEL will come with a set of pre-made property classes which are potentially useful for all/most games.
The PL itself is split into several sub-layers:
- Entity Manager: The entity manager is responsible for creating and managing bare-bones entities.
- Message System: This will allow property classes to send messages to the BL.
- Property Class System: Here property classes are managed.
- Persistence Layer: To save/load entities the persistence layer is defined.
The Behaviour Layer (BL)
The behaviour layer will work with the PL to create entities, add property classes, setup behaviour, and react to events or messages from property classes. This layer is often implemented using a scripting language (but that doesn't have to be the case).
In this layer the traditional inheritance based entity system can be used (but this is optional). For example, one can create a creature entity which is responsible for setting up all property classes that are relevant for creatures. The player could (in some games) be a creature too and this would then be a subclass of creature. This subclass can then setup additional property classes that are relevant for a player (like camera and input control). If the BL is written in Python then Python subclassing can be used easily for this.
Events or messages are used to communicate from PL to BL. Most events are generated by property classes that are attached to some entities and are usually sent to the BL representation of that entity. If the BL is written in Python (for example) then every event could correspond to a Python method in the corresponding script for that entity.
The following table lists all current messages grouped by the property class by which they are generated (from the list of example property classes above):
- pcinventory_addchild: An entity was added to the inventory contained within this entity.
- pcinventory_removechild: An entity was removed.
- pcinventory_added: This entity was added to another inventory.
- pcinventory_removed: This entity was removed from another inventory.
- pcmeshsel_down: Mouse down on a mesh (selection).
- pcmeshsel_up: Mouse up on a mesh.
- pcmeshsel_move: Mouse move for a mesh.
- pctimer_wakeup: Timer wake-up event.
- pcproperties_setproperty: Property has been set.
- pcproperties_clearproperty: Property will be cleared.
- pccommandinput_...1: Specified command has been activated (i.e. the key associated with the command is pressed).
- pccommandinput_...0: Specified command has been de-activated (i.e. the key associated with the command is released).
- and more...
More events will of course follow. This is only the beginning.
The persistence layer in CEL allows games to save and load entity state. In a single player game this can be used for saving the current game state so that the player can later load the game again. In a multiplayer game this can be used in more controlled scenarios to persists parts of the world in a cache or on the network. There are currently two persistence layer: classic (using binary output) or XML.