Lecture outline Lecture outline Run Run-time game architecture - - PowerPoint PPT Presentation

lecture outline lecture outline
SMART_READER_LITE
LIVE PREVIEW

Lecture outline Lecture outline Run Run-time game architecture - - PowerPoint PPT Presentation

Lecture outline Lecture outline Run Run-time game architecture revisited time game architecture revisited Game Engine Architecture Game Engine Architecture a very complicated multilayered system a very complicated multilayered


slide-1
SLIDE 1

Game Engine Architecture Game Engine Architecture Spring 2017 Spring 2017 2

  • 2. Game engine basic support systems

. Game engine basic support systems

Juha Vihavainen Juha Vihavainen University of Helsinki University of Helsinki [Gregory, [Gregory, Ch Ch. . 5 5.1: .1: Subsystem start Subsystem start-

  • up and shut

up and shut-

  • down,

down, 7.1 7.1 -

  • 7.3

7.3 Game loops Game loops

  • Ch. 15.7
  • Ch. 15.7 Events and message

Events and message-

  • passing

passing] ] [ [McShaffry McShaffry, Ch. 5: , Ch. 5: Game initialization and shutdown Game initialization and shutdown, ,

  • Ch. 7:
  • Ch. 7: Controlling the main loop + cooperative multitasking

Controlling the main loop + cooperative multitasking] ]

Lecture outline Lecture outline

  • Run

Run-time game architecture revisited time game architecture revisited

  • a very complicated multilayered system

a very complicated multilayered system

  • subsystems within subsystems

subsystems within subsystems

  • Low

Low-level engine systems level engine systems

  • need subsystem initialization and shutdown

need subsystem initialization and shutdown

  • use different implementation strategies: static

use different implementation strategies: static

  • vs. dynamic objects
  • vs. dynamic objects

Controlling the Controlling the main loop main loop

  • Controlling the

Controlling the main loop main loop

  • simulation of a game world and its entities

simulation of a game world and its entities

  • real

real-time discrete time discrete updates updates of game entities

  • f game entities
  • co

co-operative multitasking

  • perative multitasking system for games

system for games

21.2.2017 21.2.2017 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki 2 21.2.2017 21.2.2017 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki 3

slide-2
SLIDE 2

Run Run-time game architecture (simpl.from Gregory, 2014) time game architecture (simpl.from Gregory, 2014)

  • Game

Game-specific specific subsystems subsystems

  • vehicles

vehicles, , weapons weapons, , cameras cameras, , game game-specific specific rendering rendering (water water), AI, etc. ), AI, etc.

  • Gameplay

Gameplay foundations foundations

  • game

game flow flow system/FSM system/FSM, , scripting scripting, event/msg , event/msg system system, , loading/streaming loading/streaming, ,

Question: where is the game loop?

real real-time time agent agent-based based simulation simulation

  • Game model & view support

Game model & view support: skeletal animation, rendering : skeletal animation, rendering engine engine (scene scene graphs graphs, , particles particles, , lighting lighting), physics/collisions, audio , IO, ), physics/collisions, audio , IO, online

  • nline multiplayer

multiplayer

  • Game

Game assets assets (multimedia (multimedia resources resources)

  • 3D

3D models models, , textures textures, , materials materials, , fonts fonts, , physics physics parameters parameters, , world world maps maps, etc. , etc.

  • Core

Core programming programming systems systems (for any large (for any large-scale C++ application/system) scale C++ application/system) start/shut start/shut, asserts , asserts, , testing testing, , mem mem. . alloc alloc., ., string/hashed string/hashed ids, ids, profiling profiling, , math math, ,

  • start/shut

start/shut, asserts , asserts, , testing testing, , mem mem. . alloc alloc., ., string/hashed string/hashed ids, ids, profiling profiling, , math math, , debugging debugging, , logging logging, , engine engine config config., ., RTTI/refl./serialization RTTI/refl./serialization, , object

  • bject ids

ids, files.. , files..

  • Platform

Platform-independence independence layer layer: : detection detection, , file file system system, , timer timer, , threading threading, , wrappers wrappers.. ..

  • 3rd

3rd-party SDKs party SDKs: DirectX/OpenGL/Vulkan, PhysX/Bullet/ODE.., : DirectX/OpenGL/Vulkan, PhysX/Bullet/ODE.., Boost Boost++, ++, STLPort STLPort, Kynapse AI, , Kynapse AI, Havok Havok animation animation

21.2.2017 21.2.2017 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki 5

Engine core support systems Engine core support systems

  • Starting up and shutting down the engine

Starting up and shutting down the engine

  • interdependencies between subsystems

interdependencies between subsystems => => ordering of

  • rdering of

initializations and final release (e.g., file system, logging) initializations and final release (e.g., file system, logging)

  • Handling access to file systems

Handling access to file systems

  • wrapping native file system for portability

wrapping native file system for portability

  • support streaming (loading data on

support streaming (loading data on-

  • the

the-

  • fly/on background)

fly/on background)

  • Loading and providing access to different kinds of assets (files)

Loading and providing access to different kinds of assets (files)

  • meshes, textures, animations, audio (e.g., in UPK package)

meshes, textures, animations, audio (e.g., in UPK package)

  • meshes, textures, animations, audio (e.g., in UPK package)

meshes, textures, animations, audio (e.g., in UPK package)

  • Development tools for game programming

Development tools for game programming

  • debugging, logging, RTTI, low

debugging, logging, RTTI, low-

  • level resource handling

level resource handling (memory, strings, files), math, object handles.. (memory, strings, files), math, object handles..

21.2.2017 21.2.2017 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki 6

Initialization of subsystems Initialization of subsystems

Consider the following approach: Consider the following approach:

class GRenderEngine { class GRenderEngine { // // game renderer game renderer (or whatever

  • r whatever)

public: public: GRenderEngine () GRenderEngine () { / { /* start up the render manager start up the render manager */ } / } GRenderEngine () GRenderEngine () { / { /* start up the render manager start up the render manager */ } / } ~ GRenderEngine () ~ GRenderEngine () { / { /* * shut down the manager shut down the manager */ } / } . . . . . . }; }; . . . . . . GRenderEngine Renderer; // GRenderEngine Renderer; // note singleton = global instance note singleton = global instance

  • This

