FIT CTU

Adam Vesecký

vesecky.adam@gmail.com

Lecture 4

Components

Architecture of Computer Games

Adam Vesecký

Game Model

What is a Game Model

Game model is a model of a domain in which the simulated world takes place.

Game Model

  • Gameplay and all mechanics are incorporated into a game model
  • Game model can be divided into several parts: domain model, view model, networking model,...
  • A game engine updates the model and interprets it to the player
  • With each update, the current state of the model is drawn on screen
  • State - values of all attributes of a model at a given point in time

Game Model

Domain Model

  • contains rules, game mechanics and essential data to the gameplay

Presentation Model

  • a model represented to the player
  • e.g. played sounds, animated sprites

Physics model

  • all entities that take part in physical interaction

AI model

  • all data and structures that are processed by AI (navigation maps, behavioral trees,...)

Networking model

  • everything that has to be in sync with other models on other devices

Example: Super Mario Bros.

Game Model

  • The more complex the system, the more complex the models
  • Simple games have a tiny domain model that can be easily detached from other parts
  • Complex games have highly coupled respective models

Example: Chess

Chessmaster 2000 (1986)

Chessmaster 11 (2007)

Chess Model

Chess models

Chessmaster 2000Chessmaster 11
Domain modelBoard, pieces, time, game rulesBoard, pieces, time, game rules
Presentation modelCGA Sprites, beep soundsAnimated 3D objects, 3D sounds
Physics modelNoneRigidbody engine
AI modelDavid Kittinger's engineKing engine
Networking modelNoneMove commands

Example: Pacman

Pacman architecture

Example: Strategy

Heroes of Might and Magic

Heroes of Might and Magic architecture

Example: Adventure

Belegost (1989)

Polda (1998-2020)

Dreamfall Saga (1999-2016)

Adventure architecture

Adventure events

Object-oriented architecture

Example: Platformer Scene

Hierarchy for the model

How things may get messed up

Issues

  • dragon may have ability to use mana
    • opt. a) rework the object hierarchy
    • opt. b) move mana to a base class
  • mage may have ability to transform himself into a dragon
    • opt. a) create a new object DragonMage
    • opt. b) add a reference to an object the mage has transformed into
  • orc may have ability to walk on foot
    • add more states
  • mage may have an inventory of items affecting his abilities
  • we might want to add a dozen features during the development without the necessity of rearranging the class hierarchy
Not every set of relationships can be described in a directed acyclic graph

Generalizing

This may work for attributes but not for game logic

All-purpose object pattern

  • used in 90's and early 2000's
  • one class determines behavior of every game object
  • lots of switches and if-checks

Example: Doom 2

Object-oriented architecture: summary

  • The most common way of expressing object hierarchy is via OOP and inheritance
  • This approach works well for games with a tiny model and a weak emergence
  • In case of more complex games we can end up with thousands of dependencies
  • Inheritance is insufficient relation for relationship modelling
  • We need to use composition, and that's where the component architecture comes into place

Summary

  • simple
  • fast prototyping
  • low overhead
  • easy to debug
  • hard to maintain
  • hard to scale
  • not flexible
  • game objects may have not needed features

Component-oriented architecture

Component

  • a unit of composition with specified interfaces and explicit context dependencies
  • components are plug-and-play objects
  • prefers composition over inheritance
  • behavior of an entity is determined by the aggregation of its components
  • Dungeon Siege (2002) - one of the first games featuring component-based systems

ECS Pattern

  • Entity-Component-System
  • common pattern for component-oriented libraries and engines
  • game object is just a container for data and logic
  • components are attached to their game objects
  • can be easily integrated into scene graph

Terms

Entity

  • a single entity/game object, usually incorporated into a scene graph

Attribute

  • data unit attached to an entity

Property

  • data unit attached to a component

Component

  • instantiable unit that defines simple functional behavior

System

  • a superior component responsible for a bigger scope (HealthSystem, GameManager, AudioSystem)
The naming varies. Some engines use behaviours for components, components for systems, property for attributes etc.

