How to Design Fun Games

126 views 7:48 am 0 Comments July 22, 2023

Today
How to design “fun” gamesSample Page
Image basics
PImage class
How to load and display an
image
Case study: use images to build
an Avoider Game
State control
2
Debugging tips
Compiler errors
Semantic errors
Runtime exceptions
How to Design Fun Games
3
Fun topology
MDA Model for game design
Motivation Design
Design Rewards
Balancing skills and challenges
What do we mean by a “Fun
Game”?
Sid Meier: “a game is a series of interesting choices”
Must be Fun: Challenging, Rewarding, and Balancing
4
Describe the challenge here? How about here?
Typology of Fun (Aesthetics)
1. Sensation: Game as sense-pleasure
2. Fantasy: Game as make-believe
3. Narrative: Game as unfolded story
4. Challenge: Game as obstacle course
5. Fellowship: Game as social framework
6. Discovery: Game as uncharted territory, novelty
7. Expression: Game as self-discovery
8. Submission: Game as pastime
Marc LeBlanc
5
Example: Fun types in
Games
Charades (word guessing game)
Fellowship, Expression, Challenge
Fable 2 (Action RPG)
An epic story (Narrative) involving the player
searching the world (
Discovery) of Albion for the
Legendary heroes (
Fantasy & Challenge) who are
prophesized to save the world from Lucien (
Grand
Challenge
)
Players can morph their characters based on their
actions (
Expression), and is allowed for marriage
and sexual liaisons with NPCs (
Fellowship)
6
How to
Design a Game with Desired
Funs?
One approach is to follow Mark
Leblanc’s
MDA Model
7
Games as Software
Rules Activity “Fun”
Code Process Requirements
8
=
The MDA Framework
Mechanics Dynamics Aesthetics
9
Game Play: Player’s
Perspective

Mechanics

Dynamics Aesthetics
Player
10
Game Design: The
Designer’s Perspective
Mechanics

Dynamics

Aesthetics
Designer
11
Game Design: Essential
Parts
System design: game mechanics, rules,
objectives, challenges, AI, economy
Interaction design: Controls, interfaces,
feedbacks
Motivation design: Learning, reward,
balancing difficulty against ability

Motivation Design: Rewards
in Games
Operant Conditioning
A method of learning that occurs through rewards and
punishments for behaviour
Established by Skinner
Behaviors can be forged through:
Reinforcements – rewarding to increase the behavior
Punishments – punishing to decrease the behavior
13
Categories of Rewards
Rewards of Glory: things that have no impact on gameplay
but will be taken away by players after playing:
illusionary achievements (e.g. ranks, social rewards, …)
Rewards of Sustenance or Enhancement: maintaining or
enhancing player’s status or ability
Restoring health/lives by health packs
Enhancing using power-ups (on a temporary basis)
Rewards of Access (to new locations or resources)
Gaining keys, passwords, picklocks, secret room, bonus level, etc.
(on
temporary basis)
Hallford & Hallford
14
Motivation Design: Balancing for
skills
Players have different level of skill, and it will get improved
along with playing
These require your game design to:
Provide various difficulty levels (e.g. easy/midum/hard)
And more importantly:
Escalate difficulties/challenges as player moves up on levels
Tetris: speed increases according to player score
Space invader: aliens approaching faster + boss
fight
15
Balancing Skills and Challenges
0 (Low) Skills (High)
Flow
Chann
el