This Renderer Renderer is created before and destroyed after the is created before and destroyed after the main () main ()

  • But remember that constructors and destructors of global objects

But remember that constructors and destructors of global objects in in different compilation units different compilation units are executed in unpredictable order are executed in unpredictable order => => subsystems depending and using each other may not work subsystems depending and using each other may not work

21.2.2017 21.2.2017 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki 7

Use Use construction construction on demand, with

  • n demand, with static

static locals locals

class class GRenderEngine { . . . GRenderEngine { . . . // // game game render render manager manager static GRenderEngine& static GRenderEngine& Get Get () { () { static GRenderEngine static GRenderEngine instance instance; ; return return instance instance; }

Ensure the initializations Ensure the initializations Construction done once only Construction done once only

} GRenderEngine GRenderEngine () { () { GraphicsDeviceMgr:: GraphicsDeviceMgr::Get Get (); TextureMgr (); TextureMgr::Get ::Get (); (); /* now now set set up up the the render render manager manager . . . . . . */ / } ~ GRenderEngine ~ GRenderEngine () { () { /* shut shut down down the the manager manager */ / } }; };

Ensure the initializations Ensure the initializations

}; };

  • Order

Order of

  • f destruction

destruction depends depends on

  • n calling

calling constructors constructors in the in the right right order

  • rder
  • I

Innocent nnocent-

  • looking

looking Get Get() () allocates allocates and and initializes initializes heavy heavy-

  • weight

weight objects

  • bjects
  • S

Still till cannot cannot prevent prevent some some destructor destructor calling calling on a

  • n a destroyed

destroyed singleton singleton

21.2.2017 21.2.2017 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki 8

slide-3
SLIDE 3

A simple approach that works (1/2) A simple approach that works (1/2)

