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.

and animate swarms of sprite objects.

10.9. Miscellaneous

This section contains a variety of miscellaneous information.

Housekeeping material

Module name: Slick0190-Sprite sheet animation, part 2

File: Slick0190.htm

Published: 02/06/13

Disclaimers:

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.

10.10. Complete program listing

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

/*Slick0190.java

Copyright 2013, R.G.Baldwin

Fairly complex animation using a sprite sheet.

Sprite moves to right during first third of the

animation. Sprite remains stationary during second third

of the animation. Sprite moves to the left back to the

starting point during the last third of the animation.

Much more complicated than Slick0180 for several reasons

including the following:

The sprite is moved horizontally during a portion but not

all of the animation. Movement must be synchronized with

the animation frame counter.

The sprite sheet contains only images of the dog facing

to the left. However, images of the dog facing to the

right are also required. This requires that each image

on the sprite sheet be extracted and flipped horizontally

before being fed to the Animation object for half of

the animation sequence.

The display duration for images from the first row is

shorter than for images from the second row.

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.SpriteSheet;

import org.newdawn.slick.Animation;

import org.newdawn.slick.Color;

public class Slick0190 extends BasicGame{

Image spriteSheetImage = null;

float spriteSheetWidth;

float spriteSheetHeight;

int spritesPerRow = 5;

int spritesPerColumn = 2;

int spriteWidth;

int spriteHeight;

int targetDelta = 16;//msec

SpriteSheet spriteSheet;

Animation animation = new Animation();

//Horizontal and vertical drawing coordinates.

float spriteX = 0;

float spriteY = 0;

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

public Slick0190(){

//Call to superclass constructor is required.

super("Slick0190, Baldwin.");

}//end constructor

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

public static void main(String[] args)

throws SlickException{

AppGameContainer app = new AppGameContainer(

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

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

}//end main

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

@Override

public void init(GameContainer gc)

throws SlickException {

//Create a SpriteSheet object

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

//Enlarge the sprite sheet.

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

spriteSheetImage = temp;

spriteSheetWidth = spriteSheetImage.getWidth();

spriteSheetHeight = spriteSheetImage.getHeight();

spriteWidth = (int)(spriteSheetWidth/spritesPerRow);

spriteHeight =

(int)(spriteSheetHeight/spritesPerColumn);

//Instantiate a new spriteSheet object based on the

// width and height of the individual tiles on the

// sheet.

spriteSheet = new SpriteSheet(spriteSheetImage,

spriteWidth,

spriteHeight);

//Populate the Animation object

//Begin by adding four sets of five sprites from the

// top row with the images flipped to face right.

for(int cntr = 0;cntr < 4;cntr++){

for(int cnt = 0;cnt < 5;cnt++){

animation.addFrame(

spriteSheet.getSprite(cnt,0).getFlippedCopy(

true,false),100);

}//end inner loop

}//end outer loop

//Add two sets of five sprites from the bottom row

// with the images flipped to face right.

for(int cntr = 0;cntr < 2;cntr++){

for(int cnt = 0;cnt < 5;cnt++){

animation.addFrame(

spriteSheet.getSprite(cnt,1).getFlippedCopy(

true,false),400);

}//end inner loop

}//end outer loop

//Add two sets of five sprites from the bottom row

// with the images facing left.

for(int cntr = 0;cntr < 2;cntr++){

for(int cnt = 0;cnt < 5;cnt++){

animation.addFrame(

spriteSheet.getSprite(cnt,1),400);

}//end inner loop

}//end outer loop

//Add four sets of five sprites from the top row with

// the images facing left

for(int cntr = 0;cntr < 4;cntr++){

for(int cnt = 0;cnt < 5;cnt++){

animation.addFrame(

spriteSheet.getSprite(cnt,0),100);

}//end for loop

}//end for loop

gc.setShowFPS(true);//display FPS

//Set frame rate

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

}//end init

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

@Override

public void update(GameContainer gc, int delta)

throws SlickException{

int stepSize = 15;//Distance the sprite moves

int frame = animation.getFrame();//animation frame

int oneThird = animation.getFrameCount()/3;

//Treat the entire animation in thirds with regard

// to sprite movement. Move to the right during first

// third. Stay stationary during second third. Move

// to left back to starting point during last third.

if(frame < oneThird){

//Sprite is moving to the right. Compute the new

// position.

spriteX = frame*stepSize;

}else if(frame < 2*oneThird){

//Sprite is stationary. Don't change position

}else if(frame < 3*oneThird){

//Cause the sprite to turn around and start

// moving to the left toward the starting point.

//Reduce frame count by number of frames during

// which the sprite wasn't moving.

frame -= oneThird;

//Compute the new position.

spriteX = (2*oneThird - frame)*stepSize;

}//end else if

}//end update

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

public void render(GameContainer gc, Graphics g)

throws SlickException{

g.setDrawMode(g.MODE_NORMAL);

g.setBackground(Color.gray);

animation.draw(spriteX,spriteY);

}//end render

}//end class Slick0190

Figure 10.15. Listing 11. Source code for Slick0190.

Listing 11. Source code for Slick0190.

-end-

Solutions

Chapter 11. Slick0200: Developing a sprite class*

It is licensed under the Creative Commons Attribution License:

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

2013/02/06 10:50:50 -0600

Summary

Learn how to develop a Sprite class from which you can instantiate and animate swarms of sprite

objects.

11.1. Table of Contents

Preface

Viewing tip

Images

Listings

Preview

General background information

Discussion and sample code

The class named Sprite01

The class named Slick0200

The init method

The update method

The render method

Run the program

Summary

What's next?

Miscellaneous

Complete program listings

11.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 develop a sprite class (see Sprite01 ) from which you can instantiate and animate swarms of sprite objects.

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 . Graphic output from program named Slick0200.

Image 2 . Graphic output from the earlier program.

Listings

Listing 1 . Beginning of the class named Sprite01.

Listing 2 . Beginning of the class named Slick0200.

Listing 3 . Beginning of the init method.

Listing 4 . Populate the array.

Listing 5 . The update method.

Listing 6 . The move method of the Sprite01 class.

Listing 7 . The edgeBounce method of the Sprite01 class.

Listing 8 . The render method.

Listing 9 . The draw method of the Sprite01 class.

Listing 10 . Source code for the program named Slick0200.

Listing 11 . Source code for the sprite class named Sprite01.

11.3. Preview

I will present and explain a program that uses a class named Sprite01 (see Listing 11 ) to produce an animation of 1000 ladybug sprite objects flying around inside the game window as shown in

Image 1 .

index-196_1.jpg

Figure 11.1. Image 1. Graphic output from program named Slick0200.

Image 1. Graphic output from program named Slick0200.

The frame rate

As you can see from the text in the upper-left corner of Image 1 , the program is running at 62

frames per second. My rather old desktop computer can maintain this frame rate up to about 7000

sprite objects. Beyond that, it can no longer handle the computing load and the frame rate begins

to decrease.

What you have learned

In the previous module, you learned how to use objects of the SpriteSheet class and the

Animation class to perform relatively complex sprite sheet animations.

What you will learn

In this module, you will learn how to develop a sprite class from which you can instantiate and

animate swarms of sprite objects. In the next two modules, you will learn how to put that class to

work.

11.4. General background information

While the Slick2D library provides many useful classes, there is nothing to stop you from

developing your own classes to work in combination with the Slick2D library classes. That is the

index-197_1.jpg

thrust of this module.

In an earlier module titled Slick0150: A first look at sprite motion, collision detection, and

timing control , you learned how to cause a single sprite to bounce around inside the game

window as shown in Image 2 .

Figure 11.2. Image 2. Graphic output from the earlier program.

Image 2. Graphic output from the earlier program.

Adding many more sprites would have been difficult

While it would have been possible to add more sprites to the animation by expanding the code

used in that program, the code would have quickly gotten out of hand without the use of a sprite

class and sprite objects. (To use the common jargon, that program architecture was not very

scalable.)

Encapsulate complexity in a class

Basically, this program solves that problem by encapsulating many of the properties and methods

that are useful for manipulating sprites into a class from which sprite objects can be instantiated.

Most of the complexity is encapsulated in the class and thereby removed from the program that

uses objects of the class.

The scenario

This program shows a baseball coach ( Image 1 ) being attacked by a swarm of vicious ladybug sprites. (Don't worry, we will find a way to save the coach in the next module.)

This program uses the class named Sprite01 to populate the game window with 1000 ladybug

sprites in different colors with different sizes that fly around the game window in different

directions with different speeds as shown in Image 1 .

11.5. Discussion and sample code

The class named Sprite01

The class named Slick0200

The init method

The update method

The render method

The class named Sprite01

A complete listing of this class is provided in Listing 11 . I will not explain the entire class in detail in this module. Instead, I will provide an overview of the class and then explain various

parts of the class as I use them in this and the next two modules.

Beginning of the class named Sprite01

The beginning of the class named Sprite01 down through the constructor is shown in Listing 1 .

public class Sprite01{

Image image = null;//The sprite wears this image

float X = 0f;//X-Position of the sprite

float Y = 0f;//Y-Position of the sprite

float width = 0f;//Width of the sprite

float height = 0f;//Height of the sprite

float xStep = 1f;//Incremental step size in pixels - X

float yStep = 1f;//Incremental step size in pixels - Y

float scale = 1f;//Scale factor for draw method

Color colorFilter = null;//Color filter for draw method

float xDirection = 1.0f;//Move to right for positive

float yDirection = 1.0f;//Move down for positive

int life = 1;//Used to control life or death of sprite

boolean exposed = false;//Used in the contagion program

//Constructor

public Sprite01(Image image,//Sprite wears this image

float X,//Initial position

float Y,//Initial position

float xDirection,//Initial direction

float yDirection,//Initial direction

float xStep,//Initial step size

float yStep,//Initial step size

float scale,//Scale factor for drawing

Color colorFilter)

throws SlickException {

//Save incoming parameter values

this.image = image;

this.X = X;

this.Y = Y;

this.xDirection = xDirection;

this.yDirection = yDirection;

this.xStep = xStep;

this.yStep = yStep;

this.scale = scale;

this.colorFilter = colorFilter;

//Compute and save width and height of image

width = image.getWidth();

height = image.getHeight();

}//end constructor

Figure 11.3. Listing 1. Beginning of the class named Sprite01.

Listing 1. Beginning of the class named Sprite01.

Straightforward code

The code in Listing 1 is straightforward. It simply declares a number of instance variables, most of which become properties of the object. Listing 1 also defines a constructor that receives and saves values for many of those properties.

The remaining code in Sprite01

If you examine the remaining code in Listing 11 , you will see that it consists of simple property accessor methods along with some methods that control the behavior of an object of the class. I

will explain those behavioral methods when I use them later in this and the next two modules.

The class named Slick0200

Will discuss in fragments

A complete listing of the program named Slick0200 is provided in Listing 10 . I will break the program down and explain it in fragments.

Beginning of the class named Slick0200

The class named Slick0200, down through the main method is shown in Listing 2 .

public class Slick0200 extends BasicGame{

//Store references to Sprite01 objects here.

Sprite01[] sprites = new Sprite01[1000];

//Populate this with a ladybug image later.

Image image = null;

//Populate these variables with the background

// image along with the width and height of the

// image later.

Image background = null;

float backgroundWidth;

float backgroundHeight;

//This object produces random float values for a

// variety of purposes.

Random random = new Random();

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

// rate we will allow.

int targetFPS = 60;

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

public Slick0200(){//constructor

//Set the title

super("Slick0200, baldwin");

}//end constructor

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

public static void main(String[] args)

throws SlickException{

AppGameContainer app = new AppGameContainer(

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

app.start();

}//end main

Figure 11.4. Listing 2. Beginning of the class named Slick0200.

Listing 2. Beginning of the class named Slick0200.

Everything in Listing 2 is completely straightforward and should not require an explanation beyond the embedded comments.

The init method

The init method begins in Listing 3 .

public void init(GameContainer gc)

throws SlickException {

//Create and save the background image object. Also

// compute and save the width and height of the image.

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

backgroundWidth = background.getWidth();

backgroundHeight = background.getHeight();

//Create and save an Image object of a ladybug. The

// sprites will wear this image

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

Figure 11.5. Listing 3. Beginning of the init method.

Listing 3. Beginning of the init method.

There is also nothing new in Listing 3 . Therefore, the embedded comments should suffice to explain the code.

Populate the array

Listing 4 uses a for loop to populate the array object referred to by the variable named sprites that was declared in Listing 2 . The array object is populated with references to objects of the class Sprite01 .

//Populate the array with references to objects of

// the Sprite01 class.

for(int cnt = 0;cnt < sprites.length;cnt++){

sprites[cnt] = new Sprite01(

image,//ladybug image

backgroundWidth/2.0f,//initial position

backgroundHeight/2.0f,//initial position

(random.nextFloat() > 0.5) ? 1f : -1f,//direction

(random.nextFloat() > 0.5) ? 1f : -1f,//direction

0.1f+random.nextFloat()*2.0f,//step size

0.1f+random.nextFloat()*2.0f,//step size

random.nextFloat()*0.15f,//scale

new Color(random.nextFloat(),//color filter

random.nextFloat(),

random.nextFloat()));

}//end for loop

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

}//end init

Figure 11.6. Listing 4. Populate the array.

Listing 4. Populate the array.

Random values