(High)
(Low)
0
Challenges
Boredom
Anxiety
Your goal is to adjust challenge dynamically to
maintain gameplay within the
flow channel so as
to
maximize the player’s enjoyment
16
=
Summary
To design a fun game, one approach you are suggested is to:
Follow MDA model to specify what types of funs you want your
target audience to enjoy, and set them as the goal of your game
design
Design your game revolving around the essential parts of game
design: System design (Mechanics & Gameplay), Interaction design
(UI), and Motivation design (Rewards):
Design reasonable mechanics to allow for enjoyable gameplay
experience
Design different types of rewards into your game to motivate and
engage players to play your game
Make your game balanced between game challenges and players’ skill
levels by scaling up challenges with player’s progression
17
Image Basics
PImage class
How to load and display an image
Case study: use images to build an Avoider Game
State control
18
Revisit Game Framework:
where images could be relevant?
Visual Presentations
Game map or world
Game tokens (e.g. avatar, NPCs,
weapons, ammos, rewards, etc.)
User interface(Buttons, menus,
HUD
)
HUD: for feedbacks such as
Scoreboard, lives, timer, etc.
Game map, tokens and even part of
HUD can be done in either graphics
or images
Game logic and flow control
Objects for storing property and
state info
Game Loop
Update game state
Move and detect collision
Handle user events for interactions
Winning Conditions
If the goal(s) has (have) been
achieved
19
PImage class
PImage is a Processing’s built-in class
Each actual image seen on Processing screen is a PImage object
Inside Processing library, it is defined as:
class PImage {

float width;
float height;
float[] pixels;
//width of the image
//height of the image
//pixels of the image

}
20
Methods for
Image Loading & Displaying
Method for image loading:
PImage loadImage (String filename);
Method for image display:
void image (PImage img, float x, float y ) – display in actual size
void image (PImage img, float x, float y, float w, float h )
be careful, it needs to recompute the sizes of the image every time it is called
(60 times per second!)
It will slow down your program’s running considerably
21
Loading Images
Loading an image
You need to add the image into the data folder under your sketch
folder
The data folder can be generated automatically when you add images to
your sketch folder
Then use the file name to load it:
Or you may want to create a sub-folder under your data folder to hold
all image files, e.g.
img, then load an image file as follows:
PImage img = loadImage(“image filename”);
PImage img = loadImage(“img/image filename”);
22
Displaying Images
image(PImage img, float x, float y)
draw image starting at location (x, y)
image(img, 0, 0) will draw your image starting
at the origin of the window
Where is (x, y) referring to on the image?
It depends on the imageMode
23
imageMode() sets the location of drawing
imageMode(CORNER);
(x, y) is the top left corner of the image – the default
location
imageMode(CENTER);
(x, y) is the center of the image
24
Load and Display a BG Image
First we need to add the image into the
sketch folder
To make the window size match the
image size:
PImage bkImg = loadImage(“background.png”);
image(bkImg, 0, 0);
//set window’s size as per bkImg’s size
size(550, 400);
PImage bkImg = loadImage(“background.png”);
image(bkImg, 0, 0);
25
Load and Display other Images
Add two characters onto the background
PImage bkImg, avatarImg, monsterImg;
void setup() {
//set window’s size as per bkImg’s size
size(550, 400);
bkImg = loadImage(“background.png”);
avatarImg = loadImage(“character.png”);
monsterImg = loadImage(“monster.png”);
}
void draw() {
image (bkImg, 0, 0, bkImg.width, bkImg.height);
image(avatarImg, width/2-avatarImg.width/2, height-avatarImg.height);
image(monsterImg, width/2-monsterImg.width/2, 0);
}
26
Note to loadImage()
Load images in setup() only, so that they will be
loaded only once
Never load images in draw(), which would load the
image repeatedly
Very costly and will slow down your program’s running
significantly!!
27
Case Study: using these images to
create an
Avoider Game
The avatar will be controlled with keyboard to move
around
A collection of monsters would roam around, get killed
when hit by avatar
To win the game, the avatar should try to dodge the
monsters and reach the top of the screen
It loses health when hit by a monster, and loses the game if
the health depletes
The game will have state-control, switching among
states:
Gameplay, Winning, and Losing
28
The main sketch
GameWorld gw;
Avatar avt;
int countMons = 18;
PImage monImg;
ArrayList<Monster> monsters = new
ArrayList<Monster>();
int gameState = 0;
//Keyboard control section

29
void setup() {
… //load images and initialize variables
}
void draw() {
switch (
gameState) {
case
0: gameplay(); break;
case
1: gameOver(“You Win!!”); break;
case
2: gameOver(“Game Over.nTry
Again!!”)
; break;
}
}

