Chapters 3 and 4: Game Display Modes - Intro These two...

48
Chapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple game The purpose is to demonstrate various display modes: 1. Window-based 2. Applet 3. Full screen Once again, understanding the graphics aspects is not important - they are dealt with in subsequent chapters The game: The game is called Worms A worm (similar to the creature in Centipede) moves rapidly around the screen Your task is to click on its head Your score is based on the number of misses and the time it takes to com- plete the task When you miss, an obstacle is placed at the point you clicked The worm must go around the obstacles Wraparound is in effect As the game progresses, the worm grows in length to a fixed maximum The main class is WormPanel It is the game’s version of GamePanel discussed earlier, and is essentially the same The primary difference between the different versions of the game resides in the top-level class The window-based game inherits from JFrame, the applet-based one from JApplet This difference affects various aspects of game control The game incorporates code for generating statistics (like those gathered in Chapter 2) 1

Transcript of Chapters 3 and 4: Game Display Modes - Intro These two...

Page 1: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Intro

• These two chapters implement a simple game

• The purpose is to demonstrate various display modes:

1. Window-based

2. Applet

3. Full screen

• Once again, understanding the graphics aspects is not important - they aredealt with in subsequent chapters

• The game:

– The game is called Worms

– A worm (similar to the creature in Centipede) moves rapidly around thescreen

– Your task is to click on its head

– Your score is based on the number of misses and the time it takes to com-plete the task

– When you miss, an obstacle is placed at the point you clicked

– The worm must go around the obstacles

– Wraparound is in effect

– As the game progresses, the worm grows in length to a fixed maximum

• The main class is WormPanel

– It is the game’s version of GamePanel discussed earlier, and is essentiallythe same

• The primary difference between the different versions of the game resides inthe top-level class

– The window-based game inherits from JFrame, the applet-based one fromJApplet

– This difference affects various aspects of game control

• The game incorporates code for generating statistics (like those gathered inChapter 2)

1

Page 2: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Windowed Version, WormChase Class

• The abbreviated class diagram:

• There are two threads:

– WormChase is the top-level class and manages window events and the GUI

– WormPanel maintains the animation loop

2

Page 3: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Windowed Version, WormChaseClass (2)

• WormChase class

– Class diagram:

– main() reads in the frame rate from the command line

∗ This is converted to a frame quantum in nanoseconds

∗ There is a default FPS if no argument is provided∗ Code:

public static void main(String args[])

{

int fps = DEFAULT_FPS;

if (args.length != 0)

fps = Integer.parseInt(args[0]);

long period = (long) 1000.0/fps;

System.out.println("fps: " + fps + "; period: " + period + " ms");

new WormChase(period*1000000L); // ms --> nanosecs

}

3

Page 4: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Windowed Version,WormChase Class (3)

– No buttons are provided for game control

∗ Control is handled by WindowListener methods

∗ The game is paused when the window loses focus, and resumed when itregains focus

∗ The game terminates when the window is killed∗ Code:

public void windowActivated(WindowEvent e)

{ wp.resumeGame(); }

public void windowDeactivated(WindowEvent e)

{ wp.pauseGame(); }

public void windowDeiconified(WindowEvent e)

{ wp.resumeGame(); }

public void windowIconified(WindowEvent e)

{ wp.pauseGame(); }

public void windowClosing(WindowEvent e)

{ wp.stopGame(); }

public void windowClosed(WindowEvent e) {}

public void windowOpened(WindowEvent e) {}

public void resumeGame()

// called when the JFrame is activated / deiconified

{ isPaused = false; }

public void pauseGame()

// called when the JFrame is deactivated / iconified

{ isPaused = true; }

public void stopGame()

// called when the JFrame is closing

{ running = false; }

4

Page 5: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Windowed Version, WormPanel ClassArchitecture

• Class diagram:

5

Page 6: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Windowed Version, WormPanel ClassConstructor

• The constructor:

public WormPanel(WormChase wc, long period)

{

wcTop = wc;

this.period = period;

setBackground(Color.white);

setPreferredSize( new Dimension(PWIDTH, PHEIGHT));

setFocusable(true);

requestFocus(); // the JPanel now has focus, so receives key events

readyForTermination();

// create game components

obs = new Obstacles(wcTop);

fred = new Worm(PWIDTH, PHEIGHT, obs);

addMouseListener( new MouseAdapter() {

public void mousePressed(MouseEvent e)

{ testPress(e.getX(), e.getY()); }

});

// set up message font

font = new Font("SansSerif", Font.BOLD, 24);

metrics = this.getFontMetrics(font);

// initialise timing elements

fpsStore = new double[NUM_FPS];

upsStore = new double[NUM_FPS];

for (int i=0; i < NUM_FPS; i++) {

fpsStore[i] = 0.0;

upsStore[i] = 0.0;

}

} // end of WormPanel()

6

Page 7: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Windowed Version, WormPanel ClasstestPress()

• testPress()

– Handles hits:private void testPress(int x, int y)

// is (x,y) near the head or should an obstacle be added?

{

if (!isPaused && !gameOver) {

if (fred.nearHead(x,y)) { // was mouse press near the head?

gameOver = true;

score = (40 - timeSpentInGame) + (40 - obs.getNumObstacles());

// hack together a score

}

else { // add an obstacle if possible

if (!fred.touchedAt(x,y)) // was the worm’s body untouched?

obs.add(x,y);

}

}

} // end of testPress()

7

Page 8: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Windowed Version, WormPanel Classrun()

• Same as before, but with code for printing statistics added (see pp 54 - 56)

public void run()

