Game Engine Overview

The game uses a modified entity component system, ecs, based mainly on the ideas from Alex Kehayias presentation Functional Game Engine Design for the Web. It's recommended to watch this.

The game state is a single hashmap containing other clojure data structures. State transitions are functions from one game state to another. State transition functions are composed of systems (see below). Since clojure data structures are persistent we could get some of the benefits of the flyweight pattern without any effort.

See Flyweight Pattern and Clojure Data Structures

List of concepts used in engine

Entities

Entities are objects in the game world. Each entity has a collection of components attached to the entity.

Components

Components store component-state for each entity with that component attached. Component-state is implemented as a hashmap.

Systems

Systems are functions that take the game state hashmap and a vector of events as an arguments and returns a pair, the updated game state and events that should be sent. Which systems should be used to get the next gamestate is specified by scenes.

Systems are connected to a type of component. They iterate over all entities. If an entity has a component attached the system may calls a component-function or perform an some action involving the entity.

Systems are also Observers. They can subscribe to events, reciving all events of a specific type. They can subscribe to multiple types of events at the same time.

Component-functions take the component-state and events as an arguments and returns a pair containing the new component-state and new events.

Scenes

A Scene is a vector of systems that should be ran with the game state in the specified order to get the next game state. Exactly one scene is active at any given moment.

Events

Events are used to send information between different component. An event consists of an event-id and a hashmap (possibly vector in the future) containing the event-arguments. They can be sent by systems to other systems. If an observer recives an event or not is decided by the events type.

Local events that can only be recived by the entity sending it should maybe be added.

Services

Asynchronously executes actions that do not need to be synced with the rest of the engine.

If a sound system is added it should probably be a service.

question: can player IO be implemented as a service?

(Services are not a priority right now).

See Service Locator

Instance map

Some kind of instance map containing information of what entities are located at a position is needed to allow* entities to interact with each other based on their location in the game world.

(* allow them to do it efficiently without looping through every entity way too many times)

Game State Hashmap

The game state is implemented as a clojure hashmap.

Keys

:entities

Hashmap from entity-id to set of component-ids.

:systems

Hashmap from system-id to system function.

:scenes

Hashmap from scene-id to vector of system-ids.

:active-scene

Scene-id of currently active scene.

:state

Nested hashmap where the path [:component-id :entity-id] contains the state of a component in the entity.

:subscriptions

Hashmap from system-id to collection of event-types/event-ids. Should maybe be under :system key instead.

:events

Hashmap from system-id to vector of events sent to system. The vector should be empty after the corresponding system was called.

Example State:

{:entities {:player #{:move :print-state}}
 :systems {:state-printer (get-system-fn :print-state print-state)
           :movment (get-system-fn :move move)}
 :scenes {:default [:movment :state-printer]}
 :active-scene :default
 :state {:move {:player {:x 0 :y 0}}
         :print-state {:player {:printed-state "state-thing"}}}
 :subscriptions {:movment #{:move-entity}}
 :events {:movment []}}