Example: Platformer

  • attributes are DATA, components are CODE

ECS Architecture in C++

ECS example

Example: Pacman

Pacman

Stage

  • parent of all game objects

Maze

  • model of the maze, contains location of dots, power-pellets etc.

GameManager

  • controls the game as a unified whole
  • may be responsible for collision handling (e.g. by killing pacman or a ghost)

BoxBehavior

  • game logic of the center box

PacmanBehavior

  • controls the movement of Pacman using the InputManager

Where is the game model?

  • we can put it in attributes, components or both
  • if it's an attribute, it can be detached and tested separately, but we need to keep it in sync
  • if it's a component/system, it can make full use of the architecture

Simple games

  • one tiny model that contains game rules and global attributes, stored in the root element
  • game entities have their logic implemented in respective components
  • game model can be used as an API layer by all components

Complex games

  • game rules are implemented in systems (physics, relations, triggers)
  • components customize behavior of individual game elements

Example: Platformer

Example: Paratrooper

Example: Driver

Component-oriented architecture: summary

  • scalable
  • data-oriented
  • components are easy to reuse
  • easy to make new object types
  • polymorphic operations for components
  • dynamic typing - everything is assembled at runtime
  • all depedencies have to be wired together
  • the code must be written in a generic way
  • refactoring may become annoying
  • harder to debug

Messaging

Messaging possibilities

By modifying the container object's state

  • e.g. shared state machine
  • indirect communication
  • difficult to debug

By direct calls

  • OOP way
  • fast, but increases coupling
  • we need to know what to call
  • e.g. calling a global component that is always present

By using a message broker

  • events and commands
  • each component can declare interest in relevant messages
  • slower than the direct call
  • e.g. listening to GAME_OVER event and stopping the game

Message Broker

  • components should be notified of any state change that is relevant
  • can be used for returning values (danger of feedback deadlock)
  • a handler can be implemented inside components - OnMessage() function
  • processing can be instant or delayed/scheduled
  • Event - a message informing that something happened
  • Command - a message instructing that something should happen

Message Types

Unicast

  • a component sends a message to another component
  • in most cases, this can be handled by a direct call
  • example: pause the game

Multicast

  • a) component sends a message to subscribers
  • b) component sends a message to all objects that meet specific criteria
  • example: notify all nearby units that the player has entered the area
  • example: collision trigger - notify all subscribers

Broadcast

  • rarely used, usually for global messages
  • example: level completed, game over

Example: Atomic Game Engine Event Passing

1 void Object::SendEvent(StringHash eventType, VariantMap& eventData) {
2 SharedPtr<EventReceiverGroup> group(context->GetEventReceivers(this, eventType));
3 if (group) {
4 group->BeginSendEvent();
5
6 for (unsigned i = 0; i < group->receivers_.Size(); ++i) {
7 Object* receiver = group->receivers_[i];
8 // Holes may exist if receivers removed during send
9 if (!receiver) continue;
10
11 receiver->OnEvent(this, eventType, eventData);
12
13 // If self has been destroyed as a result of event handling, exit
14 if (self.Expired()) {
15 group->EndSendEvent();
16 context->EndSendEvent();
17 return;
18 }
19 processed.Insert(receiver);
20 }
21 group->EndSendEvent();
22 }
23 }

Example: Unity Messages

1 public interface ICustomMessageTarget : IEventSystemHandler
2 {
3 // functions that can be called via the messaging system
4 void Message1();
5 void Message2();
6 }
7
8 public class CustomMessageTarget : MonoBehaviour, ICustomMessageTarget
9 {
10 public void Message1()
11 {
12 // handle message
13 }
14
15 public void Message2()
16 {
17 // handle message
18 }
19 }
20
21 // sending message
22 ExecuteEvents.Execute<ICustomMessageTarget>(target, null, (x,y) => x.Message1());

Example: Unreal Message Bus

  • facilitates communication between application parts via Message Passing
  • messages are classified into commands and events
  • all messages are wrapped into IMessageContext, containing additional information