{

long beforeTime, afterTime, timeDiff, sleepTime;

long overSleepTime = 0L;

int noDelays = 0;

long excess = 0L;

gameStartTime = J3DTimer.getValue();

prevStatsTime = gameStartTime;

beforeTime = gameStartTime;

running = true;

while(running) {

gameUpdate();

gameRender();

paintScreen();

afterTime = J3DTimer.getValue();

timeDiff = afterTime - beforeTime;

sleepTime = (period - timeDiff) - overSleepTime;

if (sleepTime > 0) { // some time left in this cycle

try {

Thread.sleep(sleepTime/1000000L); // nano -> ms

}

catch(InterruptedException ex){}

overSleepTime = (J3DTimer.getValue() - afterTime) - sleepTime;

}

else { // sleepTime <= 0; the frame took longer than the period

excess -= sleepTime; // store excess time value

overSleepTime = 0L;

if (++noDelays >= NO_DELAYS_PER_YIELD) {

Thread.yield(); // give another thread a chance to run

noDelays = 0;

}

}

beforeTime = J3DTimer.getValue();

int skips = 0;

while((excess > period) && (skips < MAX_FRAME_SKIPS)) {

excess -= period;

gameUpdate(); // update state but don’t render

skips++;

}

framesSkipped += skips;

storeStats();

}

printStats();

System.exit(0); // so window disappears

} // end of run()

8

Page 9: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Windowed Version, WormPanel ClassgameUpdate(), gameRender(), and printScreen()

• gameUpdate()

– Calls fred.move(), which handles the updates to the worm (fred)private void gameUpdate()

{ if (!isPaused && !gameOver)

fred.move();

} // end of gameUpdate()

• gameRender()

– Responsible for drawing - worm and obstacles

– Also handles statistics output

– Otherwise, like gameRender() from GamePanel

private void gameRender()

{

if (dbImage == null){

dbImage = createImage(PWIDTH, PHEIGHT);

if (dbImage == null) {

System.out.println("dbImage is null");

return;

}

else

dbg = dbImage.getGraphics();

}

// clear the background

dbg.setColor(Color.white);

dbg.fillRect (0, 0, PWIDTH, PHEIGHT);

dbg.setColor(Color.blue);

dbg.setFont(font);

// report frame count & average FPS and UPS at top left

// dbg.drawString("Frame Count " + frameCount, 10, 25);

dbg.drawString("Average FPS/UPS: " + df.format(averageFPS) + ", " +

df.format(averageUPS), 20, 25); // was (10,55)

dbg.setColor(Color.black);

// draw game elements: the obstacles and the worm

obs.draw(dbg);

fred.draw(dbg);

if (gameOver)

gameOverMessage(dbg);

} // end of gameRender()

• printScreen()

– No changes

9

Page 10: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Windowed Version, Worm ClassStructure

• The worm’s structure defined by

private Point cells[];

private int nPoints;

private int tailPosn, headPosn; // the tail and head of the buffer

public Worm(int pW, int pH, Obstacles os)

