Version 3 (modified by Philip Taylor, 14 years ago) ( diff )

--

Simulation System Architecture

Concepts

The entity system is the infrastructure that runs the game's simulation/gameplay code. (The gameplay itself is not part of this system - the system is not even specific to RTS games - but it is all built on top of this system, and influences the system's design.)

The design is based most closely on the one presented in GPG5 (Bjarne Rene: Component Based Object Management; Game Programming Gems 5, 2005, page 25). Some other useful references: Evolve Your Hierarchy, Game Object Structure: Inheritance vs. Aggregation, Scott Bilas's GDC 2002 presentation: A Data-Driven Game Object System, and a few of the things linked from those.

An important concept in the entity system is the entity. This represents any kind of 'thing' in the simulation world - a person, a tree, a rock, an arrow, and more abstract things like event triggers, players, and player input controllers.

Entities consist of a set of components. A component is a largely self-contained piece of data and code, responsible for one part of the behaviour of an entity. One component might be responsible for rendering the entity; another for keeping track of its location in the world; another for tracking its health and reducing it when damaged and killing the entity when reaching zero.

Each component is an object instance in the C++ code. However, there is no C++ object representing an entity - each component is tied to an entity ID (an arbitrary integer), and an entity exists only as a concept defined by the set of components with the same entity ID.

Components have two communication mechanisms: one-to-one communication with another component, by retrieving the component with a QueryInterface call (described in more detail later) and then calling methods on it; and one-to-many communication by posting or broadcasting messages, which will be received by any component that has chosen to subscribe to that message type.

The goal of the system is to ease development of moderately complex gameplay code, and to easily adapt to changing gameplay requirements. There is a focus on modularity and flexibility - it should be easy to read, understand, modify and replace a component. Therefore a component should be small, with few dependencies on other components. (These are often conflicting requirements - lots of small components require more dependencies than a few large components. Thoughtful design is needed.)

Scripting

To simplify development, improve iteration times, and avoid crashes, most gameplay code should be written in JavaScript. Each component can be written either completely in C++, or completely in JS. Native<->scripted component communication is exactly the same as native<->native and scripted<->scripted, except that a native component may not expose all its methods to script.

Components should be written in C++ only when necessary for run-time performance, memory usage, or to interact with C++ parts of the game engine (e.g. the renderer). Run-time performance should only be a concern for code that is executed every frame (e.g. render functions), or executed every simulation turn for a large number of entities (e.g. checking for any unit coming into range). Components should be initially written in JS, and if profiling indicates they are slow, then try to optimise the JS or call it less often (e.g. run on a timer rather than on every simulation turn), and if it's still unfixably slow then it could be rewritten in C++.

Component scripts support hotloading: while the game is running, you can edit and save a script file, and it will be immediately reloaded and used in the game with no need to stop or restart. (The data associated with each component will not be changed at all, only the code.)

Note: See TracWiki for help on using the wiki.