Anatomy of a Game Engine by Richard Baldwin - HTML preview

PLEASE NOTE: This is an HTML preview only and some elements such as links or page numbers may be incorrect.
Download the book in PDF, ePub, Kindle for a complete version.

Financial : Although the Connexions site makes it possible for you to download a PDF file for

this module at no charge, and also makes it possible for you to purchase a pre-printed version

of the PDF file, you should be aware that some of the HTML elements in this module may not

translate well into PDF.

I also want you to know that, I receive no financial compensation from the Connexions

website even if you purchase the PDF version of the module.

In the past, unknown individuals have copied my modules from cnx.org, converted them to

Kindle books, and placed them for sale on Amazon.com showing me as the author. I neither

receive compensation for those sales nor do I know who does receive compensation. If you

purchase such a book, please be aware that it is a copy of a module that is freely available on

cnx.org and that it was made and published without my prior knowledge.

Affiliation : I am a professor of Computer Information Technology at Austin Community

College in Austin, TX.

8.10. Complete program listing

A complete listing of the program discussed in this module is provided in Listing 5 .

/*Slick0170java

Copyright 2013, R.G.Baldwin

Cause a ladybug sprite to move inside the game window by

pressing the arrow keys or the left and right mouse

buttons. The mouse pointer must be inside the game window

for the mouse buttons to move the sprite.

Right arrow or right mouse button: move right

Left arrow or left mouse button: move left

Up arrow: move up

Down arrow: move down

The program also gets and displays the X and Y

coordinates of the mouse pointer.

Much of this program is identical to the earlier program

named Slick0150a.java.

Tested using JDK 1.7 under WinXP

*********************************************************/

import org.newdawn.slick.AppGameContainer;

import org.newdawn.slick.BasicGame;

import org.newdawn.slick.GameContainer;

import org.newdawn.slick.Graphics;

import org.newdawn.slick.Image;

import org.newdawn.slick.SlickException;

import org.newdawn.slick.Input;

public class Slick0170 extends BasicGame{

Image bug = null;

Image background = null;

float backgroundWidth;

float backgroundHeight;

float bugX = 100;

float bugY = 100;

float bugWidth;

float bugHeight;

float xStep = 4.0f;//horizontal step size

float yStep = 3.0f;//vertical step size

float bugScale = 0.75f;//drawing scale factor

//Frame rate we would like to see and maximum frame

// rate we will allow.

int targetFPS = 60;

//This is new code relative to Slick0150a.java

int mouseX = 0;

int mouseY = 0;

//----------------------------------------------------//

public Slick0170(){//constructor

//Set the title

super("Slick0170, baldwin");

}//end constructor

//----------------------------------------------------//

public static void main(String[] args)

throws SlickException{

AppGameContainer app = new AppGameContainer(

new Slick0170(),414,307,false);

app.start();

}//end main

//----------------------------------------------------//

@Override

public void init(GameContainer gc)

throws SlickException {

bug = new Image("ladybug.png");

background = new Image("background.jpg");

backgroundWidth = background.getWidth();

backgroundHeight = background.getHeight();

bugWidth = bug.getWidth()*bugScale;

bugHeight = bug.getHeight()*bugScale;

System.out.println(

"backgroundWidth: " + backgroundWidth);

System.out.println(

"backgroundHeight: " + backgroundHeight);

System.out.println("bugWidth: " + bugWidth);

System.out.println("bugHeight: " + bugHeight);

gc.setTargetFrameRate(targetFPS);//set frame rate

}//end init

//----------------------------------------------------//

@Override

public void update(GameContainer gc, int delta)

throws SlickException{

//Most of the code in this method is different from

// the code in Slick0150a.java.

//Get a reference to the Input object.

Input input = gc.getInput();

//Control horizontal bug position by pressing the

// arrow keys or pressing the left and right mouse

// buttons.

if(input.isKeyDown(Input.KEY_RIGHT) ||

input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON)){

bugX += xStep;

}//end if

if(input.isKeyDown(Input.KEY_LEFT) ||

input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON)){

bugX -= xStep;

}//end if

//Control vertical bug position by pressing the arrow

// keys. Vertical bug position cannot be controlled by

// pressing mouse buttons.

if(input.isKeyDown(Input.KEY_UP)){

bugY -= yStep;

}//end if

if(input.isKeyDown(Input.KEY_DOWN)){

bugY += yStep;

}//end if

//Test for collisions with the sides of the game

// window and stop moving the bug when a collision

// occurs.

if(bugX + bugWidth >= backgroundWidth){

//Set the position to the right edge less the width

// of the sprite.

bugX = backgroundWidth - bugWidth;

}//end if

//Continue testing for collisions with the edges.

if(bugX <= 0){

bugX = 0;

}//end if

if(bugY + bugHeight >= backgroundHeight){

bugY = backgroundHeight - bugHeight;

}//end if

if(bugY <= 0){

bugY = 0;

}//end if

//Get and save the X and Y coordinates of the mouse

// pointer.

mouseX = input.getMouseX();

mouseY = input.getMouseY();

}//end update