{

cells = new Point[MAXPOINTS]; // initialise buffer

nPoints = 0;

headPosn = -1; tailPosn = -1;

• The body is represented by cells

– Each cell stores a point that represents a body section (or the head)

– To move the worm, the tail is erased and a new head is added to the nextavailable cell

– It is a circular array

10

Page 11: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Windowed Version, Worm ClassBearing

• Bearing (heading)

– The worm’s bearing/heading is represented by an integerprivate static final int NUM_DIRS = 8;

private static final int N = 0; // north, etc going clockwise

private static final int NE = 1;

private static final int E = 2;

private static final int SE = 3;

private static final int S = 4;

private static final int SW = 5;

private static final int W = 6;

private static final int NW = 7;

private int currCompass; // stores the current compass dir/bearing

– Graphically:

– currCompass holds the current heading/direction/bearing

11

Page 12: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Windowed Version, Worm ClassBearing (2)

• Changing bearing

– The new direction is determined by varyBearing()

∗ The greatest probability is to go straight (33.3%)

∗ Of lesser probability is to veer 45 degrees (22.2 % L/R)

∗ Of least probability is to veer 90 degrees (11.1 % L/R)

∗ The change of direction is represented as an int stored in probsForOffsetprobsForOffset = new int[NUM_PROBS];

probsForOffset[0] = 0; probsForOffset[1] = 0;

probsForOffset[2] = 0; probsForOffset[3] = 1;

probsForOffset[4] = 1; probsForOffset[5] = 2;

probsForOffset[6] = -1; probsForOffset[7] = -1;

probsForOffset[8] = -2;

private int varyBearing()

// vary the compass bearing semi-randomly

{

int newOffset = probsForOffset[ (int)( Math.random()*NUM_PROBS )];

return calcBearing( newOffset );

} // end of varyBearing()

– The new bearing is computed in calcBearing(), which adds the offset to thecurrent headingprivate int calcBearing(int offset)

// Use the offset to calculate a new compass bearing based

// on the current compass direction.

{

int turn = currCompass + offset;

// ensure that turn is between N to NW (0 to 7)

if (turn >= NUM_DIRS)

turn = turn - NUM_DIRS;

else if (turn < 0)

turn = NUM_DIRS + turn;

return turn;

} // end of calcBearing()

– For example:

bearing direction offset new bearing new direction

0 N 0 0 N0 N -1 7 NW0 N 1 1 NE2 E -1 1 NE2 E -2 0 N2 E 2 4 S

12

Page 13: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Windowed Version, Worm ClassMovement

• Calculating the head coords

– The head will be moved one body segment on each iteration

– The new coordinates are based on the size of the circle representing a bodysegment and the computed head offset

∗ A circle is defined by the

1. Coords of its upper left corner

2. Its width and height

∗ The differentials by which to adjust the (x, y) coords are stored in theincr array:

incrs = new Point2D.Double[NUM_DIRS];

incrs[N] = new Point2D.Double(0.0, -1.0);

incrs[NE] = new Point2D.Double(0.7, -0.7);

incrs[E] = new Point2D.Double(1.0, 0.0);

incrs[SE] = new Point2D.Double(0.7, 0.7);

incrs[S] = new Point2D.Double(0.0, 1.0);

incrs[SW] = new Point2D.Double(-0.7, 0.7);

incrs[W] = new Point2D.Double(-1.0, 0.0);

incrs[NW] = new Point2D.Double(-0.7, -0.7);

13

Page 14: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Windowed Version, WormClass Movement (2)

– The new coordinates are computed by nextPoint():private Point nextPoint(int prevPosn, int bearing)

{

// get the increments for the compass bearing

Point2D.Double incr = incrs[bearing];

int newX = cells[prevPosn].x + (int)(DOTSIZE * incr.x);

int newY = cells[prevPosn].y + (int)(DOTSIZE * incr.y);

// modify newX/newY if < 0, or > pWidth/pHeight: use wraparound

if (newX+DOTSIZE < 0) // is circle off the left edge of the canvas?

newX = newX + pWidth;

else if (newX > pWidth) // is circle off the right edge of the canvas?

newX = newX - pWidth;

if (newY+DOTSIZE < 0) // is circle off the top of the canvas?

newY = newY + pHeight;

else if (newY > pHeight) // is circle off the bottom of the canvas?

newY = newY - pHeight;

return new Point(newX,newY);

} // end of nextPoint()

– When calculating the new coordinates, wraparound at the window edgesmust be considered

• Movement and growth

– The worm starts out as a single cell

– On each iteration, it adds one cell until it is full length

14

Page 15: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Windowed Version, WormClass Movement (3)

– Movement is achieved by adding a new head

∗ The next available slot in the cells array is used

∗ The array use is circular - when full grown, the new head overwrites thetail

– The number of body segments is maintained by nPoints

– Pointers headPosn and tailPosn keep track of the head and tail positions inthe array

– The combination of growth and movement requires three phases in move():

1. Game start

∗ A head is added to the worm and an initial direction of movementcalculated

2. Worm is not full length

∗ A new head is added to the next available free slot

3. Worm is full length

∗ The new head overwrites the tail

– The head and tail indices are adjusted appropriately in each case

– Example growth:

iteration headPosn tailPosn prevPosn nPoints cells

0 -1 -1 - 0 - - - -1 0 0 -1 1 p1 - - -2 1 0 0 2 p1 p2 - -3 2 0 1 3 p1 p2 p3 -4 3 0 2 4 p1 p2 p3 p45 0 1 3 4 p5 p2 p3 p46 1 2 0 4 p5 p6 p3 p4

– The new head position is generated by newHead(prevPosn)

∗ See the next sections re collisions and obstacles

15

Page 16: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Windowed Version, Worm ClassMovement (4)

• Worm movement carried out by move():

public void move()

{

int prevPosn = headPosn; // save old head posn while creating new one

headPosn = (headPosn + 1) % MAXPOINTS;

if (nPoints == 0) { // empty array at start of game

tailPosn = headPosn;

currCompass = (int)( Math.random()*NUM_DIRS ); // random dir.

cells[headPosn] = new Point( pWidth/2, pHeight/2 ); // center pt

nPoints++;

}

else if (nPoints == MAXPOINTS) { // array is full

tailPosn = (tailPosn + 1) % MAXPOINTS; // forget last tail

newHead(prevPosn);

}

else { // still room in cells[]

newHead(prevPosn);

nPoints++;

}

} // end of move()

16

Page 17: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Windowed Version, Worm ClassDrawing

• This is handled by draw() and is straightforward:

public void draw(Graphics g)

// draw a black worm with a red head

{

if (nPoints > 0) {

g.setColor(Color.black);

int i = tailPosn;

while (i != headPosn) {

g.fillOval(cells[i].x, cells[i].y, DOTSIZE, DOTSIZE);

i = (i+1) % MAXPOINTS;

}

g.setColor(Color.red);

g.fillOval( cells[headPosn].x, cells[headPosn].y, DOTSIZE, DOTSIZE);

}

} // end of draw()

17

Page 18: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Windowed Version, Worm ClassCollisions

• The new head position is generated by newHead()

• If the worm should encounter an obstacle (see below), its new direction ishard-coded in fixedOffs[]

1. If nothing is to the immediate left or right, the worm makes a 90 degreeturn

2. Otherwise, it reverses direction

• newHead():

private void newHead(int prevPosn)

{

Point newPt;

int newBearing;

int fixedOffs[] = {-2, 2, -4}; // offsets to avoid an obstacle

newBearing = varyBearing();

newPt = nextPoint(prevPosn, newBearing );

// Get a new position based on a semi-random

// variation of the current position.

if (obs.hits(newPt, DOTSIZE)) {

for (int i=0; i < fixedOffs.length; i++) {

newBearing = calcBearing(fixedOffs[i]);

newPt = nextPoint(prevPosn, newBearing);

if (!obs.hits(newPt, DOTSIZE))

break; // one of the fixed offsets will work

}

}

cells[headPosn] = newPt; // new head position

currCompass = newBearing; // new compass direction

} // end of newHead()

18

Page 19: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Windowed Version, Worm Class Hits

• Checking whther the user has clicked on the worm is performed by nearHead()and touchedAt()

1. nearHead() determines if the head was hit

– A hit is considered to be anywhere within one diameter from the head’scenter

– This will terminate the game

public boolean nearHead(int x, int y)

// is (x,y) near the worm’s head?

{ if (nPoints > 0) {

if( (Math.abs( cells[headPosn].x + RADIUS - x) <= DOTSIZE) &&

(Math.abs( cells[headPosn].y + RADIUS - y) <= DOTSIZE) )

return true;

}

return false;

} // end of nearHead()

2. touchedAt() determines if the body was hit

– Note that no obstacles are added on a body hit

public boolean touchedAt(int x, int y)

// is (x,y) near any part of the worm’s body?

{

int i = tailPosn;

while (i != headPosn) {

if( (Math.abs( cells[i].x + RADIUS - x) <= RADIUS) &&

(Math.abs( cells[i].y + RADIUS - y) <= RADIUS) )

return true;

i = (i+1) % MAXPOINTS;

}

return false;

} // end of touchedAt()

19

Page 20: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Windowed Version, Obstacle Class

• Obstacles are represented as an ArrayList of boxes

• hits() is called by Worm to determine whether the worm has hit an obstacle

• add() adds a new obstacle

• draw() displays the obstacles

• Note that these methods are synchronized as a box could be in the process ofbeing added while the animation loop is accessing the list for drawing

• Code:

public class Obstacles

{

private static final int BOX_LENGTH = 12;

private ArrayList boxes; // arraylist of Rectangle objects

private WormChase wcTop;

public Obstacles(WormChase wc)

{ boxes = new ArrayList();

wcTop = wc;

}

synchronized public void add(int x, int y)

{ boxes.add( new Rectangle(x,y, BOX_LENGTH, BOX_LENGTH));

wcTop.setBoxNumber( boxes.size() ); // report new number of boxes

}

synchronized public boolean hits(Point p, int size)

{

Rectangle r = new Rectangle(p.x, p.y, size, size);

Rectangle box;

for(int i=0; i < boxes.size(); i++) {

box = (Rectangle) boxes.get(i);

if (box.intersects(r)) return true;

}

return false;

} // end of intersects()

synchronized public void draw(Graphics g)

{

Rectangle box;

g.setColor(Color.blue);

for(int i=0; i < boxes.size(); i++) {

box = (Rectangle) boxes.get(i);

g.fillRect( box.x, box.y, box.width, box.height);

}

} // end of draw()

synchronized public int getNumObstacles()

{ return boxes.size(); }

} // end of Obstacles class

20

Page 21: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Applet Version

• The abbreviated class diagram:

21

Page 22: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Applet Version (2)

• There is very little difference between this version and the windowed version

1. WormChaseApplet extends JApplet instead of JFrame2. Applet methods are used for game control instead of WindowListener events

public class WormChaseApplet extends JApplet

{

private WormPanel wp; // where the worm is drawn

// ...

public void init()

{

String str = getParameter("fps");

int fps = (str != null) ? Integer.parseInt(str) : DEFAULT_FPS;

long period = (long) 1000.0/fps;

System.out.println("fps: " + fps + "; period: " + period + " ms");

makeGUI(period);

wp.startGame();

} // end of init()

// ...

public void start()

{ wp.resumeGame(); }

public void stop()

{ wp.pauseGame(); }

public void destroy()

{ wp.stopGame(); }

3. startGame() replaces addNotify()

4. There are some changes re printStats()

22

Page 23: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Full Screen Modes Introduction

• A ”full screen” approach to game display is preferred to the previous methods

– The display window occupies most - if not all - of the display device area

– The programmer generally must provide onscreen buttons for game control,rather than relying on window controls

– It provides a more immersive environment for game play, eliminating thedistraction of other windows, etc.

• Three increasingly fuller screen modes will be presented:

1. Almost full screen

2. Undecorated full screen

3. Full screen exclusive

• Each version adds revisions to the previous, culminating in full screen exclusivemode

23

Page 24: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Almost Full Screen Mode

• The game window consists of a JFrame object

• OS desktop controls are still visible (e.g., menu bar)

• Game control is via the desktop controls, so no need to provide additional GUIcontrols within the game itself

• The abbreviated class diagram:

– Note that this is the same as the diagram presented at the beginning of thisset of slides

24

Page 25: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Almost Full Screen Mode,WormChase

• WormChase

– Class diagram:

– In this version, the size of the JPanel will be programmatically computed

∗ The width and height are based on

1. The JFrame’s size

2. The JFrame’s insets

3. The desktop’s insets

4. Size of any other Swing components in the window

∗ An inset is simply a border component

∗ Once these sizes are known, simply subtract them from the screen’s size

∗ Since the JFrame and GUI element dimensions are not available untilthe game window is created, the app is contructed in stages:

1. Construct the JFrame and GUI elements

2. Get their sizes

3. Calculate the resulting size of the JPanel

4. Build the JPanel

25

Page 26: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Almost Full Screen Mode,WormChase (2)

∗ Since you cannot prevent the user from dragging the game window, theapp is coded to snap the window back into place should the user do so(see addComponentListener( new ComponentAdapter()) in the construc-tor))

∗ Code:import javax.swing.*;

import java.awt.*;

import java.awt.event.*;

public class WormChase extends JFrame implements WindowListener

{

private static int DEFAULT_FPS = 80;

private WormPanel wp;

private JTextField jtfBox; // displays no.of boxes used

private JTextField jtfTime; // displays time spent in game

private int pWidth, pHeight; // dimensions of the panel

public WormChase(long period)

{

super("The Worm Chase");

makeGUI();

pack(); // first one (the GUI doesn’t include the JPanel yet)

setResizable(false); // sizes may change when resizable

calcSizes();

setResizable(true);

Container c = getContentPane();

wp = new WormPanel(this, period, pWidth, pHeight);

c.add(wp, "Center");

pack(); // second, after JPanel added

addWindowListener( this );

addComponentListener( new ComponentAdapter() {

public void componentMoved(ComponentEvent e)

/* Called by the Component listener when the JFrame is

moved. Put it back in its original position. */

{ setLocation(0,0); }

});

setResizable(false);

setVisible(true);

} // end of WormChase() constructor

26

Page 27: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Almost Full Screen Mode,WormChase makeGUI and calcSizes

∗ makeGUI() constructs the text output area

∗ calcSizes() computes the size of the JPanel based on insetsprivate void makeGUI()

{

Container c = getContentPane(); // default BorderLayout used

JPanel ctrls = new JPanel(); // a row of textfields

ctrls.setLayout( new BoxLayout(ctrls, BoxLayout.X_AXIS));

jtfBox = new JTextField("Boxes used: 0");

jtfBox.setEditable(false);

ctrls.add(jtfBox);

jtfTime = new JTextField("Time Spent: 0 secs");

jtfTime.setEditable(false);

ctrls.add(jtfTime);

c.add(ctrls, "South");

} // end of makeGUI()

public void setBoxNumber(int no)

{ jtfBox.setText("Boxes used: " + no); }

public void setTimeSpent(int t)

{ jtfTime.setText("Time Spent: " + t + " secs"); }

private void calcSizes()

/* Calculate the size of the drawing panel to fill the screen, but

leaving room for the JFrame’s title bar and insets, the OS’s insets

(e.g. taskbar) and the textfields under the JPanel.

*/

{

GraphicsConfiguration gc = getGraphicsConfiguration();

Rectangle screenRect = gc.getBounds();

Toolkit tk = Toolkit.getDefaultToolkit();

Insets desktopInsets = tk.getScreenInsets(gc);

Insets frameInsets = getInsets(); // only works after a pack() call

Dimension tfDim = jtfBox.getPreferredSize(); // size of text field

pWidth = screenRect.width - (desktopInsets.left + desktopInsets.right)

- (frameInsets.left + frameInsets.right);

pHeight = screenRect.height - (desktopInsets.top + desktopInsets.bottom)

- (frameInsets.top + frameInsets.bottom)

- tfDim.height;

} // end of calcSizes()

27

Page 28: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Almost Full Screen Mode,WormChase window callbacks and main

public void windowActivated(WindowEvent e)

{ wp.resumeGame(); }

public void windowDeactivated(WindowEvent e)

{ wp.pauseGame(); }

public void windowDeiconified(WindowEvent e)

{ wp.resumeGame(); }

public void windowIconified(WindowEvent e)

{ wp.pauseGame(); }

public void windowClosing(WindowEvent e)

{ wp.stopGame(); }

public void windowClosed(WindowEvent e) {}

public void windowOpened(WindowEvent e) {}

public static void main(String args[])

{

int fps = DEFAULT_FPS;

if (args.length != 0)

fps = Integer.parseInt(args[0]);

long period = (long) 1000.0/fps;

System.out.println("fps: " + fps + "; period: " + period + " ms");

new WormChase(period*1000000L); // ms --> nanosecs

}

} // end of WormChase class

28

Page 29: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Undecorated Full Screen Mode

• This version eliminates the window’s title bar

– Window controls cannot be used for game control

– Must provide on-screen buttons and must explicitly check for user interac-tion

• Class diagram:

29

Page 30: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Undecorated Full Screen Mode,WormChase

• WormChase

– Since not using window to control game, no need for window handlingmethods any longer

– Code:import javax.swing.*;

import java.awt.*;

public class WormChase extends JFrame

{

private static int DEFAULT_FPS = 80;

public WormChase(long period)

{

super("The Worm Chase");

Container c = getContentPane();

c.setLayout( new BorderLayout() );

WormPanel wp = new WormPanel(this, period);

c.add(wp, "Center");

setUndecorated(true); // no borders or title bar

setIgnoreRepaint(true); // turn off all paint events since doing active rendering

pack();

setResizable(false);

setVisible(true);

} // end of WormChase() constructor

public static void main(String args[])

{

int fps = DEFAULT_FPS;

if (args.length != 0)

fps = Integer.parseInt(args[0]);

long period = (long) 1000.0/fps;

System.out.println("fps: " + fps + "; period: " + period + " ms");

new WormChase(period*1000000L); // ms --> nanosecs

}

} // end of WormChase class

30

Page 31: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Undecorated Full Screen Mode,WormPanel

• WormPanel class

– Constructor creates 2 rectangles to serve as buttons:

1. One for pausing game

2. One for quitting

∗ They exhibit usual behavior of buttons (color and text change, etc.)

∗ See testPress() and testMove() methods and calls they make (triggeredby mouse actions), and drawButtons() (called by gameRender())

31

Page 32: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Undecorated Full Screen Mode,WormPanel Constructor

– Code:import javax.swing.*;

import java.awt.event.*;

import java.awt.*;

import java.text.DecimalFormat;

import com.sun.j3d.utils.timer.J3DTimer;

public class WormPanel extends JPanel implements Runnable

{

\\Lots of variable declarations

public WormPanel(WormChase wc, long period)

{

wcTop = wc;

this.period = period;

Toolkit tk = Toolkit.getDefaultToolkit();

Dimension scrDim = tk.getScreenSize();

pWidth = scrDim.width;

pHeight = scrDim.height;

setBackground(Color.white);

setPreferredSize(scrDim);

setFocusable(true);

requestFocus(); // the JPanel now has focus, so receives key events

readyForTermination();

obs = new Obstacles(this);

fred = new Worm(pWidth, pHeight, obs);

addMouseListener( new MouseAdapter() {

public void mousePressed(MouseEvent e)

{ testPress(e.getX(), e.getY()); }

});

addMouseMotionListener( new MouseMotionAdapter() {

public void mouseMoved(MouseEvent e)

{ testMove(e.getX(), e.getY()); }

});

font = new Font("SansSerif", Font.BOLD, 24);

metrics = this.getFontMetrics(font);

pauseArea = new Rectangle(pWidth-100, pHeight-45, 70, 15); //Create buttons

quitArea = new Rectangle(pWidth-100, pHeight-20, 70, 15);

// Stats stuff

}

} // end of WormPanel()

32

Page 33: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Undecorated Full Screen Mode,WormPanel readyForTermination()

– addShutDownHook() (in readyForTermination()) is included in case thegame ends abnormally

∗ It insures that the statistics are displayed no matter what

private void readyForTermination()

{

addKeyListener( new KeyAdapter() {

public void keyPressed(KeyEvent e)

{ int keyCode = e.getKeyCode();

if ((keyCode == KeyEvent.VK_ESCAPE) || (keyCode == KeyEvent.VK_Q) ||

(keyCode == KeyEvent.VK_END) ||

((keyCode == KeyEvent.VK_C) && e.isControlDown()) ) {

running = false;

}

}

});

Runtime.getRuntime().addShutdownHook(new Thread() {

public void run()

{

running = false;

// System.out.println("Shutdown hook executed");

finishOff();

}

});

} // end of readyForTermination()

public void addNotify()

{

super.addNotify(); // creates the peer

if (animator == null || !running) { // start the thread

animator = new Thread(this);

animator.start();

}

} // end of addNotify()

33

Page 34: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Undecorated Full Screen Mode,WormPanel testPress() and testMove()

– isOverPauseButton and isOverQuitButton are Boolean variables

private void testPress(int x, int y)

{

if (isOverPauseButton)

isPaused = !isPaused; // toggle pausing

else if (isOverQuitButton)

running = false;

else {

if (!isPaused && !gameOver) {

if (fred.nearHead(x,y)) { // was mouse pressed near the head?

gameOver = true;

score = (40 - timeSpentInGame) + (40 - boxesUsed);

}

else { // add an obstacle if possible

if (!fred.touchedAt(x,y)) // was the worm’s body untouched?

obs.add(x,y);

}

}

}

} // end of testPress()

private void testMove(int x, int y)

{

if (running) { // stops problems with a rapid move after pressing ’quit’

isOverPauseButton = pauseArea.contains(x,y) ? true : false;

isOverQuitButton = quitArea.contains(x,y) ? true : false;

}

}

public void setBoxNumber(int no)

{ boxesUsed = no; }

34

Page 35: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Undecorated Full Screen Mode,WormPanel run()

public void run()

{

long beforeTime, afterTime, timeDiff, sleepTime;

long overSleepTime = 0L;

int noDelays = 0;

long excess = 0L;

Graphics g;

gameStartTime = J3DTimer.getValue();

prevStatsTime = gameStartTime;

beforeTime = gameStartTime;

running = true;

while(running) {

gameUpdate();

gameRender(); // render the game to a buffer

paintScreen(); // draw the buffer on-screen

afterTime = J3DTimer.getValue();

timeDiff = afterTime - beforeTime;

sleepTime = (period - timeDiff) - overSleepTime;

if (sleepTime > 0) { // some time left in this cycle

try {

Thread.sleep(sleepTime/1000000L); // nano -> ms

}

catch(InterruptedException ex){}

overSleepTime = (J3DTimer.getValue() - afterTime) - sleepTime;

}

else { // sleepTime <= 0; the frame took longer than the period

excess -= sleepTime; // store excess time value

overSleepTime = 0L;

if (++noDelays >= NO_DELAYS_PER_YIELD) {

Thread.yield(); // give another thread a chance to run

noDelays = 0;

}

}

beforeTime = J3DTimer.getValue();

int skips = 0;

while((excess > period) && (skips < MAX_FRAME_SKIPS)) {

excess -= period;

gameUpdate(); // update state but don’t render

skips++;

}

framesSkipped += skips;

// excess = excess % period;

storeStats();

}

finishOff();

} // end of run()

private void gameUpdate()

{ if (!isPaused && !gameOver)

fred.move();

} // end of gameUpdate()

35

Page 36: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Undecorated Full Screen Mode,WormPanel gameRender()

– Note that gameRender() now includes a call to drawButtons()

– drawButtons() handles drawing the buttons appropriately, based on whetherthe mouse is positioned over the button or not

private void gameRender()

{

if (dbImage == null){

dbImage = createImage(pWidth, pHeight);

if (dbImage == null) {

System.out.println("dbImage is null");

return;

}

else

dbg = dbImage.getGraphics();

}

dbg.setColor(Color.white);

dbg.fillRect(0, 0, pWidth, pHeight);

dbg.setColor(Color.blue);

dbg.setFont(font);

dbg.drawString("Average FPS/UPS: " + df.format(averageFPS) + ", " +

df.format(averageUPS), 20, 25);

dbg.drawString("Time Spent: " + timeSpentInGame + " secs", 10, pHeight-15);

dbg.drawString("Boxes used: " + boxesUsed, 260, pHeight-15);

drawButtons(dbg);

dbg.setColor(Color.black);

obs.draw(dbg);

fred.draw(dbg);

if (gameOver)

gameOverMessage(dbg);

} // end of gameRender()

private void drawButtons(Graphics g)

{

g.setColor(Color.black);

if (isOverPauseButton)

g.setColor(Color.green);

g.drawOval( pauseArea.x, pauseArea.y, pauseArea.width, pauseArea.height);

if (isPaused)

g.drawString("Paused", pauseArea.x, pauseArea.y+10);

else

g.drawString("Pause", pauseArea.x+5, pauseArea.y+10);

if (isOverPauseButton)

g.setColor(Color.black);

if (isOverQuitButton)

g.setColor(Color.green);

g.drawOval(quitArea.x, quitArea.y, quitArea.width, quitArea.height);

g.drawString("Quit", quitArea.x+15, quitArea.y+10);

if (isOverQuitButton)

g.setColor(Color.black);

} // drawButtons()

36

Page 37: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Undecorated Full Screen Mode,WormPanel Rest

private void gameOverMessage(Graphics g)

{

String msg = "Game Over. Your Score: " + score;

int x = (pWidth - metrics.stringWidth(msg))/2;

int y = (pHeight - metrics.getHeight())/2;

g.setColor(Color.red);

g.setFont(font);

g.drawString(msg, x, y);

} // end of gameOverMessage()

private void paintScreen()

{

Graphics g;

try {

g = this.getGraphics();

if ((g != null) && (dbImage != null))

g.drawImage(dbImage, 0, 0, null);

Toolkit.getDefaultToolkit().sync();

g.dispose();

}

catch (Exception e) // quite commonly seen at applet destruction

{ System.out.println("Graphics error: " + e); }

} // end of paintScreen()

// private void storeStats()

private void finishOff()

{

if (!finishedOff) {

finishedOff = true;

printStats();

System.exit(0);

}

} // end of finishedOff()

// private void printStats()

} // end of WormPanel class

