the gamedesigninitiative at cornell university

Lecture 8 Game Architecture Revisited Recall: The Game Loop

Receive player input Process player actions Update 60 times/s Process NPC actions Interactions (e.g. physics) = 16.7 ms Cull non-visible objects Transform visible objects Draw Draw to backing buffer Display backing buffer

the gamedesigninitiative 2 Architecture Revisited at cornell university The Game Loop

Receive player input Process player actions Update Process NPC actions Interactions (e.g. physics)

— Almost everything is in loop Draw — Except asynchronous actions — Is enough for simple games — How do we organize this loop? — Do not want spaghetti code — Distribute over programmers

the gamedesigninitiative 3 Architecture Revisited at cornell university Model-View-Controller Pattern

Controller Calls the • Updates model in methods of response to events • Updates view with model changes Model View • Defines/manages • Displays model the program data to the user/player • Responds to the • Provides interface controller requests for the controller

the gamedesigninitiative 4 Architecture Revisited at cornell university The Game Loop and MVC

— Model: The game state — Value of game resources — Location of game objects Update — View: The draw phase — Rendering commands only — Major computation in update Draw — Controller: The update phase — Alters the game state — Vast majority of your code the gamedesigninitiative 5 Architecture Revisited at cornell university Application Structure

Root Controller Ownership

Subcontroller Subcontroller View

Model Model Model

Collaboration

the gamedesigninitiative 6 Architecture Revisited at cornell university Application Structure

Root — Collaboration Controller — Must import class/interface Ownership — Instantiates an object OR — Calls the objects methods

Subcontroller Subcontroller— Ownership View — Instantiated the object — Responsible for disposal — Superset of collaboration

Model Model Model

Collaboration

the gamedesigninitiative 7 Architecture Revisited at cornell university Avoid Cyclic Collaboration

Controller

collaborates with Z

Y X collaborates with

Y X collaborates with

the gamedesigninitiative 8 Architecture Revisited at cornell university Application Structure

Root Controller

Subcontroller Subcontroller View

?

Model Model Model

the gamedesigninitiative 9 Architecture Revisited at cornell university CUGL Views: Scene Graphs

Root Controller

Scene Root

Model Node Node

Node Node Node Node

Model the gamedesigninitiative 10 Architecture Revisited at cornell university CUGL Views: Scene Graphs

Root Controller

Scene Root

Model Node Node

Node Node Node Node

Model the gamedesigninitiative 11 Architecture Revisited at cornell university CUGL Views: Scene Graphs

Root Controller

Scene Root

Model Node Node

Node Node Node Node

Model the gamedesigninitiative 12 Architecture Revisited at cornell university Model-Controller Separation (Standard)

Model Controller

— Store/retrieve object data — Process user input — Limit access (getter/setter) — Determine action for input — Preserve any invariants — Example: mouse, gamepad — Only affects this object — Call action in the model — Implements object logic — Complex actions on model Traditional controllers — May affect multiple models are “lightweight” — Example: attack, collide

the gamedesigninitiative 13 Architecture Revisited at cornell university Classic Software Problem: Extensibility

— Given: Class with some base functionality — Might be provided in the language API — Might be provided in 3rd party software — Goal: Object with additional functionality — Classic solution is to subclass original class first — Example: Extending GUI widgets (e.g. Swing) — But subclassing does not always work… — How do you extend a Singleton object?

the gamedesigninitiative 14 Architecture Revisited at cornell university Problem with Subclassing

— Games have lots of classes

— Each game entity is different NPC — Needs its own functionality (e.g. object methods) Human Orc — Want to avoid redundancies — Makes code hard to change

— Common source of bugs Human Human Orc Orc Warrior Archer Warrior Archer — Might be tempted to subclass — Common behavior in parents — Specific behavior in children Redundant Behavior

the gamedesigninitiative 15 Architecture Revisited at cornell university Problem with Subclassing

— Games have lots of classes

— Each game entity is different NPC — Needs its own functionality (e.g. object methods) Warrior Archer — Want to avoid redundancies — Makes code hard to change

— Common source of bugs Human Orc Human Orc Warrior Warrior Archer Archer — Might be tempted to subclass — Common behavior in parents — Specific behavior in children Redundant Behavior

the gamedesigninitiative 16 Architecture Revisited at cornell university Model-Controller Separation (Standard)

Model NPC — Store/retrieve object data — Limit access (getter/setter) — Preserve any invariants Human Orc — Only affects this object

— Implements object logic Human Human Orc Orc Warrior Archer Warrior Archer — Complex actions on model — May affect multiple models — Example: attack, collide Redundant Behavior

the gamedesigninitiative 17 Architecture Revisited at cornell university Model-Controller Separation (Alternate)

Model Controller

— Store/retrieve object data — Process game actions — Limit access (getter/setter) — Determine from input or AI — Preserve any invariants — Find all objects effected — Only affects this object — Apply action to objects — Process interactions In this case, models — Look at current game state are lightweight — Look for “triggering” event — Apply interaction outcome

the gamedesigninitiative 18 Architecture Revisited at cornell university Does Not Completely Solve Problem

— Code correctness a concern Can I — Methods have specifications flee? — Must use according to spec — Check correctness via typing — Find methods in object class — Example: orc.flee() — Check type of parameters — Example: force_to_flee(orc) — Logical association with type — Even if not part of class

the gamedesigninitiative 19 Architecture Revisited at cornell university Issues with the OO Paradigm