//----------------------------------------------------//

public void render(GameContainer gc, Graphics g)

throws SlickException{

//set the drawing mode to honor transparent pixels

g.setDrawMode(g.MODE_NORMAL);

//Draw the background to erase the previous picture.

background.draw(0,0);

//Draw the bug in its new location.

bug.draw(bugX,bugY,bugScale);

//Display the location of the mouse pointer. This is

// new code relative to Slick0150a.java

g.drawString(

"X: " + mouseX + " Y: " + mouseY,100f,10f);

}//end render

}//end class Slick0170

//======================================================//

Figure 8.6. Listing 5. Source code for the program named Slick0170.

Listing 5. Source code for the program named Slick0170.

-end-

Solutions

Chapter 9. Slick0180: Sprite sheet animation, part 1*

It is licensed under the Creative Commons Attribution License:

http://creativecommons.org/licenses/by/3.0/

2013/02/05 19:16:54 -0600

Summary

Learn to use objects of the Slick2D SpriteSheet class and the Animation class to perform simple

spritesheet animation.

9.1. Table of Contents

Preface

Viewing tip

Images

Listings

Preview

General background information

The SpriteSheet class

The Animation class

Discussion and sample code

The class named Slick0180

The init method

The update method

The render method

Run the program

Summary

What's next?

Miscellaneous

Complete program listing

9.2. Preface

Viewing tip

Images

Listings

This module is one in a collection of modules designed to teach you about the anatomy of a game

engine.

Although the modules in this collection will concentrate on the Java game library named Slick2D,

the concepts involved and the knowledge that you will gain is applicable to different game engines

written in different programming languages as well.

The purpose of this module is to teach you how to use objects of the SpriteSheet class and the

Animation class to perform simple sprite sheet animation.

Viewing tip

I recommend that you open another copy of this module in a separate browser window and use the

following links to easily find and view the images and listings while you are reading about them.

Images

Image 1 . The sprite sheet.

Image 2 . Random screen shot of the animation in action.

index-158_1.jpg

Image 3 . Random screen shot of the animation in action.

Image 4 . Random screen shot of the animation in action.

Listings

Listing 1 . Beginning of the class named Slick0180.

Listing 2 . Beginning of the init method.

Listing 3 . Create a SpriteSheet object.

Listing 4 . Create a new Animation object.

Listing 5 . Set frame rate and display location.

Listing 6 . The update method.

Listing 7 . The render method.

Listing 8 . Source code for Slick0180 .

9.3. Preview

I will present a program that uses the top row of sprites from the sprite sheet shown in Image 1

along with a SpriteSheet object and an Animation object to produce an animation of a dog

playing. (Note that the overall sprite sheet image is quite small, and the image shown in Image 1

was enlarged for this presentation.)

Figure 9.1. Image 1. The sprite sheet.

Image 1. The sprite sheet.

index-159_1.jpg

index-159_2.jpg

index-159_3.jpg

Image 2 , Image 3 , and Image 4 show random screen shots taken while the animation was running.

Figure 9.2. Image 2. Random screen shot of the animation in action.

Image 2. Random screen shot of the animation in action.

.

Figure 9.3. Image 3. Random screen shot of the animation in action.

Image 3. Random screen shot of the animation in action.

.

Figure 9.4. Image 4. Random screen shot of the animation in action.

Image 4. Random screen shot of the animation in action.

Operating characteristics

The program uses only the five sprites in the top row of Image 1 . The five sprites in the bottom row are ignored. (A program that uses all ten sprites in both rows will be presented in the next

module.)

By default, the program displays one cycle of five sprites each second. (Each sprite is displayed

for 200 milliseconds, or 0.2 seconds.)

Clock time

As you can see in Image 4 , clock time in seconds is displayed below the animation. That makes it easy to visually correlate the repetition rate with the clock.

Repetition rate is independent of the frame rate

The time that each image of the dog is displayed is independent of the frame rate. This can be

demonstrated by changing the value of a variable named targetDelta and observing the

relationship between the repetition rate and the clock. However, best results are achieved by

keeping targetDelta less than the display time for each sprite ( duration ) .

What you have learned

In the previous module, you learned how to use the following methods of the Input class to get user input:

isKeyDown

isMouseButtonDown

getMouseX

getMouseY

What you will learn

In this module, you will learn how to use objects of the SpriteSheet class and the Animation

class to perform simple sprite sheet animation. In the next module, you will learn how to perform

more complex animation.

9.4. General background information

The SpriteSheet class

The Animation class

The SpriteSheet class

Sprite sheets are individual sprites (or images) combined into a single image as shown in Image 1

. Slick2D provides the SpriteSheet class that makes it relatively easy for you to access each of the

sub-images of the sheet as separate images in your program.

The SpriteSheet class assumes that all the images are evenly spaced. It splits the source image

into an even grid of cells and allows you to access the image in each cell as a separate image.

(Slick2D also provides the capability to work with packed sprite sheets with fewer restrictions on

the organization of the sprite sheet.)

The Animation class

A series of images

Since well before the first Disney movies, animations have been created by displaying a series of

