the gamedesigninitiative at cornell university

Lecture 8

Component Design Recall: 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 Redundant Behavior — Specific behavior in children

the gamedesigninitiative 2 Architecture Patterns at cornell university Recall: 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 Redundant Behavior — Specific behavior in children

the gamedesigninitiative 3 Architecture Patterns at cornell university Alternative:

New Functionality

OriginalReference to Original Request Decorator Object Functionalitybase object Object

the gamedesigninitiative 4 Architecture Patterns at cornell university Alternate: Delegation Pattern

Original Reference to Request Delegate Object delegate Object

Forward Request

Inversion of the Decorator Pattern

the gamedesigninitiative 5 Architecture Patterns at cornell university Issues with Static Typing

Method in original class Original object class obj.request(arg1,…, argn)

Original Reference to Request Delegate Object delegate Object

Forward Request

the gamedesigninitiative 6 Architecture Patterns 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 7 Architecture Patterns at cornell university Component-Based Programming

Delegates — Role: Set of capabilities Role 1 — Class with very little data — A collection of methods Slot Role 2 — Add it to object as delegate Entity Slot — Object gains those methods Slot Role 3 — Acts as a “function pointer”

Role 4 — Can-it: search object roles — Check class of each role Field storing a — Better than duck typing single delegate or — Possible at compile time? a set of delegates

the gamedesigninitiative 8 Architecture Patterns at cornell university Aside: Objective-C

— Delegates are emphasized in Objective-C — ObjC protocols function like Java interfaces — But implement with a delegate, not a subclass — Often leverages message passing — Method call is considered a message — Can call any method on any object — Will get runtime error if method not supported — Heavily optimized in latest Apple compilers

the gamedesigninitiative 9 Architecture Patterns at cornell university Aside: Objective-C

— Delegates are emphasized in Objective-C — ObjC protocols function like Java interfaces — But implement with a delegate, not a subclass — Often leverages message passing — MethodAddresses call is considered Static aTyping message Issue — Can call any method on any object — Will get runtime error if method not supported — Heavily optimized in latest Apple compilers

the gamedesigninitiative 10 Architecture Patterns at cornell university Working With Static Typing

Uniform Method Direct Access

o.doSomething(kitchenSink) if (o has role) { extract role from o role.doSomething(info) }

the gamedesigninitiative 11 Architecture Patterns at cornell university Working With Static Typing

Uniform Method Direct Access

o.doSomething(kitchenSink) if (o has role) { extract role from o role.doSomething(info) }

Problem: Multiple capabilities

the gamedesigninitiative 12 Architecture Patterns at cornell university Working With Static Typing

Uniform Method Direct Access

o.doSomething(kitchenSink) if (o has role) { extract role from o role.doSomething(info) }

Problem: Problem: Multiple capabilities Big switch statements

the gamedesigninitiative 13 Architecture Patterns at cornell university Entities Need Both Is-a and Can-it

Table Chair

Objects share same capabilities in theory. But certain actions are preferred on each.

the gamedesigninitiative 14 Architecture Patterns at cornell university Solving the Problem

Can-It Is-A

— Indicated by object roles — Indicated by object class — Component indicates role — But could do more than that — Check type of each role — Keep a bag of types — So again a typing solution — Contains of the descriptions — “Formal duck typing” that apply to this object — Not limited to method name — Example: table, heirloom — Role type indicates action — Why would we do this? name, not a noun

the gamedesigninitiative 15 Architecture Patterns at cornell university Fast Can-It Testing: Bit Vectors

— Give each object a bit-vector to cover types — Each role is a position in the bit vector — Example: 0100101

the gamedesigninitiative 16 Architecture Patterns at cornell university Fast Can-It Testing: Bit Vectors

— Give each object a bit-vector to cover types — Each role is a position in the bit vector — Example: 0100101 Can Build

Can Fight Can Heal

— Use array if more than 32/64 roles

the gamedesigninitiative 17 Architecture Patterns at cornell university Fast Can-It Testing: Bit Vectors