class GRenderEngine { class GRenderEngine { // // game render manager game render manager public: public: void void startUp startUp () () { /* { /* start up the render manager start up the render manager */ } */ } void void shutDown shutDown () () { /* { /* shut down the manager shut down the manager */ } */ } void void shutDown shutDown () () { /* { /* shut down the manager shut down the manager */ } */ } GRenderEngine () GRenderEngine () { /* { /* initial values for empty state initial values for empty state */ } */ } ~GRenderEngine () ~GRenderEngine () { /* { /* do do (mostly mostly) nothing nothing */ } . . . */ } . . . }; }; class GPhysicsEngine class GPhysicsEngine { / { /* similar . . . similar . . . */ }; / }; class GAnimationMachine { / class GAnimationMachine { /* similar . . . similar . . . */ }; / }; * * class GTextureManager class GTextureManager { / { /* similar . . . similar . . . */ }; / }; class GVideoManager class GVideoManager { / { /* similar . . . similar . . . */ }; / }; class GMemoryManager class GMemoryManager { / { /* similar . . . similar . . . */ }; / }; class GFileSystemManager { / class GFileSystemManager { /* similar . . . similar . . . */ }; . . . / }; . . .

21.2.2017 21.2.2017 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki 9

Simple approach that works (2/2) Simple approach that works (2/2)

GRenderEngine Renderer; GRenderEngine Renderer; GPhysicsEngine PhysicsEngine; . . . GPhysicsEngine PhysicsEngine; . . . // // etc. etc. GMemoryManager MemoryManager; GMemoryManager MemoryManager; GFileSystemManager FileSystemManager; GFileSystemManager FileSystemManager; . . . . . .

static instances

GFileSystemManager FileSystemManager; GFileSystemManager FileSystemManager; . . . . . . int main () { int main () { Renderer.startUp (); Renderer.startUp (); // // start all subsystems start all subsystems in order in order PhysicsEngine.startUp (); PhysicsEngine.startUp (); . . . . . . GameRun (); GameRun (); . . . . . . // // run the game run the game PhysicsEngine.shutDown (); PhysicsEngine.shutDown (); // // close down systems close down systems in reverse in reverse PhysicsEngine.shutDown (); PhysicsEngine.shutDown (); // // close down systems close down systems in reverse in reverse Renderer.shutDown (); Renderer.shutDown (); }

  • [Gregory]: the solution is crude but simple and explicit; and easy

[Gregory]: the solution is crude but simple and explicit; and easy to implement, debug, and maintain to implement, debug, and maintain

21.2.2017 21.2.2017 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki 10 10

Sample initialization: Ogre3D Sample initialization: Ogre3D

OgreRoot.h OgreRoot.h // // header file header file class _OgreExport Root: public class _OgreExport Root: public Singleton Singleton <Root> { <Root> { // < // < some code omitted some code omitted... > ... > OverlayElementFactory OverlayElementFactory * mPanelFactory; mPanelFactory; OverlayElementFactory OverlayElementFactory * mBorderPanelFactory; mBorderPanelFactory;

defines singleton entry point and checks

// // declare singletons declare singletons LogManager LogManager * mLogManager; mLogManager; ControllerManager ControllerManager * mControllerManager; mControllerManager; SceneManagerEnumerator SceneManagerEnumerator * mSceneManagerEnum; mSceneManagerEnum; SceneManager SceneManager * mCurrentSceneManager; mCurrentSceneManager; DynLibManager DynLibManager * mDynLibManager; mDynLibManager; ArchiveManager ArchiveManager * mArchiveManager; mArchiveManager; OverlayElementFactory OverlayElementFactory * mBorderPanelFactory; mBorderPanelFactory; OverlayElementFactory OverlayElementFactory * mTextAreaFactory; mTextAreaFactory; OverlayManager OverlayManager * mOverlayManager; mOverlayManager; FontManager FontManager * mFontManager; mFontManager; ArchiveFactory ArchiveFactory * * mZipArchiveFactory; mZipArchiveFactory; ArchiveFactory ArchiveFactory * * mFileSystemArchiveFactory; mFileSystemArchiveFactory; ResourceGroupManager ResourceGroupManager * mResourceGroupManager; mResourceGroupManager; ResourceBackgroundQueue ResourceBackgroundQueue *

21.2.2017 21.2.2017 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki 11 11

MaterialManager MaterialManager * mMaterialManager; mMaterialManager; MeshManager MeshManager * mMeshManager; mMeshManager; ParticleSystemManager ParticleSystemManager * mParticleManager; mParticleManager; SkeletonManager SkeletonManager * mSkeletonManager; mSkeletonManager; ResourceBackgroundQueue ResourceBackgroundQueue * mResourceBackgroundQueue; mResourceBackgroundQueue; ShadowTextureManager ShadowTextureManager * mShadowTextureManager; mShadowTextureManager; // // etc. etc. }; };

Sample initialization: Ogre3D (cont.) Sample initialization: Ogre3D (cont.)

OgreRoot.cpp OgreRoot.cpp // // translation unit translation unit Root::Root (const String& pluginFileName, Root::Root (const String& pluginFileName, const String& configFileName, const String& configFileName, // // create log manager and default log file if there create log manager and default log file if there // // is no log manager yet is no log manager yet

Uses manager objects, but could be possible to hide these in .cpp files?

const String& configFileName, const String& configFileName, const String& logFileName) : const String& logFileName) : mLogManager (0), mLogManager (0), mCurrentFrame (0), mCurrentFrame (0), mFrameSmoothingTime (0.0f), mFrameSmoothingTime (0.0f), mNextMovableObjectTypeFlag (1), mNextMovableObjectTypeFlag (1), mIsInitialised (false) mIsInitialised (false) { // { // superclass will do singleton checking superclass will do singleton checking // // is no log manager yet is no log manager yet if (LogManager::getSingletonPtr () == 0) { if (LogManager::getSingletonPtr () == 0) { mLogManager = new LogManager (); mLogManager = new LogManager (); mLogManager mLogManager->createLog (logFileName, true, >createLog (logFileName, true, true); true); } mDynLibManager = new DynLibManager (); mDynLibManager = new DynLibManager (); mArchiveManager = new ArchiveManager (); mArchiveManager = new ArchiveManager (); mResourceGroupManager = new mResourceGroupManager = new

21.2.2017 21.2.2017 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki 12 12

. . . . mActiveRenderer = 0; mActiveRenderer = 0; mVersion = . . . // mVersion = . . . // construct version name construct version name mConfigFileName = configFileName; mConfigFileName = configFileName; mResourceGroupManager = new mResourceGroupManager = new ResourceGroupManager (); ResourceGroupManager (); mResourceBackgroundQueue = new mResourceBackgroundQueue = new ResourceBackgroundQueue (); ResourceBackgroundQueue (); // // and so on.. and so on..

slide-4
SLIDE 4

Formalizing the " Formalizing the "module module" concept " concept

template <typename M> struct Module { template <typename M> struct Module { // // model of a module model of a module public: public: virtual void startUp () virtual void startUp () = 0 = 0 { { // // required for module required for module Utl::Log << "Module: "+name+" start up. Utl::Log << "Module: "+name+" start up.\n"; // n"; // illustrative only illustrative only Utl::Log << "Module: "+name+" start up. Utl::Log << "Module: "+name+" start up.\n"; // n"; // illustrative only illustrative only } } virtual void shutDown () virtual void shutDown () = 0 = 0 { { // // required for module required for module Utl::Log << "Module: "+name+" shut down. Utl::Log << "Module: "+name+" shut down.\n"; n"; } Module () : name (typeid (M).name ()) { Utl::Log::Get (); } Module () : name (typeid (M).name ()) { Utl::Log::Get (); } virtual ~Module () = 0 {} virtual ~Module () = 0 {} // // by convention by convention Inits logger Inits logger virtual ~Module () = 0 {} virtual ~Module () = 0 {} // // by convention by convention const std::string name; const std::string name; protected: protected: using super = Module; using super = Module; // // for for "super::startUp ();" "super::startUp ();" }; };

21.2.2017 21.2.2017 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki 13 13

Formalizing the " Formalizing the "module module" concept (cont.) " concept (cont.)

class class Renderer Renderer : public Module < : public Module <Renderer Renderer> { > { public: public: virtual void startUp () virtual void startUp () override

  • verride {

super::startUp (); super::startUp (); // // call call Module Module default operations default operations super::startUp (); super::startUp (); // // call call Module Module default operations default operations /* start up code for renderer . . start up code for renderer . . */ / } virtual void shutDown () virtual void shutDown () override

  • verride {

/* shup down code for renderer . . shup down code for renderer . . */ / super::shutDown (); super::shutDown (); // // call call Module Module default operations default operations } // . . . } // . . .

Curiously recurring template pattern: custom base class uses properties of derived.

} // . . . } // . . . }; };

  • Now we

Now we could could build a clever build a clever dependence dependence analysis and management analysis and management system for global object initialization; Gregory: system for global object initialization; Gregory: don't bother don't bother

21.2.2017 21.2.2017 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki 14 14

Initialization for a Windows game (in a Initialization for a Windows game (in a proper proper order)

  • rder)

(McShaffry, 2012) (McShaffry, 2012)

  • check system resources: hard drive space, memory, IO

check system resources: hard drive space, memory, IO devices devices check the CPU speed => customize the engine to fit.. check the CPU speed => customize the engine to fit..

  • check the CPU speed => customize the engine to fit..

check the CPU speed => customize the engine to fit..

  • initialize the main random number generator

initialize the main random number generator

  • load programmer's options for debugging purposes

load programmer's options for debugging purposes

  • initialize your memory/resource cache

initialize your memory/resource cache

  • create your window

create your window initialize the audio system initialize the audio system

  • initialize the audio system

initialize the audio system

  • load the player's game options and saved game files

load the player's game options and saved game files

  • create your drawing surface

create your drawing surface

  • perform initialization for game system: physics, AI, etc

perform initialization for game system: physics, AI, etc.

21.2.2017 21.2.2017 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki 15 15

Notes on Notes on Windows Windows initialization initialization

  • Any part of the initialization of modules may fail

Any part of the initialization of modules may fail

  • Must check that initializations succeeded

Must check that initializations succeeded

  • if so, must shut down, of course

if so, must shut down, of course must also report the user about the failure: what can be done must also report the user about the failure: what can be done

  • must also report the user about the failure: what can be done

must also report the user about the failure: what can be done (if anything) (if anything)

  • Below, a hypothetical init for a

Below, a hypothetical init for a Windows Windows game (McShaffry, 2012) game (McShaffry, 2012)

  • Treat modules as dynamic objects => have explicit life cycle

Treat modules as dynamic objects => have explicit life cycle

GVideoSystem::GVideoSystem () { GVideoSystem::GVideoSystem () { bool initSucceeded = true; bool initSucceeded = true; // // being optimistic.. being optimistic.. // // . . some initialization code here . . . . some initialization code here . . if (! initSucceeded) if (! initSucceeded) // // throw a custom exception throw a custom exception throw InitError ("Video system init failed"); // throw InitError ("Video system init failed"); // or specific..

  • r specific..

} } // // no no new new allowed here allowed here

21.2.2017 21.2.2017 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki 16 16

slide-5
SLIDE 5

GVideoSystem GVideoSystem * VideoSystem; VideoSystem; // = // = nullptr nullptr, by default for static objects , by default for static objects GAudioSystem GAudioSystem * Audiosystem; Audiosystem; // = // = nullptr nullptr, by default , by default

. . . . . .

int main () { int main () { int returnCode = 0; int returnCode = 0; // // means OK means OK try { Audiosystem = new GAudioSystem; try { Audiosystem = new GAudioSystem; VideoSystem = new GVideoSystem; VideoSystem = new GVideoSystem; VideoSystem = new GVideoSystem; VideoSystem = new GVideoSystem;

. . . . . .

RunGame (); RunGame (); } catch (InitError const& e) { } catch (InitError const& e) { e.informUser (); e.informUser (); // // depends on error depends on error returnCode = 1; returnCode = 1; // // means failure means failure

} . . . } . . .

// // other exceptions

  • ther exceptions

} . . . } . . .

// // other exceptions

  • ther exceptions

delete VideoSystem; delete VideoSystem; // // in reverse order in reverse order delete Audiosystem; delete Audiosystem; // // set set nullptr nullptr (maybe maybe) return returnCode; return returnCode; }

21.2.2017 21.2.2017 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki 17 17

More detailed init for a More detailed init for a Windows Windows game (McShaffry) game (McShaffry)

  • detect multiple instances of the application (an impatient player)

detect multiple instances of the application (an impatient player)

  • load the language DLL

load the language DLL

  • check secondary storage space, RAM, and VRAM

check secondary storage space, RAM, and VRAM calculate the CPU speed calculate the CPU speed

  • calculate the CPU speed

calculate the CPU speed

  • parse command lines

parse command lines

  • load the game's resource cache

load the game's resource cache

  • load strings that will be presented to the player

load strings that will be presented to the player

  • create the script manager (e.g.,

create the script manager (e.g., Lua Lua) )

  • create the game's event manager (discussed later)

create the game's event manager (discussed later) create the game's event manager (discussed later) create the game's event manager (discussed later)

  • use the script manager to load initial game options

use the script manager to load initial game options

  • initialize DirextX, and application window, and the D3D device

initialize DirextX, and application window, and the D3D device

  • set the save and tmp

set the save and tmp-

  • file directory, preload selected resources

file directory, preload selected resources

  • create the game logic, and game views (player/network)

create the game logic, and game views (player/network)

21.2.2017 21.2.2017 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki 18 18

The "rendering loop" The "rendering loop"

  • GUI systems often use

GUI systems often use rectangle invalidation rectangle invalidation and try to redraw only and try to redraw only small portions of the screen (to minimize the number of drawn pixels) small portions of the screen (to minimize the number of drawn pixels)

  • But in 3D games, the entire screen contents (pixels) keep changing

But in 3D games, the entire screen contents (pixels) keep changing But in 3D games, the entire screen contents (pixels) keep changing But in 3D games, the entire screen contents (pixels) keep changing

  • verall and must be redrawn again and again in rapid succession
  • verall and must be redrawn again and again in rapid succession
  • A very simplified version of a rendering loop (Gregory, p. 340):

A very simplified version of a rendering loop (Gregory, p. 340): while (! quit) { while (! quit) { // // hypothetical 1st person shooter hypothetical 1st person shooter updateCamera (); updateCamera (); // // based on interactive inputs or path based on interactive inputs or path updateSceneElements (); updateSceneElements (); // // positions, orientation, visual state, etc. positions, orientation, visual state, etc. updateSceneElements (); updateSceneElements (); // // positions, orientation, visual state, etc. positions, orientation, visual state, etc. renderScene (); renderScene (); // // into off into off-screen frame buffer screen frame buffer swapBuffers (); swapBuffers (); // // copy contents, or switch ptrs copy contents, or switch ptrs }

21.2.2017 21.2.2017 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki 19 19

A more bit complicated game loop ( A more bit complicated game loop (Pong Pong)

int main () { int main () { initGame initGame (); (); while (true) { while (true) { // // main game loop main game loop readHumanInterfaceDevices readHumanInterfaceDevices (); ();

(GEA, pp. 341 (GEA, pp. 341-342) 342)

readHumanInterfaceDevices readHumanInterfaceDevices (); (); if ( if (quitButtonPressed quitButtonPressed ()) break; ()) break; movePaddles movePaddles (); (); // // the game logic the game logic (flow flow) moveBall moveBall (); (); collideAndBounceBall collideAndBounceBall (); (); if ( if (ballImpactedSide ballImpactedSide (LeftPlayer) { (LeftPlayer) { incrementScore incrementScore (RightPlayer); (RightPlayer); resetBall resetBall (); } (); }

The solution does The solution does not scale up not scale up

incrementScore incrementScore (RightPlayer); (RightPlayer); resetBall resetBall (); } (); } else if ( else if (ballImpactedSide ballImpactedSide (RightPlayer) { (RightPlayer) { incrementScore incrementScore (LeftPlayer); (LeftPlayer); resetBall resetBall (); } (); } renderPlayField renderPlayField (); (); }

21.2.2017 21.2.2017 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki 20 20

slide-6
SLIDE 6

Using the legacy Using the legacy Windows Windows "message message pump pump" ( " (simplified simplified)

  • Firstly, need to handle events/msgs from the OS (

Firstly, need to handle events/msgs from the OS (Windows Windows platform) platform) while (true) { while (true) { // // get from OS and send to the get from OS and send to the "application application" // // first, service any and all pending Windows messages first, service any and all pending Windows messages

(GEA, p. 343) (GEA, p. 343)

MSG msg; MSG msg; // msg.message = // msg.message = message kind message kind (tag tag) while (PeekMessage (&msg, NULL, 0, 0) > 0) { // while (PeekMessage (&msg, NULL, 0, 0) > 0) { // or whatever

  • r whatever

TranslateMessage (&msg); // TranslateMessage (&msg); // keyboard input into char msgs keyboard input into char msgs DispatchMessage (&msg); // DispatchMessage (&msg); // send to a send to a "window procedure window procedure" } // // no more Windows messages, so no more Windows messages, so

Should handle end Should handle end-of

  • f-

program (WM_QUIT).. program (WM_QUIT).. (GEA, p. 343) (GEA, p. 343)

// // no more Windows messages, so no more Windows messages, so RunOneIterationOfGameLoop RunOneIterationOfGameLoop () (); // ; // possible some delay here.. possible some delay here.. }

  • The game may freeze up when resizing/dragging the game's window

The game may freeze up when resizing/dragging the game's window (since OS/IO messages are given higher priority) (since OS/IO messages are given higher priority)

21.2.2017 21.2.2017 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki 21 21

" "Pump Pump" still needed for game " still needed for game engines on engines on Windows Windows/PC PC

Callback Callback-driven framework (1/2) driven framework (1/2)

  • Most of game subsystems and 3rd

Most of game subsystems and 3rd-

  • party components are

party components are libraries libraries

  • Instead, a

Instead, a framework framework provides an application skeleton with a ready provides an application skeleton with a ready-

  • defined flow of control; the user just completes it with missing parts

defined flow of control; the user just completes it with missing parts For example, For example, Ogre3D Ogre3D provides a framework provides a framework-

  • like functionality

like functionality

  • For example,

For example, Ogre3D Ogre3D provides a framework provides a framework-

  • like functionality

like functionality

  • the main rendering loop is already written as a part of this library

the main rendering loop is already written as a part of this library

  • user "

user "callback callback" methods are called before/after rendering a scene " methods are called before/after rendering a scene

  • The internal game loop of

The internal game loop of Ogre Ogre

while (true) { . . . while (true) { . . . // // in pseudocode, here in pseudocode, here for listener in frameListeners do listener.frameStarted for listener in frameListeners do listener.frameStarted ( . . ); ( . . );

(GEA, pp. 344) (GEA, pp. 344)

for listener in frameListeners do listener.frameStarted for listener in frameListeners do listener.frameStarted ( . . ); ( . . ); renderCurrentScene (); renderCurrentScene (); for listener in frameListeners do listener.frameEnded for listener in frameListeners do listener.frameEnded ( . . ); ( . . ); finalizeSceneAndSwapBuffers (); . . . finalizeSceneAndSwapBuffers (); . . . }

21.2.2017 21.2.2017 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki 22 22

Callback Callback-driven framework (2/2) driven framework (2/2)

class GameFrameListener : public Ogre::FrameListener { class GameFrameListener : public Ogre::FrameListener { public: public: virtual void frameStarted (FrameEvent const& event) { virtual void frameStarted (FrameEvent const& event) {

(GEA, p. 345) (GEA, p. 345)

virtual void frameStarted (FrameEvent const& event) { virtual void frameStarted (FrameEvent const& event) { // // service subsystems service subsystems: IO, physics, AI, .. IO, physics, AI, .. pollJoyPad (event); pollJoyPad (event); updatePlayerControls (event); updatePlayerControls (event); updateDynamicsSimulation (event); updateDynamicsSimulation (event); resolveCollisions (event); updateCamera (event); . . . resolveCollisions (event); updateCamera (event); . . . } }

Provides rovides times since last times since last event/last event/last frame frame .. ..

} } virtual void frameEnded (FrameEvent const& event) { virtual void frameEnded (FrameEvent const& event) { drawHUD (event); . . . drawHUD (event); . . . // // after 3D scene rendered after 3D scene rendered } } }

21.2.2017 21.2.2017 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki 23 23

A general event A general event-based updating (preview) based updating (preview)

  • An

An event event: any interesting change of the state of the game or its : any interesting change of the state of the game or its environment, e.g. environment, e.g.

  • the player pressing a button on the joypad

the player pressing a button on the joypad an explosion going off => an explosion going off => many nearby game entities affected many nearby game entities affected

(GEA, p. 345) (GEA, p. 345)

  • an explosion going off =>

an explosion going off => many nearby game entities affected many nearby game entities affected

  • an enemy spotting the player

an enemy spotting the player-

  • character => change in behaviour

character => change in behaviour

  • Somewhat similar to event systems in GUI libraries

Somewhat similar to event systems in GUI libraries

  • Many game engines provide some

Many game engines provide some event system event system where subsystems where subsystems

  • can register interest on particular events/event types

can register interest on particular events/event types can handle those events when they occur/are delivered can handle those events when they occur/are delivered

  • can handle those events when they occur/are delivered

can handle those events when they occur/are delivered

  • An event system can be used to service game subsystems

An event system can be used to service game subsystems -

  • but

but

  • may need events to be delayed/queued for

may need events to be delayed/queued for later later delivery delivery

  • an event handler may even post events to itself (for future need)

an event handler may even post events to itself (for future need)

21.2.2017 21.2.2017 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki 24 24

slide-7
SLIDE 7

Sample design for main game loop (traditional) Sample design for main game loop (traditional)

  • Autonomous game entities come to life, stomp around, and die off

Autonomous game entities come to life, stomp around, and die off

  • For a specific game, a simple design could have been as follows

For a specific game, a simple design could have been as follows void MyGameUpdate (float elapsedTime) { void MyGameUpdate (float elapsedTime) { // . . // . . we have many activities going on we have many activities going on calculateAI calculateAI (elapsedTime); (elapsedTime); detectTriggerFire detectTriggerFire (elapsedTime); (elapsedTime); reticulateSplines reticulateSplines (elapsedTime); (elapsedTime); runPhysicsSimulation runPhysicsSimulation (elapsedTime); (elapsedTime); moveKinematicsObjects moveKinematicsObjects (elapsedTime); (elapsedTime);

Number and order Number and order are hard are hard-coded coded Running low Running low-level level subsystems, here subsystems, here

moveKinematicsObjects moveKinematicsObjects (elapsedTime); (elapsedTime); processIncomingCommands processIncomingCommands (elapsedTime); // (elapsedTime); // or whatever

  • r whatever

}

  • These routines

These routines manage manage lists of objects in a very specialized way lists of objects in a very specialized way

  • not very flexible, and does not easily scale up:

not very flexible, and does not easily scale up: can do better can do better!

21.2.2017 21.2.2017 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki 25 25 From McShaffry, From McShaffry, Game Coding Complete Game Coding Complete, , 3rd edition (2009). 3rd edition (2009).

Co Co-operative

  • perative multitasking

multitasking for game loops for game loops

  • Game activities (or "

Game activities (or "tasks tasks") usually carry on over multiple frames ") usually carry on over multiple frames

  • n each step, each task gets a little time to run (to update its state)
  • n each step, each task gets a little time to run (to update its state)
  • CPU time is given in a

CPU time is given in a round round-

  • robin

robin fashion fashion we can we can think think them as kind of threads/processes them as kind of threads/processes

  • we can

we can think think them as kind of threads/processes them as kind of threads/processes

  • Co

Co-

  • operative
  • perative means that

means that

  • each task is responsible for releasing control back to the main loop

each task is responsible for releasing control back to the main loop (so that other tasks can have their turn) (so that other tasks can have their turn)

  • if one task goes into an infinite loop, the system becomes locked!

if one task goes into an infinite loop, the system becomes locked!

  • Co

Co-

  • operative system is simple to design and can be extremely efficient
  • perative system is simple to design and can be extremely efficient

Co Co-

  • operative system is simple to design and can be extremely efficient
  • perative system is simple to design and can be extremely efficient
  • basic principles are easy; (almost) no mutual exclusion problems

basic principles are easy; (almost) no mutual exclusion problems

  • seems to suit very well game engine programming

seems to suit very well game engine programming

  • The following solution is based on the design from (McShaffry, 2012)

The following solution is based on the design from (McShaffry, 2012)

  • nly a
  • nly a sketch

sketch -

  • not all details or data structures (

not all details or data structures ( => => project project) )

21.2.2017 21.2.2017 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki 26 26

A simple tasking model A simple tasking model

  • If "everything" is run as a task, the system could

If "everything" is run as a task, the system could almost almost look like look like int main () { int main () { if (createTasks ()) if (createTasks ()) // // create activities create activities runTasks (); runTasks (); // // schedule tasks as required schedule tasks as required runTasks (); runTasks (); // // schedule tasks as required schedule tasks as required shutDownTasks (); shutDownTasks (); }

  • But game tasks are not totally independent

But game tasks are not totally independent

  • calling

calling onUpdate()

  • nUpdate() on some task may destroy other tasks, or
  • n some task may destroy other tasks, or

can even indirectly cause the task itself to be destroyed can even indirectly cause the task itself to be destroyed

  • Say, think about a hand grenade exploding

Say, think about a hand grenade exploding => =>

  • query for game objects in a certain range

query for game objects in a certain range

  • cause all these objects to be destroyed (in a nice fireball)

cause all these objects to be destroyed (in a nice fireball)

  • the grenade itself would be included in that range

the grenade itself would be included in that range

21.2.2017 21.2.2017 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki 27 27

Outline for game object multitasking Outline for game object multitasking

class GTask { // class GTask { // game task game task: ongoing activity; e.g. action part of game entity

  • ngoing activity; e.g. action part of game entity

public: public: virtual void onUpdate (int elapsedMilliSecs) = 0; . . . virtual void onUpdate (int elapsedMilliSecs) = 0; . . . }; }; We create objects inheriting from We create objects inheriting from GTask GTask and put them in a main and put them in a main task list task list

  • We create objects inheriting from

We create objects inheriting from GTask GTask and put them in a main and put them in a main task list task list using GTasks = std::list <TaskPtr>; using GTasks = std::list <TaskPtr>; // TaskPtr // TaskPtr is is reference counted reference counted GTasks Tasks; GTasks Tasks; // // a singleton , again a singleton , again

  • Every game cycle, we traverse this list and call

Every game cycle, we traverse this list and call onUpdate

  • nUpdate for each task

for each task void updateAllTasks (int deltaMillisecs) { void updateAllTasks (int deltaMillisecs) { // // simplified simplified for each task in Tasks do for each task in Tasks do // // pseudocode here pseudocode here

defined elsewhere

for each task in Tasks do for each task in Tasks do // // pseudocode here pseudocode here task task->onUpdate (deltaMillisecs);

  • nUpdate (deltaMillisecs);

// // game actions game actions }

  • Still problem with dependencies: a task is destroyed by actions of another

Still problem with dependencies: a task is destroyed by actions of another

  • how to safely destroy and remove it from the

how to safely destroy and remove it from the shared shared task list? task list?

21.2.2017 21.2.2017 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki 28 28

slide-8
SLIDE 8

Another service: creating Another service: creating sequences sequences of tasks

  • f tasks
  • One

One Task Task can depend on another to complete before beginning can depend on another to complete before beginning

  • A property

A property next next can be used set up such dependency: a task first waits can be used set up such dependency: a task first waits for another task to finish, and is then (automatically) activated for another task to finish, and is then (automatically) activated

  • For example (a hypothetical game and situation)

For example (a hypothetical game and situation) TaskPtr walk (new WalkTask (George, door)); TaskPtr walk (new WalkTask (George, door)); TaskPtr openDoor (new AnimTask (OPEN_DOOR, George, door)); TaskPtr openDoor (new AnimTask (OPEN_DOOR, George, door)); TaskPtr drawSword (new AnimTask (DRAW_WEAPON, George, TaskPtr drawSword (new AnimTask (DRAW_WEAPON, George, sword)); sword)); TaskPtr goBerserk (new CombatTask (BERSERK, George)); TaskPtr goBerserk (new CombatTask (BERSERK, George)); ((walk ((walk->next = openDoor) >next = openDoor)->next = drawSword) >next = drawSword)->next = goBerserk; >next = goBerserk; GTask::activate (walk); GTask::activate (walk); // // now start the first task now start the first task

21.2.2017 21.2.2017 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki 29 29

(1.) (2.) (3.) (4.)

  • every "updatable" entity with state can inherit from

every "updatable" entity with state can inherit from GTask GTask

class GTask { class GTask { // // game task game task public: public: virtual void onUpdate (int elapsedMillisecs) = 0; virtual void onUpdate (int elapsedMillisecs) = 0;

class class GTask GTask : a sketch of implementation : a sketch of implementation

virtual void onUpdate (int elapsedMillisecs) = 0; virtual void onUpdate (int elapsedMillisecs) = 0; virtual void onInitialize () {} virtual void onInitialize () {} // // do before first update do before first update only

  • nly

virtual void kill (); virtual void kill (); // // mark mark to die to die ( (in in next cycle next cycle) ) virtual void togglePaused () ; virtual void togglePaused () ; // // stop/run stop/run ( (staying in task list staying in task list) ) TaskPtr TaskPtr next next; ; // // the next task after this the next task after this ( (if any if any) ) void setOrder (unsigned); void setOrder (unsigned); . . . . . . // // set ordering property set ordering property ( (-

  • 1)

1) GTask (TaskPtr GTask (TaskPtr next next = nullptr); = nullptr); . . . . . . GTask (TaskPtr GTask (TaskPtr next next = nullptr); = nullptr); . . . . . . private: . . . private: . . . unsigned order; unsigned order; // // ordering

  • rdering (

(place place) ) in task list in task list GState status; GState status; // // task status: created, waiting.. task status: created, waiting.. }; };

Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki 30 30

GTask continued: static member functions GTask continued: static member functions

  • inherit from

inherit from GTask GTask; a task can carry along game components ; a task can carry along game components

  • game objects, audio objects, user

game objects, audio objects, user-interface objects, etc. interface objects, etc.

  • after we create a task, we must separately

after we create a task, we must separately activate activate it it task tasks s can be "run" in a specific order (determined by can be "run" in a specific order (determined by order

  • rder)

)

  • task

tasks s can be "run" in a specific order (determined by can be "run" in a specific order (determined by order

  • rder)

) class GTask { class GTask { // // game task game task public: public: // . . . // . . . instance properties instance properties static void activate (TaskPtr, unsigned order = static void activate (TaskPtr, unsigned order = -1); // 1); // -1: : no order no order static void deactivate (TaskPtr); static void deactivate (TaskPtr); // // remove from actives remove from actives

  • Note. No manager object used here.

static void deactivate (TaskPtr); static void deactivate (TaskPtr); // // remove from actives remove from actives static GTasks activeTasks (); static GTasks activeTasks (); // // get a copy to inspect get a copy to inspect static void updateAllTasks (int elapsedMillisecs); // static void updateAllTasks (int elapsedMillisecs); // one cycle

  • ne cycle

// . . . // . . . }; };

21.2.2017 21.2.2017 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki 31 31

GTask:: GTask::updateAllTasks ( updateAllTasks (in pseudocode in pseudocode)

for each task in for each task in the the Tasks Tasks do do // // do do safe safe iteration iteration if task if task is now updated is now updated first time then first time then task task->onInitialize

  • nInitialize ()

() // // do do any any start start up up if task is running if task is running then then // // activated, not killed activated, not killed if task is running if task is running then then // // activated, not killed activated, not killed if task is paused then continue if task is paused then continue // // skip to next skip to next task task->onUpdate

  • nUpdate (elapsedMillisecs

elapsedMillisecs) ) // // give it CPU time give it CPU time if task is dead then if task is dead then // // was marked to die was marked to die (kill () kill ()) if task if task->next != none then next != none then activate (task activate (task->next); task next); task->next = none next = none remove the task from the remove the task from the task list task list // // shut it down shut it down

in what order?

remove the task from the remove the task from the task list task list // // shut it down shut it down

  • Here, a

Here, a task task can can be be removed removed after after its its own

  • wn onUpdate
  • nUpdate ()

() - no no problem (..?) problem (..?)

  • Other tasks can also kill it (after or before its

Other tasks can also kill it (after or before its onUpdate

  • nUpdate on this cycle)
  • n this cycle)
  • Could

Could single single out a special

  • ut a special failure

failure termination termination (the (the task task was was aborted aborted) => must ignore => must ignore the the next next task task (since since this this one

  • ne didn't

didn't finish finish up successfully) up successfully)

  • Note

Note that that we we are are both both traversing traversing and and modifying modifying a a list list => => be be very very careful careful!

21.2.2017 21.2.2017 32 32

slide-9
SLIDE 9

Provide a Provide a timer timer task: a general service task: a general service

  • Wait tasks can be used for any timed delays

Wait tasks can be used for any timed delays

  • E.g., the fuse is burning on an explosive

E.g., the fuse is burning on an explosive

class WaitTask : public GTask { class WaitTask : public GTask { class WaitTask : public GTask { class WaitTask : public GTask { public: public: WaitTask (TaskPtr next, int dMilliSecs) WaitTask (TaskPtr next, int dMilliSecs) : GTask (next), elapsed (0), stop (dMilliSecs) {} : GTask (next), elapsed (0), stop (dMilliSecs) {} virtual void onUpdate (int dMilliSecs) override { virtual void onUpdate (int dMilliSecs) override { if ((elapsed += dMilliSecs) >= stop) if ((elapsed += dMilliSecs) >= stop) this this->kill (); // kill (); // and the next task becomes started and the next task becomes started this this->kill (); // kill (); // and the next task becomes started and the next task becomes started } private: private: int elapsed, stop; int elapsed, stop; }; };

21.2.2017 21.2.2017 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki 33 33

Example: creating an instance of wait task Example: creating an instance of wait task

  • Creating two tasks, and binding them together

Creating two tasks, and binding them together

  • Then attach the first task to the task manager

Then attach the first task to the task manager

TaskPtr kaboom (new Explosion); TaskPtr kaboom (new Explosion); TaskPtr doWait (new WaitTask (kaboom, 3000)); TaskPtr doWait (new WaitTask (kaboom, 3000)); GTask::activate (doWait); GTask::activate (doWait); // // stays around 3 seconds stays around 3 seconds . . . . . . while (true) { while (true) { // // game main loop game main loop // // other activities . .

  • ther activities . .

// // other activities . .

  • ther activities . .

GTask::updateAllTasks ( GTask::updateAllTasks (elapsedMilliSecs elapsedMilliSecs); ); . . . . . . } }

21.2.2017 21.2.2017 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki 34 34

Implementation notes Implementation notes

  • Co

Co-operative tasks are

  • perative tasks are coroutines

coroutines: they must explicitly release turns : they must explicitly release turns

  • the original "green"

the original "green" Java Java Thread Threads used s used yield() yield(); ; (impl. by the JVM) (impl. by the JVM)

  • instead,

instead, pre pre-emptive scheduling emptive scheduling lets lets the operating system to the operating system to determine when a context switch should occur determine when a context switch should occur => => "race hazards" "race hazards"

  • Note that a

Note that a GTask GTask doesn't have a doesn't have a stack stack of its own (but

  • f its own (but threads

threads do) do)

  • such stack could store temporary results and execution point (PC)

such stack could store temporary results and execution point (PC)

  • now, execution state/data is here saved in data members of a task

now, execution state/data is here saved in data members of a task

  • A convenient feature is

A convenient feature is ordering

  • rdering of tasks in the task list
  • f tasks in the task list
  • a game often needs to make certain updates in a specific order

a game often needs to make certain updates in a specific order

  • Problems if

Problems if update updates directly modify the task list: must prevent/control s directly modify the task list: must prevent/control

  • The original design of McShaffry uses a separate

The original design of McShaffry uses a separate TaskManager TaskManager class class

  • How is

How is TaskPtr TaskPtr used and defined? used and defined?

  • uses reference counts

uses reference counts - smart pointers smart pointers now part of the C++ standard now part of the C++ standard

21.2.2017 21.2.2017 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki 35 35

Still more tasking services.. (~ Still more tasking services.. (~ threads threads)

  • A task may want to stop (become non

A task may want to stop (become non-

  • active) and wait for some

active) and wait for some condition to come condition to come true true

  • to keep waiting tasks around, store them somewhere (into a list)

to keep waiting tasks around, store them somewhere (into a list) when the condition becomes when the condition becomes true true, the tasks can be again be , the tasks can be again be

  • when the condition becomes

when the condition becomes true true, the tasks can be again be , the tasks can be again be activated.. activated..

  • To express this idea, could build something like:

To express this idea, could build something like: Condition r; Condition r; // // includes a list for waiting tasks includes a list for waiting tasks myTask.wait (r); myTask.wait (r); // // task is deactivated and put into the wait list task is deactivated and put into the wait list . . . . . . . . . . . . // // at some later time, under specific conditions at some later time, under specific conditions: r.signal (); r.signal (); // // the waiting tasks are removed and activated.. the waiting tasks are removed and activated..

  • waitFor

waitFor ( (lambda expression lambda expression) ? ) ?

21.2.2017 21.2.2017 Juha Vihavainen / University of Helsinki Juha Vihavainen / University of Helsinki 36 36