37

Page 38: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Full Screen Exclusive Mode

• Full screen exclusive mode gives the programmer almost direct access to thescreen

• It bypasses most of AWT and Swing access in favor of accessing the graphicscard’s capabilities

• This mode uses VRAM, which may be grabbed by the OS

– This means that the image buffer must be re-created on each iteration

• Hardware acceleration primarily a Windows feature

– Neither Linux nor Solaris provide direct access to VRAM

• Class diagram:

• Note that WormPanel has been incorporated into WormChase, since gameevents are now handled elsewhere, leaving little for WormChase to do

– The undecorated mode could have been coded this way as well, since that’swhere the functionality disappeared originally

38

Page 39: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Full Screen Exclusive Mode,WormChase constructor

• WormChase class

– Note that addNotify() is replaced by gameStart() (last line of constructor)

– Code:import javax.swing.*;

import java.awt.event.*;

import java.awt.*;

import java.text.DecimalFormat;

import java.awt.image.BufferStrategy;

import com.sun.j3d.utils.timer.J3DTimer;

public class WormChase extends JFrame implements Runnable

{

//Lots of variable declarations

public WormChase(long period)

{

super("Worm Chase");

this.period = period;

initFullScreen();

readyForTermination();

obs = new Obstacles(this);

fred = new Worm(pWidth, pHeight, obs);

addMouseListener( new MouseAdapter() {

public void mousePressed(MouseEvent e)

{ testPress(e.getX(), e.getY()); }

});

addMouseMotionListener( new MouseMotionAdapter() {

public void mouseMoved(MouseEvent e)

{ testMove(e.getX(), e.getY()); }

});

font = new Font("SansSerif", Font.BOLD, 24);

metrics = this.getFontMetrics(font);

pauseArea = new Rectangle(pWidth-100, pHeight-45, 70, 15);

quitArea = new Rectangle(pWidth-100, pHeight-20, 70, 15);

// Stats stuff

// ...

}

gameStart();

} // end of WormChase()