— Give each object a bit-vector to cover types — Each role is a position in the bit vector — Example: 0100101 Can Build

Can Fight Can Heal

— Use array if more than 32/64 roles — Test is quick logical operation. — Example: vector & CAN_FIGHT — Faster than dynamic typing (why?)

the gamedesigninitiative 18 Architecture Patterns at cornell university Model-Controller Separation Revisited

Model Controller

— Store/retrieve object data — Process interactions — Preserve any invariants — Look at current game state — Data may include delegates — Look for “triggering” event — Determines is-a properties — Apply interaction outcome Components

? — Process game actions ? — Attached to a entity (model) — Uses the model as context

the — Determines can-it properties gamedesigninitiative 19 Architecture Patterns at cornell university Model-Controller Separation Revisited

Model Controller

— Store/retrieve object data — Process interactions — Preserve any invariants — Look at current game state — Data may include delegates — Look for “triggering” event — Determines is-a properties — Apply interaction outcome

Components Is this always the best idea? ? — Process game actions ? — Attached to a entity (model) — Uses the model as context

the — Determines can-it properties gamedesigninitiative 20 Architecture Patterns at cornell university Recall: Lab 3 in CS 3152

the gamedesigninitiative 21 Architecture Patterns at cornell university Processing Actions in Lab 3

GameplayController.cs

Ship.cs

— Decides whether to shoot — Waits until objects commit — Stores intent in the object — Checks intent in Ship object — But DOES NOT shoot — Performs action for intent

the gamedesigninitiative 22 Game Architecture at cornell university Processing Actions in Lab 3

GameplayController.cs

Attach components Ship.cs to controller?

— Decides whether to shoot — Waits until objects commit — Stores intent in the object — Checks intent in Ship object — But DOES NOT shoot — Performs action for intent

the gamedesigninitiative 23 Game Architecture at cornell university Related: Handling the View

— Way too much to draw — Backgrounds — UI elements Update — Individual NPCs — Other moveable objects — Cannot cram all in Draw Draw — Put it in game object? — But objects are models — Violates MVC again

the gamedesigninitiative 24 Architecture Patterns at cornell university First Attempt: A Drawing Canvas

