the gamedesigninitiative at cornell university

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 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 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 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 Structure of a CUGL Application

Main Application

Scene Scene

Models Root Node Models Root Node Structure of a CUGL Application

Main Application Memory policy (future lecture) App Configuration

Scene Scene

Models Root Node Models Root Node Structure of a CUGL Application

Main Application

Active Dormant

Scene Scene

Models Root Node Models Root Node Structure of a CUGL Application

Main Application

Controller(s)

Scene Scene

Models Root Node Models Root Node

View The Application Class

onStartup() update()

— Handles the game assets — Called each animation frame — Attaches the asset loaders — Manages gameplay — Loads immediate assets — Converts input to actions — Starts any global singletons — Processes NPC behavior — Example: AudioEngine — Resolves physics — Resolves other interactions — Creates any player modes — But does not launch yet — Updates the scene graph — Waits for assets to load — Transforms nodes — Like GDXRoot in 3152 — Enables/disables nodes The Application Class

onStartup() update()

— Handles the game assets — Called each animation frame — Attaches the asset loaders — Manages gameplay — Loads immediate assets — Converts input to actions — Starts any global singletons — Processes NPC behavior () Does not draw! onShutdown — Example: AudioChannels — ResolvesHandled physicsseparately cleans this up — Resolves other interactions — Creates any player modes — But does not launch yet — Updates the scene graph — Waits for assets to load — Transforms nodes — Like GDXRoot in 3152 — Enables/disables nodes Application Structure

Scene Controller Ownership

Subcontroller Subcontroller View

Model Model Model

Collaboration Application Structure

Scene — 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 Avoid Cyclic Collaboration

Controller

collaborates with Z

Y X collaborates

with X

collaborates with Y Scene Structure

Scene Controller

Subcontroller Subcontroller View

?

Model Model Model CUGL Views: Scene Graphs

Scene

Root Node

Model Node Node

Node Node Node Node

Model CUGL Views: Scene Graphs

Scene

Root Node

Model Node Node

Node Node Node Node

Model CUGL Views: Scene Graphs

Scene

Root Node

for Another Lecture TopicModel Node Node

Node Node Node Node

Model 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 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. Java) — But subclassing does not always work… — How do you extend a Singleton object? 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 Problem with Subclassing

— Games have lots of classes

— Each game entity is different NPC — Needs its own functionality No Help (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 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 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 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 thisMotivation object for— Apply the action to objects Entity-Component— Process Model interactions In this case, models — Look at current game state are lightweight — Look for “triggering” event — Apply interaction outcome 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 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 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 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(,) — True if object has attribute Python: 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) 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(,) — True if object— hasWithout attribute using newPython: languages or method— ofWe that use name software patternsdef __eq__(self,ob): if (not (hasattr(ob,'name’)) — This has many problems return False — Correctness is a nightmare return (self.name == ob.name) Possible Solution:

New Behavior

Request Decorator OriginalReference to Original Object Functionalitybase object Object 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 Alternate Solution: Delegation Pattern

Original Reference to Request Delegate Object delegate Object 1

Forward Request

Inversion of the Decorator Pattern Alternate Solution: Delegation Pattern

Original Reference to Request Delegate Object delegate Object 21

Forward Request

Inversion of the Decorator Pattern 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); } } } 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 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 Subclass Problem Revisited

Delegates?

NPC Orc

Slot Human Human Orc Will see how NPCto Slot do with Templates Slot Warrior Human Human Orc Orc Warrior Archer Warrior Archer Archer

Redundant Behavior Summary

— CUGL supports the traditional game loop — Has root controller for primary app control — Root separates into update/draw steps — CUGL view is handled in scene graphs — Scene is a game mode or logical unit — Scene graph is hierarchical arrangement of scene — Games naturally fit a specialized MVC pattern — Want lightweight models (mainly for serialization) — Want heavyweight controllers for the game loop — Component-based design better models actions