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.

Properties that control the spread

Later on, I will explain the properties that control the spread of the disease. Some sets of property

values produce results similar to those shown above where the disease gains a foothold, spreads

for awhile killing many sprites, and then recedes without killing the entire population.

Other sets of property values end up with all of the sprites having died.

Still other sets of property values end up with the disease being unable to gain a foothold and

spread beyond just a few individual sprites.

What you have learned

You have learned how to use a basic Slick2D game engine to create simulations involving

thousands of sprites, collision detection, and sound.

What you will learn

In this module, you will learn how to use what you have previously learned to write a relatively

complex (but somewhat simplified) simulation of a real-world pandemic.

If you were to study the characteristic of pandemics, you could probably upgrade this program to

produce a better model of a pandemic. For example, an interesting student project would be to

allow healthy sprites to reproduce when they come in contact based on a random probability

function. This would allow the population to be growing at the same time that it is dying off due

to the disease. Of course, it may then be necessary to deal with the effects of a population

explosion.

13.4. General background information

This program simulates the spread of a fatal communicable disease within a population.

A single infected sprite is introduced into a population of sprites. The disease is spread by

physical contact between a healthy sprite and an infected sprite.

You can watch as the disease either spreads and kills the entire population or spreads for awhile,

then recedes and dies out.

Infected sprites are colored red. Healthy sprites are colored green. A sound is emitted (simply to

demonstrate how to emit sounds) each time there is contact between an infected sprite and a

healthy sprite.

The final outcome

The final outcome is determined both by chance and by several factors including:

The life expectancy of an infected sprite.

The probability of infection due to contact with an infected sprite.

The degree of mobility of both infected and healthy sprites.

The population density of sprites.

The actual values for the first three factors for each individual are determined by a maximum

value multiplied by a random number between 0 and 1.0.

Experimentation

Instance variables are provided for all four of these factors. You can modify the values and

recompile the program to experiment with different combinations of the factors.

A good exercise for a student would be to create a GUI that allows the factors to be entered more

easily without having to recompile the program for purposes of experimentation.

13.5. Discussion and sample code

The class named Sprite01

The class named Slick0220

The init method

The update method

The render method

The class named Sprite01

The class named Sprite01 is shown in Listing 9 . There is nothing new in Listing 9 that I haven't explained in earlier modules.

The class named Slick0220

Will explain in fragments

A complete listing of the class named Slick0220 is provided in Listing 8 . I will break the code down and explain it in fragments.

Beginning of the class named Slick0220.

The beginning of the class named Slick0220 , down through the main method is shown in Listing

1 .

public class Slick0220 extends BasicGame{

//The values of the following variables can be changed

// to effect the spread of the disease.

//Set the life expectancy of an infected sprite

// in frames.

int infectedSpriteLife = 96;

//Set the maximum fraction of exposed sprites that will

// become infected.

float probabilityOfInfection = 0.5f;

//Set the maximum step size that a sprite will move in

// one frame.

float maxStepSize = 1;

//Set the initial number of sprites in the population.

int numberSprites = 1000;

//References to Sprite01 objects are stored here.

ArrayList <Sprite01> sprites =

new ArrayList<Sprite01>();

//These variables are populated with references to Image

// objects later.

Image redBallImage;

Image greenBallImage;

//This variable is populated with a reference to a Sound

// object later.

Sound blaster;

//These variables are populated with information about

// the background image later.

Image background = null;

float backgroundWidth;

float backgroundHeight;

//This object is used to produce random values for a

// variety of purposes.

Random random = new Random();

//This is the frame rate we would like to see and

// the maximum frame rate we will allow.

int targetFPS = 24;

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

public Slick0220(){//constructor

//Set the title

super("Slick0220, baldwin");

}//end constructor

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

public static void main(String[] args)

throws SlickException{

AppGameContainer app = new AppGameContainer(

new Slick0220(),500,500,false);

app.start();

}//end main

Figure 13.4. Listing 1. Beginning of the class named Slick0220.

Listing 1. Beginning of the class named Slick0220.

There is nothing new in Listing 1 , so there should be no need for an explanation beyond the embedded comments.

The init method

The init method begins in Listing 2 .

public void init(GameContainer gc)

throws SlickException {

//Create Image objects that will be used to visually

// represent the sprites.

redBallImage = new Image("redball.png");

greenBallImage = new Image("greenball.png");

//Create a Sound object.

blaster = new Sound("blaster.wav");

//Create a background image and save information

// about it.

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

backgroundWidth = background.getWidth();

backgroundHeight = background.getHeight();

//Add a red sprite as the first element in the

// ArrayList object. This sprite carries the disease

// into the population.

//Put it in the center of the game window. Make the

// direction of motion random. Make the speed of

// motion (step size)random. Make the size random.

// Specify a white (do nothing)color filter.

sprites.add(new Sprite01(

redBallImage,//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

0.5f+random.nextFloat()*0.5f,//scale

new Color(1.0f,1.0f,1.0f)));//color filter

//This is an infected object. Set its life

// expectancy.

sprites.get(0).setLife(

(int)(random.nextFloat()*infectedSpriteLife));

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

Listing 2. Beginning of the init method.

Sick but not yet dead

The only new code in Listing 2 is the call to the setLife method at the end. In the previous

module , the life property of a sprite was always either 0 or 1. A sprite with a life property value of 0 was dead. A sprite with a life property value of 1 was alive.

This program is more nuanced and uses values other than 0 and 1 for the infected red sprites. A

value of 0 still means that a sprite is dead. Any other positive value means that the sprite is sick

and dying but not yet dead.

The value assigned to the life property for this sprite is a random value between 0 and

infectedSpriteLife . This is one of the property values that has an impact on the extent to which

the disease spreads through the population. The longer an infected sprite lives after becoming

infected, the more healthy sprites it will infect and the more aggressive will be the disease.

You can modify this value (see Listing 1 ) and recompile the program to experiment with different values.

Remainder of the init method

The remainder of the init method is shown in Listing 3 .

//Populate the ArrayList object with green sprites.

// Make the initial position random. Make the initial

// direction of motion random. Make the speed

// (step size) random. Make the size (scale) random.

// Make the color filter white (do nothing).

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

sprites.add(new Sprite01(

greenBallImage,//image

backgroundWidth*random.nextFloat(),//position

backgroundHeight*random.nextFloat(),//position

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

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

random.nextFloat()*maxStepSize,//step size

random.nextFloat()*maxStepSize,//step size

1.0f,//scale

new Color(1.0f,1.0f,1.0f)));//color filter

}//end for loop

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

}//end init