— Treat display as a container — Often called a canvas — Cleared at start of frame — Objects added to container — Draw contents at frame end — Canvas abstracts rendering Passed as reference — Hides animation details — Like working with widget void draw(Canvas c) { // Specify perspective — Implement draw(c) in model // Add to canvas — Classic heavyweight model } — No problems with extension

the gamedesigninitiative 25 Architecture Patterns at cornell university Problem: Canvas Methods

Ideal Model1 draw(canvas)

Canvas drawShape(…)

Model2 draw(canvas)

the gamedesigninitiative 26 Architecture Patterns at cornell university Problem: Canvas Methods

In Practice Model1 draw(canvas) Canvas

drawShape1(…)

Model2 drawShape2(…) draw(canvas)

the gamedesigninitiative 27 Architecture Patterns at cornell university Views and Components

Slot Component1 Model1 Slot drawShape1(…) Slot Canvas drawPrimitive(…)

Slot Component1 Model2 Slot drawShape1(…) Slot

the gamedesigninitiative 28 Architecture Patterns at cornell university Views and Components

Primitive Slot effects Component1 Model1 Slot drawShape1(…) Slot Complex, Shape-specific Canvas rendering drawPrimitive(…) Slot Component1 Model2 Slot drawShape1(…) Slot

the gamedesigninitiative 29 Architecture Patterns at cornell university Components and AI

— NPCs use sense-think-act cycle Alert! — Sense: perceive the world — Think: choose an action — Act: update the game state — Act is stored in components — Capabilities given by roles — Where are sense and think? — In the component/role? Yes — How do roles interact? This is hard

the gamedesigninitiative 30 Game Architecture at cornell university Why Do They Need to Interact?

— Because you are trying to pick an action — NPC can only do one action (why?) — Think needs all roles to pick best one — Why not let each component do an action? — Some things can be simultaneously (run + shoot) — Only concern is conflicting actions (left vs. right) — Can we guarantee components never conflict? — Do we have to solve this in thinking-phase?

the gamedesigninitiative 31 Architecture Patterns at cornell university Recall: Rule-Based AI

— Have a list of rules R1: if event1 then act1! — All if-then statements R2: if event2 then act2! — Rules fires if satisfied R3: if event3 then act3! — Need to pick one (conflict resolution) R4: if event4 then act4!

— Often resolve by order R5: if event5 then act5!

— Each rule has a priority R6: if event6 then act6! — Higher priorities go first R7: if event7 then act7! — “Flattening” conditionals !

the gamedesigninitiative 32 Character AI at cornell university Treat Components the Same

— Each component a rule C1: warrior component! — Chooses an action C2: archer component! — Or possibly none C3: human component — More complex than simple if-then rules C4: orc component! — Conflict resolution — Priorities to components Implies a global — Or priorities to actions AI subsytem — Resolved globally

the gamedesigninitiative 33 Character AI at cornell university Non-Conflict Resolution?

— What if they do not conflict? — Commutative, disjoint: — Pick just one? — move(dx,dy): — Apply them both? x = x + dx — Only if both commutative y = y + dy — damage(d): — Treat action as f: S → S hp = hp - d — Require f (g(s)) = g( f (s)) — Easy if state is disjoint — Commutative, not disjoint: — Animation is a big problem — 2 move actions — Each action has animation — Addition commutes — Same solution as state? — Example: walk, push

the gamedesigninitiative 34 Architecture Patterns at cornell university Scalable Games Language

States Effects — Assume entities >> roles id x y health vx vy damage Common in certain genres 1 12 342 100 -10 3 50 — 2 43 12 100 0 5 0 — Treat as a database problem 3 123 90 95 9 9 0 — Entities are database rows class Unit { — Roles are database queries public state int id; — Gate checking is a DB join // State with update rules public state float x : (x+vx); public state float y : (y+vy); — Updates must all commute public state int hp: (hp-dmg); — Language + schema design // effects — Trivial conflict resolution public effect float vx : avg; public effect float vy : avg; — Surprisingly expressive public effect int dmg : sum; the ... gamedesigninitiative at cornell university Scalable Games Language

public void run() { // Compute # skeletons, group center effect int c : sum; effect float sx : sum, float sy : sum; foreach(Unit u : Extent) { if (isEnemySkeleton(u) && dist(this,u) < range) { c <- 1; sx <- u.x; sy <- u.y; } } // If too many skeletons if (c > morale) { const float norm = (x-sx/c)*(x-sx/c)+ (y-sy/c)*(y-sy/c); // Run in opposite direction vx <- (x-sx/c)/norm; vy <- (y-sy/c)/norm;

the } ... gamedesigninitiative at cornell university

Scalable Games Language

public void run() { // Compute # skeletons, group center effect int c : sum; effect float sx : sum, float sy : sum; Classicforeach DB optimizations(Unit u : Extent ) { § Query if rewrites(isEnemySkeleton (u) && § Join selection dist(this ,u) < range) { c <- 1; sx <- u.x; sy <- u.y; § Automatic } indexing } // If too many skeletons if (c > morale) { const float norm = (x-sx/c)*(x-sx/c)+ (y-sy/c)*(y-sy/c); // Run in opposite direction vx <- (x-sx/c)/norm; vy <- (y-sy/c)/norm;

the } ... gamedesigninitiative at cornell university

Animation: Blend Trees

the gamedesigninitiative 38 Architecture Patterns at cornell university Conflict and Non-Conflict?

— Create a DAG structure — Directed acyclic graph — a > b if a reachable from b — a | b if neither a < b, a > b (e.g. a, b are incomparable) — Use for conflict resolution — If comparable, pick greatest — If incomparable commute — Looks good, hard in practice — This is a global structure

the gamedesigninitiative 39 Architecture Patterns 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 — Want component-based design to model actions — Interesting challenges when incorporating AI

the gamedesigninitiative 40 Architecture Patterns at cornell university