Message Broker Summary

  • allows to make the game event-based
  • components can react on any message that goes into event bus
  • not intended for per-frame processing
  • once established, it's not easy to revise the messaging architecture
  • if there is something that should run every frame, it's better to use polling or direct call
  • avoid expensive processing in OnMessage handler

Component-oriented game engines

Component-oriented game engines

Artemis framework

Atomic Game Engine

Unity

  • ECS architecture

Unreal Engine

  • hybrid component architecture

Godot Engine

  • signal-passing architecture

Example: Artemis Engine

  • Systems encapsulate logic, components encapsulate data
1 // new component
2 public class Health extends Component {
3 public int health;
4 public int damage;
5 }
6
7 // new archetype
8 Archetype dragonArchetype =
9 new ArchetypeBuilder()
10 .add(Flaming.class).add(Health.class).build(world);
11
12 public class MovementSystem extends EntityProcessingSystem {
13 public MovementSystem() { super(Aspect.all(Position.class, Velocity.class)); }
14 }
15
16 // create new transmuter
17 this.transmuter = new EntityTransmuterFactory(world)
18 .add(FrozenFlame.class).remove(Flaming.class).build();
19
20 // apply transformation to entity
21 this.transmuter.transmute(entity);

Unity

  • ECS architecture with messaging system
  • MonoBehaviour can encapsulate both data and logic

Unity Architecture

Unity Game Object

1 public sealed class GameObject : Object {
2 public static extern GameObject CreatePrimitive(PrimitiveType type);
3 public unsafe T GetComponent<T>();
4 public T[] GetComponents<T>();
5
6 public extern Transform transform { get; }
7 public extern void SetActive(bool value);
8 public extern string tag { get; set; }
9 public static extern GameObject FindGameObjectWithTag(string tag);
10
11 public void SendMessage(string methodName, object value);
12 public void BroadcastMessage(string methodName, object parameter);
13
14 public Scene scene {get;}
15
16 public Component rigidbody {get;}
17 public Component rigidbody2D {get;}
18 public Component camera {get;}
19 public Component light {get;}
20 public Component animation {get;}
21 public Component renderer {get;}
22 public Component audio {get;}
23
24 public void PlayAnimation(Object animation);
25 public void StopAnimation();
26 }

Example: Platformer2D

Example: Platformer2D Scene Graph

Example: Platformer2D Events

1 // RocketComponent
2 void OnTriggerEnter2D (Collider2D col) {
3 // If it hits an enemy...
4 if(col.tag == "Enemy") {
5 // ... find the Enemy script and call the Hurt function.
6 col.gameObject.GetComponent<Enemy>().Hurt();
7 // Call the explosion instantiation.
8 OnExplode();
9 // Destroy the rocket.
10 Destroy (gameObject);
11 }
12 // Otherwise if it hits a bomb crate...
13 else if(col.tag == "BombPickup") {
14 // ... find the Bomb script and call the Explode function.
15 col.gameObject.GetComponent<Bomb>().Explode();
16
17 // Destroy the bomb crate.
18 Destroy (col.transform.root.gameObject);
19
20 // Destroy the rocket.
21 Destroy (gameObject);
22 }
23 }

Component-oriented libraries

Unity DOTS

EnTT

Entitas

A-Frame

ECSY

  • https://ecsy.io
  • experimental ECS framework for PixiJS, ThreeJS and BabylonJS

Example: ECSY Framework

1 var world = new World();
2 world
3 .registerSystem(MoveSystem)
4 .registerSystem(RendererSystem);
5
6 class MoveSystem extends System {
7 execute(delta, time) {
8 this.queries.moving.results.forEach(entity => {
9 let pos = entity.getMutableComponent(Position);
10 pos.x += entity.getComponent(Velocity).x;
11 });
12 }
13 }

Lecture Summary

  • I know what a game model is and what is it made of
  • I know the pitfalls of object-oriented architecture in games
  • I know what a component is
  • I know the ECS pattern
  • I know messaging practices for component architecture
  • I know the purpose of a message broker in games

Goodbye Quote

The weak suffer. I endure.Planescape Torment