FIT CTU

Adam Vesecký

NI-APH
Lecture 2

Engines

History of game engines

Pre-engine era

Pinball Construction Kit
1983
Thunder Force Construction
1984
Adventure Construction Kit
1984
Garry Kitchen's GameMaker
1988
Arcade Construction Kit
1988
RPG Maker
1990

Example: Vlak (1993)

1 ; ------ priprava pro dalsi objekt
2 PosVlak6:add si,3 ; adresa dalsiho objektu
3 mov dx,cx ; stará pozice predesleho objektu
4 mov al,0
5 cmp ds:[si],al ; je jiz konec vlaku?
6 je PosVlak7
7 jmp PosVlak2 ; neni konec - dalsi vagon
8
9 ; ------ vymazani posledniho vagonu
10 PosVlak7:cmp dl,-1
11 je PosVlak8
12 call DispObr ; vymazani posledniho vagonu
13 mov al,20 ; pocet pozic
14 mul dh ; prepocet radku na offset
15 add al,dl
16 adc ah,0
17 add ax,offset Pole
18 xchg ax,di ; DI <- adresa v poli
19 mov byte ptr ds:[di],0

Source code for MS-DOS: link

ID Tech

  • family of game engines developed by ID Software
  • Id Tech 0 - the very first game engine
  • every next game had more advanced technology
  • all games were based on raycasting until 1996
Hovertank 3-D
1991
Catacomb 3-D
1991
Wolfenstein 3-D
1992
Doom
1993

1993: Doom