images one after the other.

Each image (or frame) is typically displayed for the same amount of time, but that is not always

the case, as will be demonstrated by the program in the next module.

Slick2D provides a class named Animation that does most of the heavy lifting in the display of an

animation.

Create, populate, and configure the object

There are several different ways to create, populate, and configure an Animation object

containing a series of images, with the same or different display durations for the images.

Displaying the images

By default, calling one of several overloaded draw methods on the Animation object causes it to

display the sequence of images and to start over when the last image has been displayed. However,

that behavior can be overridden in order to provide more customized behavior.

(It is actually more complicated that that, as you will see later in the discussion of the render

method.)

Animations can be stopped, started and restarted (returning to the first frame of the animation) .

The capabilities of the Animation class go far beyond those illustrated in this module and the

next.

9.5. Discussion and sample code

The class named Slick0180

The init method

The update method

The render method

The class named Slick0180

Will discuss in fragments

A complete listing of the program named Slick0180 is provided in Listing 8 . I will break the program down and discuss it in fragments.

Listing 1 shows the beginning of the class named Slick0180 down through the main method.

public class Slick0180 extends BasicGame{

Image spriteSheetImage = null;

float spriteSheetWidth;

float spriteSheetHeight;

int spritesPerRow = 5;

int spritesPerColumn = 2;

int targetDelta = 16;//msec

int duration = 200;//time to display each sprite

long totalTime = 0;//accumulate total time for display

SpriteSheet spriteSheet;

Animation animation;

int spriteWidth;

int spriteHeight;

float spriteX = 0;//sprite drawing location

float spriteY = 0;

//----------------------------------------------------//

public Slick0180(){

//Call to superclass constructor is required.

super("Slick0180, Baldwin.");

}//end constructor

//----------------------------------------------------//

public static void main(String[] args)

throws SlickException{

AppGameContainer app = new AppGameContainer(

new Slick0180(),450,120,false);

app.start();//this statement is required

}//end main

Figure 9.5. Listing 1. Beginning of the class named Slick0180.

Listing 1. Beginning of the class named Slick0180.

Instance variables

Listing 1 declares a number of instance variables. The purpose of these variables should become clear based on their names and their usage that I will discuss later.

The constructor and the main method

There is nothing new in the constructor and the main method in Listing 1 .

The init method

The init method begins in Listing 2 . The embedded comments should provide a sufficient explanation of the code in Listing 2 .

public void init(GameContainer gc)

throws SlickException {

spriteSheetImage = new Image("Slick0180a1.png");

//Enlarge the sprite sheet.

Image temp = spriteSheetImage.getScaledCopy(580,224);

spriteSheetImage = temp;

//Get, save, and display the width and the height

// of the sprite sheet.

spriteSheetWidth = spriteSheetImage.getWidth();

spriteSheetHeight = spriteSheetImage.getHeight();

System.out.println(

"spriteSheetWidth: " + spriteSheetWidth);

System.out.println(

"spriteSheetHeight: " + spriteSheetHeight);

//Compute the width and height of the individual

// sprite images.

spriteWidth = (int)(spriteSheetWidth/spritesPerRow);

spriteHeight =

(int)(spriteSheetHeight/spritesPerColumn);

Figure 9.6. Listing 2. Beginning of the init method.

Listing 2. Beginning of the init method.

Create a SpriteSheet object

Listing 3 creates a new SpriteSheet object based on the sprite sheet image along with the width and height of the individual sprites.

//Instantiate a new Spritesheet object based on the

// width and height of the tiles.

spriteSheet = new SpriteSheet(spriteSheetImage,

spriteWidth,

spriteHeight);

Figure 9.7. Listing 3. Create a SpriteSheet object.

Listing 3. Create a SpriteSheet object.

Create a new Animation object

Listing 4 creates a new Animation object that will process the SpriteSheet object instantiated in

Listing 3 .

//Create a new animation based on a selection of

// sprites from the sprite sheet.

animation = new Animation(spriteSheet,

0,//first column

0,//first row

4,//last column

0,//last row

true,//horizontal

duration,//display time

true//autoupdate

);

Figure 9.8. Listing 4. Create a new Animation object.

Listing 4. Create a new Animation object.

Constructor parameters

Obviously, the first parameter to the constructor for the Animation class specifies the

SpriteSheet object.

The second and third parameters specify that the first image in the sequence should be the top-left

image in Image 1 .

The fourth and fifth parameters specify that the last image in the sequence should be the top-right

image in Image 1 .

The true value for the sixth parameter specifies that the images should be scanned horizontally.

The duration value in the seventh parameter specifies that each image should be displayed for 200

milliseconds.

The true value for the last parameter specifies that the display should continue cycling through the

images until the animation is stopped.

Set frame rate and display location

The code is Listing 5 sets the frame rate and specifies the drawing location. The drawing location is the location within the game window where the sprite will be displayed.

gc.setShowFPS(true);//show FPS

////set frame rate

gc.setTargetFrameRate((int)(1000/targetDelta));

//Set drawing location. This is the location within

// the game window where the sprite will be displayed.