39

Page 40: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Full Screen Exclusive Mode,WormChase initFullScreen()

– initFullScreen() sets up full screen exclusive mode

∗ getLocalGraphicsEnvironment() returns a GraphicsEnvironment() objectthat represents a 2D environment that is available to the Java VM

· This environment consists of the available screens and fonts

· The screens are represented by GraphicsDevice objects

· getDefaultScreenDevice() returns the default device

∗ GraphicsDevice gd provides access to the graphics card

∗ If full screen exclusive mode is not available, the program quits

· It should more properly switch to another mode

private void initFullScreen()

{

GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();

gd = ge.getDefaultScreenDevice();

setUndecorated(true); // no menu bar, borders, etc. or Swing components

setIgnoreRepaint(true); // turn off all paint events since doing active rendering

setResizable(false);

if (!gd.isFullScreenSupported()) {

System.out.println("Full-screen exclusive mode not supported");

System.exit(0);

}

gd.setFullScreenWindow(this); // switch on full-screen exclusive mode

showCurrentMode();

// setDisplayMode(1280, 1024, 32);

// reportCapabilities();

pWidth = getBounds().width;

pHeight = getBounds().height;

setBufferStrategy();

} // end of initFullScreen()

40

Page 41: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Full Screen Exclusive Mode,WormChase reportCapabilities() and getFlipText()

