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
General background information
11.2. Preface
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 .
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
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
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