Figure 13.6. Listing 3. Remainder of the init method.

Listing 3. Remainder of the init method.

A population of healthy sprites

Listing 3 uses a for loop to add numberSprites (see Listing 1 ) healthy sprites to the population.

This is another property that has an impact on the spread of the disease. Everything else being

equal, the more sparse the population, the more difficult it is for the disease to get a foothold in

the first place and the more difficult it is for the disease to spread if it does get a foothold.

The frame rate

Listing 3 also sets the frame rate to the value of targetFPS (see Listing 1 ) . Note that I slowed this program down to the standard movie frame rate of 24 fps (as opposed to the typical 60 fps)

mainly because I wanted to run the simulation more slowly. In other words, I wanted it to be

possible to see the disease spread through the population. Also, it is a fairly demanding program

so it may not run at 60 fps on some machines.

End of the init method

Listing 3 also signals the end of the init method.

The update method

The update method begins in Listing 4 . This is the method where most of the added complexity in this program resides.

public void update(GameContainer gc, int delta)

throws SlickException{

//Move all the sprites to their new positions.

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

//Get a reference to the current Sprite01 object.

Sprite01 thisSprite = sprites.get(cnt);

//Ask the sprite to move according to its

// properties

thisSprite.move();

//Ask the sprite to bounce off the edge if necessary

thisSprite.edgeBounce(

backgroundWidth,backgroundHeight);

}//end for loop

Figure 13.7. Listing 4. Beginning of the update method.

Listing 4. Beginning of the update method.

Nothing new in this code fragment

The is nothing new in the code fragment shown in Listing 4 . The new code begins in Listing 5 .

An overview of the code

Before getting down into the details of the code, I will give you a descriptive overview.

In the outer-most layer, the program uses a for loop to examine every sprite in the population

looking for red or infected sprites.

When it finds an infected sprite, it decreases the value of its life expectancy. Then it uses an inner

for loop to test that sprite against every sprite in the population looking for collisions.

Ignore collision with an infected red sprite

If the infected sprite collides with another infected sprite, it ignores the collision and keeps

searching the population, looking for collisions with healthy sprites.

Collision with a healthy green sprite

If the infected sprite collides with a healthy (green) sprite, it causes that sprite to become exposed