Define GameWorld Class
class GameWorld {
PImage bkImg;
GameWorld() {
bkImg = loadImage(“background.png”);
bkImg.resize(2*bkImg.width, 2*bkImg.height); //double the image display size
}
void display() {
image(bkImg, 0, 0, bkImg.width, bkImg.height); //the last two arguments can be omitted
}
}
30
Define Avatar Class
class Avatar {
PImage img;
PVector pos;
PVector vel;
float damp = 0.8;
float maxHealth=10, health = maxHealth,
healthPercentage=1;
//vars for health meter
Avatar(PImage img, PVector pos) {
this.
img = img;
this.pos = pos;
vel = new PVector();
}
31
void move(PVector acc) {
vel.add(acc);
}
void update() {
vel.mult(damp);
pos.add(vel); //moves character
if (pos.x + img.width > width) pos.x = width-img.width;
if (pos.x < 0) pos.x = 0;
if (pos.y + img.height > height) pos.y = height-img.height;
drawHealthBar();

if (pos.y < 0) {
pos.y = 0;
gameState = 1;
}
//it has reached the top – winning
//set winning state

}
Define Avatar Class (1)
void hit() {

health–;
if (health == 0) {
gameState = 2;
}
//reduce its health
//losing condition
//set losing state

//reset its position to the bottom center when hit
pos = new PVector(width/2-img.width/2, height-img.height);
}
//end of hit method
32
void drawMe() {
pushMatrix();
translate(pos.x, pos.y);
image(img, 0, 0);
popMatrix();
}
void
drawHealthBar() {

}
}
//end of Avatar class
Define Monster Class
class Monster {
PImage img;
PVector pos, vel;
Monster(PImage img, PVector pos) {
this.img = img;
this.pos = pos;
vel = new PVector(random(-8, 8), 0);
}
void update() {
detectEdges();
pos.add(vel);
//cancel its down movement if any
if (vel.y > 0) vel.y = 0;
}
33
void detectEdges() {
//if left or right edge of stage is detected
if (pos.x < 0 || (pos.x + img.width) > width) {
vel.x *= -1; //make it move back by reversing its vx
vel.y = img.height; //make it move down by an image height
}
//if half of the window height is reached wrap it to the top
if (pos.y >= height/2) {
pos.y = 0;
}
}
boolean collision (Avtar avt) {
… //collision detection between current monster against avatar
}
void drawMe() {
… //render the monster image starting from top-left corner
}
}
//end of Monster class
Summary for Image and State Control
Game map, tokens and even part of HUD can be done in either graphics or images
PImage is a Processing’s built-in class that would allow us to hold data loaded from image files
Important Properties: image’s width and height that can be accessed with an PImage object
resize(w, h): Resize the image to a new width and height

Imporant Methods
PImage loadImage (String filename)
– for loading image data from a file

void image (PImage img, float x, float y ) – display in actual size
void image (PImage img, float x, float y, float w, float h ) – display in size specified by w & h – be
careful to use it
Game state control
Use numbers to represent different game state
Use a variable to hold the number for the active state
Use switch or nested-if statements to control the flow between states
34
Debugging Tips
35
36
How do I know if my program is broken?
Compiler Errors
Errors the compiler can catch – easy to fix
Runtime Exceptions
Errors the runtime environment can catch – more
difficult to fix
Semantic errors
Your program runs but just doesn’t do the right
thing
The trickiest one to fix!!
37
Compiler Errors
Syntax errors:
Maybe missed a bracket/brace/semicolon or other necessary syntax element –
easy to fix
Logical errors – could be tricky for newbies
Method call inconsistent with method’s signature
Access a variable beyond its scope
No return statement in a method with a return type
Put a non-boolean method in an if-statement
38
How to fix Syntax Errors?
Start at the top of the error list
Use the line number
If that line looks OK, check the lines above
Match or Count Brackets and Braces
{ qwdkj
{ dw wqdlk lqwd
{ n,mnwq
}
}
}
39
012210
Braces match if the last == 0!
Mouse click to match braces in pair if IDE has the functionality
– such as Processing
Otherwise – count to match
Logical Errors
Method call inconsistent with method’s signature
Signature: Bug(float x, float y, float chgX, float chgY, float sz)
Wrong:
bugs.add( new Bug() );
AvatarBug(float x, float y, float chgX, float chgY, float sz) {
super(); //Bug is AvatarBug’s superclass
}
Right:
bugs.add( new Bug(random(width), random(height), random(-1, 1), random(-1, 1), random(12,
36))
);
AvatarBug(float x, float y, float chgX, float chgY, float sz) {
super(x, y, chgX, chgY, sz);
}
40
Logical Errors (1)
Access a variable beyond its scope
The “scope” of a variable refers to the variable’s visibility
within a program
Global variables: accessible from anywhere in the program (Try to
avoid whenever possible
)
Fields (member variables of a class): accessible from anywhere in the
class where they are declared (and its
subclasses if public)
Local variables: declared within a method or a code block within a
method, and accessible only within the method or the code block
A common error: access local variables outside the method
that they are declared
41
Logical Errors (2)
An Example:
Declare a variable (e.g. avtBug) within one method:
void setup() {
AvatarBug avtBug = respawn();
}
then try to access it in another method:
void playGame() {
if (rightKey)
avtBug.moveRight();
}
To fix:
Declare avtBug as a field (i.e. declare it as a member variable that
is outside any method)
42
Logical Errors (3)
No return statement in a method as the last statement for the type decleared
The following method returns boolean but no return statement as the last statement inside it
boolean detectCollision(Bug otherBug) {
if ( abs(bugX-otherBug.bugX) < (bSize+otherBug.bSize) &&…) {
return true;
}
//??????????????
}
To fix:
boolean detectCollision(Bug otherBug) {
if ( abs(bugX-otherBug.bugX)<(bSize+otherBug.bSize) && …) {
return true;
}
return false;
}
43
Logical Errors (4)
Or a better way to avoid this issue:
boolean detectCollision(Bug otherBug) {
boolean hit = false;
if ( abs(bugX-otherBug.bugX)<(bSize+otherBug.bSize) && …) {
hit = true;
}
return hit;
}
44
Logical Errors (5)
Put a non-boolean method in an ifstatement
Wrong:
void
detectBound() {
if (bugX+bSize > width ) bugX = width-bSize;
if (bugX-bSize < 0 ) bugX = bSize;
}
To fix: either keep the signature, change the way to use it and
modify the logic inside
void detectBound() {
if (bugX+bSize > width ) { bugX = width-bSize;
speedX *= -1;
}
if (bugX-bSize < 0 ) { bugX = bSize;
speedX *= -1;
}}
45
void draw() {

if(
bugi.detectBound() ){
speedX *= -1;
}…
}
void draw() {

bugi.detectBound();

}