1 boolean P_CheckMissileRange (mobj_t* actor){
2 if (!P_CheckSight(actor, actor->target))
3 return false; // can’t see the target
4
5 if ( actor->flags & MF_JUSTHIT ) {
6 // just hit -> fight back!
7 actor->flags &= ~MF_JUSTHIT;
8 return true;
9 }
10
11 if (actor->reactiontime)
12 return false; // do not attack yet
13
14 if (!actor->info->meleestate)
15 dist -= 128*FRACUNIT; // no melee
16
17 // check for ARCH-VILE
18 if (actor->type == MT_VILE){
19 if (dist > 14*64)
20 return false; // too far away
21 }
22 ...

Source code: link

Quake Engine

  • ~id Tech 1
  • released by ID Software in 1996
  • initiated the rise of 3D gaming
  • true 3D real-time rendering, 3D light sources
  • Id Tech 1 Games:
    • Quake, Hexen 2, Silver Wings
  • Other IdTech-based games:
    • Call of Duty
    • Medal of Honor
    • Daikatana
    • Half-Life (forked Source Engine)
  • Successors:
    • id Tech 2, 3, 4, 5, 6, 7 (2020)

Quake source code: link

Quake engine family

Influence of Game Engines

Game Engines Overview

Game Engines Today

Unreal Engine

Unreal Engine 1

  • released in 1998 by Epic Games as FPS game engine
  • Unreal - the first game powered by this engine

Unreal Engine 4

  • film-Quality postprocessing
  • VFX & Particle Systems
  • beta Raytracing

Unreal Engine 5

  • Nanite virtualized geometry
  • Lumen illumination engine
  • source code is available

Unity

  • all-purpose (not only) game engine
  • first announced for MacOS in 2005
  • over 27 platforms supported
  • most common choice for indie developers
  • source code is not available

Unity 2021.1.22 - current version

  • Universal Render Pipeline
  • visual scripting with Bolt engine
  • new networking engine

Ori

Besiege

Cuphead

Godot

  • 2D and 3D game engine
  • GDScript scripting language
  • component architecture
  • MIT license, completely free
  • no famous games so far

Which engine do I need?

  • 2D microgames with simple animations can be made solely without engines
  • more complex games can be made in graphics libraries
  • Unity is great for 2D and 3D indie games, but doesn't offer low-level customization
  • Unreal is difficult to master, yet it can be customized in any way
  • for complex games, migration between engines is practically impossible
  • open-source game engines heavily depend on the community and their future is uncertain

Engine Architecture

Game Engine Primary Modules

  • Game Loop - the heartbeat of all games
  • Scene Manager - manages objects and structures them in a scene graph
  • Input Manager - handles inputs (keyboard, mouse, touch, joystick, gamepad,...)
  • Resource Manager - manages assets, controls a cache
  • Memory Manager - memory allocator and deallocator
  • Rigidbody Engine - event-based collision detection
  • Physics Engine - handles behavior of objects based on forces and impulses
  • Rendering Engine - renders the game, takes care of the rendering pipeline
  • Animation Engine - handles animations
  • Scripting Engine - a bridge between the engine and interpreted languages (JS, C#,...)
  • Audio Engine - plays music, clips, sounds, calculates 3D sound
  • AI Engine - abstract engine for AI (state machines, behavioral trees,...)
  • Networking Engine - handles multipeer communication
  • Other modules - GUI framework, Level Editor, Camera, Event System, LOD system,...

Game Loop

Game Loop

  • the most important part of the game engine
  • each turn advances the state of the game
  • usually coordinated with the event loop of the platform/virtual machine
  • optimal time step for rendering: 60 FPS = 16.6 ms per frame
  • audio, input, and physics require more frequent updates
In general, a program spends 90% of its time in 10% of its code. The game loop will be firmly in those 10%.

Simple Game Loop

Game loop with separated rendering

Cooperative game loop

  • synchronization can be provided by delayed invocation
  • first used in Ultima VII (1994)

Update method

Fixed time step

  • each update advances the game by a certain amount of time
  • deterministic and stable
  • the game may slow down

Variable time step

  • each update advances the game based on how much real time has passed since the last frame
  • natural
  • non-deterministic and unstable

Adaptive time step

  • switches between variable and fixed time step based on thresholds
  • deals with long breaks better than the other two
Some old games were tied with the system clock, hence their CPU-sensitivity.

Example: Unity Game Loop

Update Inconsistencies

  • game objects are consistent before and after every update
  • they may get to an inconsistent state during the update - one-frame-off lag
  • possible solutions: bucket update, script execution order (Unity), process priority (Godot)

Object A reads previous state of Object B and Object B reads previous state of Object C

Scene Graph

Scene Graph

Scene Graph

  • essential structure of every interactive application
  • a way of ordering data into a hierarchy
  • represented as an N-Tree or a regular graph
  • implemented as an array, oct-tree, quad-tree, bounding volume hierarchy,...
  • parent nodes affect child nodes (translation, rotation, scale,...)
  • leaves represent atomic units (shapes, vertices, imported meshes)

Scene Manager

  • uses scene graph as a logical hierarchy
  • responsibility: communication, binding to logical components
  • Unity Engine - game objects form a hierarchy
  • Unreal Engine - components form a hierarchy
  • Godot Engine - everyting forms a hierarchy

Example: Scene Hierarchy

Input

Input Manager

  • detects events from input devices
  • polling - compare against the previous state
  • callbacks - handled by an upper SW layer

Devices

  • keyboard, touch sensor, camera, gamepad, Oculus Touch,...
  • one-axis controller - single analog state
  • two-axis controller - mouse and joystick
  • three-axis controller - accelerometer

What to consider

  • which input is active (e.g., pressed key)
  • how long has it been active
  • what state is it in and which component can react to it
  • when an input got active, what input was active before?

Input Events

Atomic events

  • key press
  • key release
  • mouse wheel move

Compound events

  • click
  • fling
  • pinch to zoom
  • double tap

Special events

  • sequences
  • actions

Special Events

Sequence

  • cheats: IDDQD, IDKFA
  • chords: combo moves in fighting games

Action

  • a button or a combination of buttons result in an action
  • there must be a button ID <-> action ID mapping

Context-sensitive input

  • several modes: walking, driving, flying

Example: Godot Input Manager

1 class InputEvent : public Resource {
2
3 public:
4 static const int DEVICE_ID_TOUCH_MOUSE;
5 void set_device(int p_device);
6 int get_device() const;
7
8 bool is_action(const StringName &p_action, bool p_exact_match = false) const;
9 bool is_action_pressed(const StringName &p_action, bool p_allow_echo = false, bool p_exact_match) const;
10 bool is_action_released(const StringName &p_action, bool p_exact_match = false) const;
11 float get_action_strength(const StringName &p_action, bool p_exact_match = false) const;
12 float get_action_raw_strength(const StringName &p_action, bool p_exact_match = false) const;
13 virtual bool is_pressed() const;
14
15 virtual bool is_match(const Ref<InputEvent> &p_event, bool p_exact_match = true) const;
16
17 virtual bool accumulate(const Ref<InputEvent> &p_event) { return false; }
18 };

Scripting Engine

Scripts

Scripts in games

  • allow rapid prototyping
  • can be reloaded at runtime
  • can separate gameplay and core components
  • can be exposed to the end users for modding

Hexen (1995) - one of the first scripted games

Example: Hexen ACS Script

1 script 137 (int dir)
2 {
3 if(!dir)
4 {
5 Floor_LowerByValue(DoorTag, 16, 64)
6 Ceiling_RaiseByValue(DoorTag, 16, 64)
7 Delay(120);
8 Floor_RaiseByValue(DoorTag, 16, 64)
9 Ceiling_LowerByValue(DoorTag, 16, 64)
10 }
11 }

Example: Arma 2 SQS Script

1 // Creates boards and markers around mission Area
2 _xPos = position (_this select 0) select 0;
3 _yPos = position (_this select 0) select 1;
4
5 _howBigA = _this select 1;
6 _howBigB = _this select 2;
7 _tablesC = _this select 3;
8 _angle = _this select 4;
9 _i = 0;
10
11 while (_i < 360) do {
12 _x = (_howBighA * (sin _i));
13 _y = (_howBigB * (cos _i));
14 _x_rot = _xPos + _x*(cos _angle) - _y*(sin _angle);
15 _y_rot = _yPos + _x*(sin _angle) + _y*(cos _angle);
16 _k = createVehicle ["Danger", [_x_rot, _y_rot, 0], [], 0, "NONE"];
17 _m = createMarker [format ["Marker" + str _i], [_x_rot, _y_rot, 0]];
18 format ["Marker" + str _i] setMarkerType "Dot";
19 _k setDir _i;
20 format ["Marker" + str _i] setMarkerDir(_i - _angle);
21 _i = _i + 360/_tablesC;
22 };

Example: Godot GDScript

1 func _process(delta):
2 # Get ball position and pad rectangles
3 var ball_pos = get_node("ball").get_pos()
4 var left_rect = Rect2(get_node("left").get_pos() - pad_size*0.5, pad_size)
5 var right_rect = Rect2(get_node("right").get_pos() - pad_size*0.5, pad_size)
6
7 # Integrate new ball position
8 ball_pos += direction*ball_speed*delta
9
10 # Flip, change direction and increase speed when touching pads
11 if ((left_rect.has_point(ball_pos) and direction.x < 0)
12 or (right_rect.has_point(ball_pos) and direction.x > 0)):
13 direction.x = -direction.x
14 ball_speed *= 1.1
15 direction.y = randf()*2.0 - 1
16 direction = direction.normalized()
17 ...

Game Engine Scripting API

  • the engine needs to communicate with the script - provided by bridging
  • bridge is a performance bottleneck, especially for per-frame calls
  • more scripting languages = more bridges to maintain
  • Marshalling - transforming memory representation of an object between two domains (different programming languages)
  • Semantic gap - descriptive difference of an object in various representations (e.g. relational database vs object-oriented structure)
  • applies to visual representations as well

Example: Atomic Game Engine JS API

1 // Duktape JS mapping
2 static void jsb_class_define_FileSystem(JSVM* vm) {
3 duk_context* ctx = vm->GetJSContext();
4 js_class_get_constructor(ctx, "Atomic", "FileSystem");
5 js_class_get_prototype(ctx, "Atomic", "FileSystem");
6 duk_pop_2(ctx);
7 js_class_get_prototype(ctx, "Atomic", "FileSystem");
8 duk_push_c_function(ctx, jsb_class_FileSystem_SetCurrentDir, 1);
9 duk_put_prop_string(ctx, -2, "setCurrentDir");
10 duk_push_c_function(ctx, jsb_class_FileSystem_CreateDir, 1);
11 duk_put_prop_string(ctx, -2, "createDir");
12 ...
13 }
14
15 // CreateDir method
16 static int jsb_class_FileSystem_CreateDir(duk_context* ctx) {
17 String __arg0 = duk_to_string(ctx, 0);
18 duk_push_this(ctx);
19 FileSystem* native = js_to_class_instance<FileSystem>(ctx, -1, 0);
20 bool retValue = native->CreateDir(__arg0);
21 duk_push_boolean(ctx, retValue ? 1 : 0);
22 return 1;
23 }

Example: Atomic Game Engine C# API

  • mapping from C++ to C# is easy
1 ATOMIC_EXPORT_API bool csb_Atomic_FileSystem_SetCurrentDir_4667(FileSystem* self, const char* pathName)
2 {
3 return self->SetCurrentDir(pathName ? String(pathName) : String::EMPTY);
4 }
5
6
7 ATOMIC_EXPORT_API bool csb_Atomic_FileSystem_CreateDir_4668(FileSystem* self, const char* pathName)
8 {
9 return self->CreateDir(pathName ? String(pathName) : String::EMPTY);
10 }
11
12
13 ATOMIC_EXPORT_API void csb_Atomic_FileSystem_SetExecuteConsoleCommands_4669(FileSystem* self, bool enable)
14 {
15 self->SetExecuteConsoleCommands(enable);
16 }

Lecture Summary

  • I know a thing or two about IDTech engine
  • I know the primary modules of game engines
  • I know how game loop works
  • I know what inconsistencies may occur during an update
  • I know what scene graph is and what elements it contains
  • I know something about scripting engines

Goodbye Quote

If You Become Light Headed From Thirst, Feel Free To Pass Out!Portal