– reportCapabilities() queries the capabilities of the graphics device

∗ Class GraphicsConfiguration() represents the properties of a graphicsenvironment (color models supported, buffers, etc.)

∗ getFlipText() indicates how page flipping (a form of double buffering -see below) is handled

private void reportCapabilities()

{

GraphicsConfiguration gc = gd.getDefaultConfiguration();

ImageCapabilities imageCaps = gc.getImageCapabilities();

System.out.println("Image Caps. isAccelerated: " + imageCaps.isAccelerated() );

System.out.println("Image Caps. isTrueVolatile: " + imageCaps.isTrueVolatile());

BufferCapabilities bufferCaps = gc.getBufferCapabilities();

System.out.println("Buffer Caps. isPageFlipping: " + bufferCaps.isPageFlipping());

System.out.println("Buffer Caps. Flip Contents: " +

getFlipText(bufferCaps.getFlipContents()));

System.out.println("Buffer Caps. Full-screen Required: " +

bufferCaps.isFullScreenRequired());

System.out.println("Buffer Caps. MultiBuffers: " + bufferCaps.isMultiBufferAvailable());

} // end of reportCapabilities()

private String getFlipText(BufferCapabilities.FlipContents flip)

{

if (flip == null)

return "false";

else if (flip == BufferCapabilities.FlipContents.UNDEFINED)

return "Undefined";

else if (flip == BufferCapabilities.FlipContents.BACKGROUND)

return "Background";

else if (flip == BufferCapabilities.FlipContents.PRIOR)

return "Prior";

else // if (flip == BufferCapabilities.FlipContents.COPIED)

return "Copied";

} // end of getFlipTest()