Logical Errors (6)
Or keep the way to call it, change the signature and the logic inside:
void detectBound() {
boolean hit = false;
if (bugX+bSize > width ) { bugX = width-bSize;
hit = true;
}
if (bugX-bSize < 0 ) { bugX = bSize;
hit = true;
}
return hit;
}
46
void draw() {

if(
bugi.detectBound() ) {
speedX *= -1;
}

}
boolean
47
Runtime Exceptions
Common Runtime Exceptions:
NullPointerException and
IndexOutOfBoundsException
Exceptions caused by semantic errors
uninitialized variable, bad loop logic, …

48
NullPointerException
Normally because you’re accessing a reference variable (e.g. use it to
call a method) but failed to initialize it with an instance
So the variable for the object is actually a pointer pointing to NULL
AvatarBug avtBug;
//failed to initialize it with an instance like:
//avtBug = new AvatarBug(width/2, height/2, 4,4,20);
avtBug.drawBug();
NullPointerException!!
To fix: Always initialize after declaring a reference variable!!

IndexOutOfBoundsException
Normally it’s because you try to access an Array or ArrayList
position that goes beyond the max length of the array
It becomes tricky when you try to use a fixed number as the control value
to access a resizable array like ArrayList
int count = 5;
for(int i=0; i<
count; i++) {
Bug bugi = (Bug)
bugs.get(i);

To fix: use bugs.size() as controller
49
X
size = 5
size = 4

50
Semantic errors: Your program runs but just
doesn’t do the right thing
Approach 1:
Use println() when dealing with “Your program just
doesn’t do the right thing”
Print a string literal inside a method or a code block ({…})
to check if it really gets in there
Print boolean expression, variables, array elements or
indexes, objects etc to check if the values are correct as
expected

51
Semantic errors: Your program runs but just
doesn’t do the right thing (1)
Approach 2:
Use Debugger tool of IDE *
Enable Debugger (must set at least one breakpoint**) and then use key
ctrl+j to step over or ctrl+shift+j to step into a method to check if it really
gets executed
Use the Watch window to look into variables, array values, array index,
objects etc to check if the values are as expected
* May get lost in the process for debugging interactive programs
** To set
breakpoints in your source double-click on the small left margin in your
source code editor to
toggle on/off

52
#1 debugging tip
TEST YOUR CODE OFTEN!!
Code a little bit, Test a little bit, Make it work.
Repeat.
Catching small errors early will help you avoid big
complicated errors later

Wrong and Right
Build
Build
Build
Build
Build
Build
Test
Build
Test
Build
Test
Build
Test
Build
Test
53
53

Tags: , , , , , , , , , ,