to the disease and plays a sound effect. (As you will see later, sprites that are exposed to the

disease don't always contract the disease.)

The state of the population

When the infected sprite has been tested for a collision with every healthy sprite, four kinds of

sprites exist in the population:

1. Healthy sprites that have not been exposed to the disease.

2. Healthy sprites that have been exposed to the disease.

3. Infected sprites that still have some remaining life.

4. Infected sprites whose life property is less than or equal to zero, meaning that they are dead.

A cleanup pass

An Iterator is used to make a cleanup pass through the population.

Exposed sprites are either converted to infected sprites or cleared of the exposure on the basis of a

random value that has a maximum value of probabilityOfInfection (see Listing 1 ) .

Dead sprites are removed from the population.

The code to accomplish all of this begins with the for loop in Listing 5 .

//Search for and process collisions between

// infected (red) sprites and healthy (green)

// sprites. Declare the green sprite to be exposed to

// the disease when a collision occurs.

for(int ctr = 0;ctr < sprites.size();ctr++){

//Get a reference to the Sprite01 object.

Sprite01 testSprite = sprites.get(ctr);

if(testSprite.getImage().equals(redBallImage)){

//This is an infected sprite. Reduce its life.

testSprite.setLife(testSprite.getLife() - 1);

// Do the following for every sprite in the

// ArrayList object.

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

//Get a reference to the Sprite01 object.

Sprite01 thisSprite = sprites.get(cnt);

//Test for a collision between this sprite and

// the infected test sprite.

boolean collision =

thisSprite.isCollision(testSprite);

//Process a collision if it has occurred.

// Exclude collisions between the testSprite

// and itself and with other infected sprites.

if((collision == true)&&(!thisSprite.getImage().

equals(redBallImage))){

//A collision has occurred, set exposed to true

thisSprite.setExposed(true);

//Play a sound to indicate that a collision

// has occurred.

blaster.play();

}//end if

}//end for loop

}//end if on redBallImage

Figure 13.8. Listing 5. Process collisions.

Listing 5. Process collisions.

You should have no difficulty matching up the code in Listing 5 with the verbal description given above.

Make a cleanup pass

The code in Listing 6 uses an Iterator to make the cleanup pass described above.

//Make a cleanup pass through the ArrayList object

Iterator <Sprite01> iterB = sprites.iterator();

while(iterB.hasNext()){

Sprite01 theSprite = iterB.next();

//Cause a percentage of the exposed objects to

// contract the disease. Clear the others.

if((theSprite.getExposed() == true) &&

(random.nextFloat() < probabilityOfInfection)){

theSprite.setImage(redBallImage);

theSprite.setLife((int)(

random.nextFloat()*infectedSpriteLife));

theSprite.setExposed(false);

}else{

//Eliminate the effects of the exposure

theSprite.setExposed(false);

}//end else

//Remove dead sprites

if(theSprite.getLife() <= 0){

iterB.remove();

}//end if

}//end while loop

}//end outer for loop

}//end update

Figure 13.9. Listing 6. Make a cleanup pass.

Listing 6. Make a cleanup pass.

Once again, you should have no difficulty matching up the code in Listing 6 with the verbal description given above.

The render method

The render method is shown in Listing 7 . There is nothing new in Listing 7 .

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 every sprite in the ArrayList object.

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

sprites.get(cnt).draw();

}//end for loop

//Display the number of sprites remaining.

g.drawString(

"Sprites remaining: " + (sprites.size()),100f,10f);

}//end render

}//end class Slick0220

Figure 13.10. Listing 7. The render method.

Listing 7. The render method.

13.6. Run the program

I encourage you to copy the code from Listing 8 and Listing 9 . Compile the code and execute it, making changes, and observing the results of your changes. Make certain that you can explain why

your changes behave as they do.

13.7. Summary

In this module, you learned how to write a program that simulates the spread of a fatal

communicable disease within a population.

13.8. Conclusion

Although I may come back and add more modules later, for now, this will be the final module in

this collection.

The objective of the collection was to explain the anatomy of a game engine. I believe I have

accomplished that objective and have also provided sample programs to illustrate the use of the

game engine.

It is worth pointing out that BasicGame is not the only game engine architecture available with

Slick2D. The Slick2D user Manual refers to BasicGame as a game container and indicates that several others are available including:

Applet Game Container

ApplicationGDXContainer/AndroidGDXContainer

The documentation also describes a class named StateBasedGame , which provides a different

anatomy than BasicGame . Bucky Roberts provides a series of video tutorials on state based

games using Slick2D at http://www.youtube.com/watch?v=AXNDBQfCd08

13.9. Miscellaneous

This section contains a variety of miscellaneous information.

Housekeeping material

Module name: Slick0220: Simulating a pandemic

File: Slick0220.htm

Published: 02/07/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.

13.10. Complete program listing

Complete listings of the code discussed in this module are provided in Listing 8 and Listing 9 .

/*Slick0220.java

Copyright 2013, R.G.Baldwin

This program simulates the propagation of a fatal

communicable disease within a population.

A single infected sprite is introduced into a large

population of sprites. The disease is spread by physical

contact with an infected sprite.

You can watch as the disease either spreads and kills the

entire population or spreads for awhile, then recedes and

dies out. Infected sprites are colored red. Healthy

sprites are colored green. A sound is emitted (for drama)

each time there is contact between an infected sprite and

a healthy sprite.

The final outcome is determined both by chance and by

several factors including:

-The maximum life expectancy of an infected sprite

-The maximum probability of infection due to contact with

an infected sprite

-The ma