41

Page 42: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Full Screen Exclusive Mode,WormChase setBufferStrategy()

– setBufferStrategy() sets up the buffer strategy

∗ invokeAndWait() starts a thread and waits until after the thread returns

∗ Note: A thread is used here to prevent a deadlock situation that mayoccur prior to J2SE5.0

– createBufferStrategy() (inherited from class Canvas) selects the buffer strat-egy

∗ It tries the following strategies in order:

1. Page flipping

2. Accelerated copy

3. Non-accelerated copy

∗ Page flipping is anther way to implement double buffering

· As usual, two buffers are used

· In this approach

1. Buffer A is displayed

2. Buffer B is written to

3. When drawing is complete, the video pointer is set to B

4. Now, A will be written to while B is displayed

· This is much faster than copying one buffer to anotherprivate void setBufferStrategy()

{ try {

EventQueue.invokeAndWait( new Runnable() {

public void run()

{ createBufferStrategy(NUM_BUFFERS); }

});

}

catch (Exception e) {

System.out.println("Error while creating buffer strategy");

System.exit(0);

}

try { // sleep to give time for the buffer strategy to be carried out

Thread.sleep(500); // 0.5 sec

}

catch(InterruptedException ex){}

bufferStrategy = getBufferStrategy(); // store for later

} // end of setBufferStrategy()

// private void readyForTermination()

// private void gameStart()

// private void testPress(int x, int y)

// private void testMove(int x, int y)

// public void setBoxNumber(int no)

42

Page 43: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Full Screen Exclusive Mode,WormChase run()

– run(): Nothing new here

public void run()

{

long beforeTime, afterTime, timeDiff, sleepTime;

long overSleepTime = 0L;

int noDelays = 0;

long excess = 0L;

gameStartTime = J3DTimer.getValue();

prevStatsTime = gameStartTime;

beforeTime = gameStartTime;

running = true;

while(running) {

gameUpdate();

screenUpdate();

afterTime = J3DTimer.getValue();

timeDiff = afterTime - beforeTime;

sleepTime = (period - timeDiff) - overSleepTime;

if (sleepTime > 0) { // some time left in this cycle

try {

Thread.sleep(sleepTime/1000000L); // nano -> ms

}

catch(InterruptedException ex){}

overSleepTime = (J3DTimer.getValue() - afterTime) - sleepTime;

}

else { // sleepTime <= 0; the frame took longer than the period

excess -= sleepTime; // store excess time value

overSleepTime = 0L;

if (++noDelays >= NO_DELAYS_PER_YIELD) {

Thread.yield(); // give another thread a chance to run

noDelays = 0;

}

}

beforeTime = J3DTimer.getValue();

int skips = 0;

while((excess > period) && (skips < MAX_FRAME_SKIPS)) {

excess -= period;

gameUpdate(); // update state but don’t render

skips++;

}

framesSkipped += skips;

storeStats();

}

finishOff();

} // end of run()