— Object-oriented programming is very noun-centric — All code must be organized into classes — Polymorphism determines capability via type — OO became popular with traditional MVC pattern — Widget libraries are nouns implementing view — Data structures (e.g. CS 2110) are all nouns — Controllers are not necessarily nouns, but lightweight — Games, interactive media break this paradigm — View is animation (process) oriented, not widget oriented — Actions/capabilities only loosely connected to entities

the gamedesigninitiative 20 Architecture Revisited at cornell university Programming and Parts of Speech

Classes/Types are Nouns Actions are Verbs

— Methods have verb names — Capability of a game object — Method calls are sentences — Often just a simple function — subject.verb(object) — damage(object) — subject.verb() — collide(object1,object1) — Classes related by is-a — Relates to objects via can-it — Indicates class a subclass of — Example: Orc can-it attack — Example: String is-a Object — Not necessarily tied to class — Example: swapping items — Objects are class instances

the gamedesigninitiative 21 Architecture Revisited at cornell university Duck Typing: Reaction to This Issue

— “Type” determined by its Java: — Names of its methods public boolean equals(Object h) { if (!(h instanceof Person)) { — Names of its properties return false;} — If it “quacks like a duck” Person ob= (Person)h; — Python has this capability return name.equals(ob.name); } — hasattr(,) Python: — True if object has attribute or method of that name def __eq__(self,ob): if (not (hasattr(ob,'name’)) — This has many problems return False — Correctness is a nightmare return (self.name == ob.name)

the gamedesigninitiative 22 Architecture Revisited at cornell university Duck Typing: Reaction to This Issue

— “Type” determined by its Java: — Names of its methods public boolean equals(Object h) { if (!(h instanceof Person)) { — Names of— itsWhat properties do we really want? return false;} Capabilities over properties — If it “quacks— like a duck” Person ob= (Person)h; — Python has this— capabilityExtend capabilities withoutreturn name.equals (ob.name); necessarily changing} type — hasattr(,) Without using newPython: languages — True if object— has attribute or method— ofAgain, that name use softwaredef __patternseq__(self,ob): if (not (hasattr(ob,'name’)) — This has many problems return False — Correctness is a nightmare return (self.name == ob.name)

the gamedesigninitiative 23 Architecture Revisited at cornell university Possible Solution:

New Functionality

OriginalReference to Original Request Decorator Object Functionalitybase object Object

the gamedesigninitiative 24 Architecture Revisited at cornell university Java I/O Example

InputStream input = System.in; Built-in console input

Reader reader = new InputStreamReader(input);

Make characters easy to read BufferedReader buffer = new BufferedReader(reader);

Read whole line at a time Most of java.io works this way

the gamedesigninitiative 25 Architecture Revisited at cornell university Alternate Solution: Delegation Pattern

Original Reference to Request Delegate Object delegate Object 1

Forward Request

Inversion of the Decorator Pattern

the gamedesigninitiative 26 Architecture Revisited at cornell university Alternate Solution: Delegation Pattern

Original Reference to Request Delegate Object delegate Object 21

Forward Request

Inversion of the Decorator Pattern

the gamedesigninitiative 27 Architecture Revisited at cornell university Example: Sort Algorithms public class SortableArray extends ArrayList{

private Sorter sorter = new MergeSorter(); new QuickSorter();

public void setSorter(Sorter s) { sorter = s; }

public void sort() { Object[] list = toArray(); public interface Sorter { sorter.sort(list); public void sort(Object[] list); clear(); } for (o:list) { add(o); } } }

the gamedesigninitiative 28 Architecture Revisited at cornell university Comparison of Approaches

Decoration Delegation

— Pattern applies to decorator — Applies to original object — Given the original object — You designed object class — Requests through decorator — All requests through object — Monolithic solution — Modular solution — Decorator has all methods — Each method can have own — “Layer” for more methods delegate implementation (e.g. Java I/O classes) — Like higher-order functions — Works on any object/class — Limited to classes you make

the gamedesigninitiative 29 Architecture Revisited at cornell university The Subclass Problem Revisited

Delegates?

NPC Orc

Slot Human Human Orc NPC Slot

Slot Warrior Human Human Orc Orc Warrior Archer Warrior Archer Archer

Redundant Behavior

the gamedesigninitiative 30 Architecture Revisited at cornell university A C++ Solution: Mix-Ins

class Number { — Orthogonal class design int n; — Start with common base void set(int v) { n = v; } int get() const { return n; } — Only one level of superclass }; — Other classes can combine template class Undo : public BASE { int pre; — Needs C++ templates void set(int v) { pre=BASE::get(); BASE::set(v); } void undo() { BASE::set(pre); } — Templatize base class };

— Nest templates to mix template class Redo : public BASE { — Builds an inheritance tree int post; void set(int v) { post= v; BASE::set(v); } — But tree details not needed void redo() { BASE::set(post); } }; — Stacking is commutative typedef Redo< Undo > ReUndoNumber;

the gamedesigninitiative 31 Architecture Revisited at cornell university Summary

— Games naturally fit a specialized MVC pattern — Want lightweight models (mainly for serialization) — Want heavyweight controllers for the game loop — View is specialized rendering with few widgets — Proper design leads to unusual OO patterns — Subclass hierarchies are unmanageable — Component-based design better models actions — More advanced patterns supported by C++.

the gamedesigninitiative 32 Architecture Revisited at cornell university