// private void gameUpdate()

43

Page 44: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Full Screen Exclusive Mode,WormChase screenUpdate()

– screenUpdate()

∗ bufferStrategy.getDrawGraphics() retrieves a graphics context for draw-ing

∗ The buffer strategy initiated by initFullScreen() (and implemented bycreateBufferStrategy()) is used here

∗ the if/else statement checks to see whether the OS has pre-empted theVRAM

· If it has, the contents have been lost and need to be redrawn (whichwill be taken care of on the next iteration)

private void screenUpdate()

// use active rendering

{

try {

gScr = bufferStrategy.getDrawGraphics();

gameRender(gScr);

gScr.dispose();

if (!bufferStrategy.contentsLost())

bufferStrategy.show();

else

System.out.println("Contents Lost");

// Sync the display on some systems.

// (on Linux, this fixes event queue problems)

Toolkit.getDefaultToolkit().sync();

}

catch (Exception e)

{ e.printStackTrace();

running = false;

}

} // end of screenUpdate()

44

Page 45: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Full Screen Exclusive Mode,WormChase gameRender()

– gameRender() same as before

∗ No need for Image object, as bufferStrategy() supplies it as a parameter

private void gameRender(Graphics gScr)

{

// clear the background

gScr.setColor(Color.white);

gScr.fillRect (0, 0, pWidth, pHeight);

gScr.setColor(Color.blue);

gScr.setFont(font);

// report frame count & average FPS and UPS at top left

// gScr.drawString("Frame Count " + frameCount, 10, 25);

gScr.drawString("Average FPS/UPS: " + df.format(averageFPS) + ", " +

df.format(averageUPS), 20, 25); // was (10,55)

// report time used and boxes used at bottom left

gScr.drawString("Time Spent: " + timeSpentInGame + " secs", 10, pHeight-15);

gScr.drawString("Boxes used: " + boxesUsed, 260, pHeight-15);

// draw the pause and quit ’buttons’

drawButtons(gScr);

gScr.setColor(Color.black);

// draw game elements: the obstacles and the worm

obs.draw(gScr);

fred.draw(gScr);

if (gameOver)

gameOverMessage(gScr);

} // end of gameRender()

45

Page 46: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Full Screen Exclusive Mode,WormChase restoreScreen()

– Quitting the game same as before

∗ restoreScreen() called to exit full screen exclusive mode

// private void drawButtons(Graphics g)

// private void gameOverMessage(Graphics g)

// private void storeStats()

// private void finishOff()

// private void printStats()

private void restoreScreen()

/* Switch off full screen mode. This also resets the

display mode if it’s been changed.

*/

{ Window w = gd.getFullScreenWindow();

if (w != null)

w.dispose();

gd.setFullScreenWindow(null);

} // end of restoreScreen()

46

Page 47: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Full Screen Exclusive Mode

– The display mode

∗ Setting the display mode can increase performance

1. Using a lower resolution will speed up double buffering (if page flip-ping not used)

2. Using the same raster depth and data representation as the displaydevice will prevent the need to convert between representations

∗ getDisplayMode() determines the mode of the display device

∗ setDisplayMode()

· Checks to see if the display mode can be changed

· If so, it checks to see if the given mode is supported

// ------------------ display mode methods -------------------

private void setDisplayMode(int width, int height, int bitDepth)

{

if (!gd.isDisplayChangeSupported()) {

System.out.println("Display mode changing not supported");

return;

}

if (!isDisplayModeAvailable(width, height, bitDepth)) {

System.out.println("Display mode (" + width + "," +

height + "," + bitDepth + ") not available");

return;

}

DisplayMode dm = new DisplayMode(width, height, bitDepth,

DisplayMode.REFRESH_RATE_UNKNOWN); // any refresh rate

try {

gd.setDisplayMode(dm);

System.out.println("Display mode set to: (" + width + "," +

height + "," + bitDepth + ")");

}

catch (IllegalArgumentException e)

{ System.out.println("Error setting Display mode (" + width + "," +

height + "," + bitDepth + ")"); }

try { // sleep to give time for the display to be changed

Thread.sleep(1000); // 1 sec

}

catch(InterruptedException ex){}

} // end of setDisplayMode()

47

Page 48: Chapters 3 and 4: Game Display Modes - Intro These two ...djmoon/gaming/gaming-notes/kgp-c3-4.pdfChapters 3 and 4: Game Display Modes - Intro These two chapters implement a simple

Chapters 3 and 4: Game Display Modes - Full Screen Exclusive Mode (2)

private boolean isDisplayModeAvailable(int width, int height, int bitDepth)

{

DisplayMode[] modes = gd.getDisplayModes();

showModes(modes);

for(int i = 0; i < modes.length; i++) {

if (width == modes[i].getWidth() && height == modes[i].getHeight() &&

bitDepth == modes[i].getBitDepth())

return true;

}

return false;

} // end of isDisplayModeAvailable()

private void showModes(DisplayMode[] modes)

// pretty print the display mode information in modes

{

System.out.println("Modes");

for(int i = 0; i < modes.length; i++) {

System.out.print("(" + modes[i].getWidth() + "," +

modes[i].getHeight() + "," +

modes[i].getBitDepth() + "," +

modes[i].getRefreshRate() + ") " );

if ((i+1)%4 == 0)

System.out.println();

}

System.out.println();

} // end of showModes()

private void showCurrentMode()

{

DisplayMode dm = gd.getDisplayMode();

System.out.println("Current Display Mode: (" +

dm.getWidth() + "," + dm.getHeight() + "," +

dm.getBitDepth() + "," + dm.getRefreshRate() + ") " );

}

public static void main(String args[])

{

int fps = DEFAULT_FPS;

if (args.length != 0)

fps = Integer.parseInt(args[0]);

long period = (long) 1000.0/fps;

System.out.println("fps: " + fps + "; period: " + period + " ms");

new WormChase(period*1000000L); // ms --> nanosecs

} // end of main()

} // end of WormChase class

48