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.

-end-

Solutions

Chapter 3. Slick0120: Starting your program*

It is licensed under the Creative Commons Attribution License:

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

2013/02/04 08:23:58 -0600

Summary

Learn how and why you should extend the BasicGame class instead of implementing the Game

interface directly. Learn about the behavior of the constructors for the AppGameContainer class.

Learn about the behavior of the setup and getDelta methods that are called by the start method of

the AppGameContainer class.

3.1. Table of Contents

Preface

Viewing tip

Listings

Preview

General background information

Discussion and sample code

Two primary objects

Behavior of an object of the AppGameContainer class

Behavior of an object that implements the Game interface

Starting the game

The constructors for the AppGameContainer class

The setup method of the AppGameContainer class

The getDelta method of the GameContainer class

The gameLoop method of the AppGameContainer class

Run the program

Summary

What's next?

Miscellaneous

Complete program listing

3.2. Preface

Viewing tip

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.

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 listings while you are reading about them.

Listings

Listing 1 . The main method.

Listing 2 . The start method of the AppGameContainer class.

Listing 3 . Constructor for the AppGameContainer class that takes a single parameter.

Listing 4 . Constructor for the AppGameContainer class that takes four parameters.

Listing 5 . The getDelta method of the GameContainer class.

Listing 6 . Source code for Slick0120a.java

3.3. Preview

The main purpose of this module is to analyze the behavior of the Slick2D game engine when you

start a Slick2D game running.

What you have learned

In previous modules, you learned how to download Slick2D and how to install Slick2D in such a

way that you can easily compile and execute Slick2D programs from the command line with no

need for a high level IDE such as Eclipse or NetBeans.

You also learned what we often mean when we speak of a "game engine" and how that

terminology relates to a "software framework."

You learned how to write a minimal Java application in conjunction with a set of Slick2D jar files

to create your own Slick2D game engine. Using that program as an example, you learned about the

overall structure of the Slick2D game engine.

You learned that game engines are typically service provider programs and you learned about a

common set of services that is provided by most game engines.

You learned about the two cooperating objects that form the heart of the Slick2D game engine.

An object instantiated from a subclass of the Slick2D class named GameContainer .

An object instantiated from a class that implements the Slick2D interface named Game .

And last but not least, you learned about the five abstract methods declared in the interface named

Game :

boolean closeRequested()

String getTitle()

void init(GameContainer container)

void render(GameContainer container, Graphics g)

void update(GameContainer container, int delta)

What you will learn

You will learn how and why you should extend the BasicGame class instead of implementing the

Game interface directly.

You will learn about the constructors for the AppGameContainer class.

You learned earlier that you need to call the start method on an object of the

AppGameContainer class to cause your Slick2D game program to start running. You will learn

that the start method calls the following three methods:

setup

getDelta

gameLoop

You will learn about the behavior of the setup and getDelta methods in this module. An

explanation of the gameLoop method will be deferred until the next module.

3.4. General background information

Listing 6 shows the skeleton code for a basic game class named Slick0120a . This code differs from the skeleton code presented in earlier modules in two important respects:

1. The class named Slick0120a extends the Slick2D class named BasicGame instead of

extending Object and implementing the Slick2D interface named Game .

2. The class named Slick0120a does not override the methods named getTitle and

closeRequested . (They are overridden with default behavior in the BasicGame class.)

Instead, it overrides only the following methods that are declared in the Slick2D Game

interface:

1. init

2. update

3. render

The class named BasicGame

Regarding the first item in the above list, while it is technically possible to write a Slick2D game program by implementing the Game interface directly, the Slick2D helper class named

BasicGame implements the Game interface and provides a number of useful methods as well.

Therefore, by extending the BasicGame class, you not only implement the Game interface, you

also get the benefit of the methods that are defined in the BasicGame class.

The methods named init, update, and render

Note, however that the Basic game class does not define concrete versions of the methods named

init , update , and render . Therefore, you are still required to provide concrete versions of those

methods in your class that extends the BasicGame class (or some subclass of that class) .

The class named Slick0120a does provide concrete versions of methods as indicated in the second

item in the above list.

The methods named getTitle and closeRequested

Further regarding the second item in the above list, the class named BasicGame does provide concrete versions of the methods named getTitle and closeRequested . Therefore, unless you need

to provide different behavior for those two methods, you don't need to override them in your new

class that extends the BasicGame class.

3.5. Discussion and sample code

Two primary objects

Behavior of an object of the AppGameContainer class

Behavior of an object that implements the Game interface

Starting the game

The constructors for the AppGameContainer class

The setup method of the AppGameContainer class

The getDelta method of the GameContainer class

The gameLoop method of the AppGameContainer class

Listing 1 shows the main method for our new class named Slick0120a .

public static void main(String[] args)

throws SlickException{

AppGameContainer app =

new AppGameContainer(new Slick0120a());

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

}//end main

Figure 3.1. Listing 1 . The main method.

Listing 1 . The main method.

We will dissect this code to make certain that we understand what it means and why we need it.

Two primary objects

You learned in an earlier module that a Slick2D game that runs as an application (not an applet)

consists of at least two cooperating objects:

1. An object instantiated from a subclass of the Slick2D class named GameContainer .

2. An object instantiated from a class that implements the Slick2D interface named Game .

Behavior of an object of the AppGameContainer class

The GameContainer object ( item 1 above ) manages the program startup and the game play after the game program starts running. For example, this is the object that manages the game loop.

As shown in Listing 1 , for the program named Slick0120a , this object is an object of the class named AppGameContainer , which extends the class named GameContainer .

The AppGameContainer class provides many public methods (including the method named start

, which is called in Listing 1 ) by which you can manipulate the behavior of the container object.

The authors of the Slick2D library did not intend for you to physically modify the source code in

the GameContainer class or the AppGameContainer class.

Behavior of an object that implements the Game interface

The behaviors of the methods of the Game object ( item 2 above ) are what distinguishes one Slick2D game from another Slick2D game.

You need not implement the Game interface directly. The authors of the Slick2D library provided

a helper class named BasicGame that implements the Game interface and provides a number of

useful methods. They intended for you to extend the BasicGame class and to override at least

three of the methods declared in the Game interface in order to provide the desired behavior for

your game..

As mentioned earlier, the class named Slick0120a extends the BasicGame class, thereby

implementing the Game interface and getting the benefit of methods defined in the BasicGame

class.

The code in the main method in Listing 1 instantiates an object of the Slick0120a class and passes that object's reference to the constructor for the class named AppGameContainer .

Therefore, Listing 1 instantiates both of the required objects and connects them in the manner intended by the authors of the Slick2D library.

Starting the game

The main purpose of this module is to analyze the behavior of the Slick2D game engine when you

start a Slick2D game running.

Listing 1 calls the start method on a reference to the AppGameContainer object. The source code for the start method is shown in Listing 2 .

public void start() throws SlickException {

try {

setup();

getDelta();

while (running()) {

gameLoop();

}

} finally {

destroy();

}

if (forceExit) {

System.exit(0);

}

}//end start

Figure 3.2. Listing 2. The start method of the AppGameContainer class.

Listing 2. The start method of the AppGameContainer class.

Copyright and license information

Copyright and license information:

I was unable to find any copyright or license information in the zip file that I downloaded

from http://slick.cokeandcode.com/index.php . I acknowledge that I am not the author of the code in that zip file and the copyrights for that material are held by someone other than

myself.

Constructors and methods

This and the next few modules will explore and discuss the constructors for the

AppGameContainer class (see Listing 1 ) along with salient aspects of the following methods that are called in Listing 2 :

setup

getDelta

gameLoop

The constructors for the AppGameContainer class

Listing 1 instantiates a new object of the AppGameContainer class by calling a constructor that takes a single parameter of the Slick2D interface type Game .

The source code for that constructor is shown in Listing 3 .

public AppGameContainer(Game game) throws SlickException {

this(game,640,480,false);

}//end constructor

Figure 3.3. Listing 3. Constructor for the AppGameContainer class that takes a single parameter.

Listing 3. Constructor for the AppGameContainer class that takes a single parameter.

A constructor with four parameters

The code in Listing 3 simply calls another overloaded version of the constructor passing four default parameters that specify a game window of 640x480 pixels.

The constructor that takes four parameters is shown in Listing 4 .

public AppGameContainer(Game game,

int width,

int height,

boolean fullscreen)

throws SlickException {

super(game);

originalDisplayMode = Display.getDisplayMode();

setDisplayMode(width,height,fullscreen);

}//end constructor

Figure 3.4. Listing 4. Constructor for the AppGameContainer class that takes four parameters.

Listing 4. Constructor for the AppGameContainer class that takes four parameters.

The first parameter is a reference to the game that is to be wrapped by the GameContainer

object. The code in Listing 4 passes that reference to its superclass, GameContainer , where it is saved in a protected variable of type Game named game . As a protected variable, it is accessible to all of the methods of the AppGameContainer class for use later.

Then Listing 4 saves the current display mode in a variable named originalDisplayMode , presumably to be used later.

Finally, Listing 4 calls the method named setDisplayMode to set the display mode to match the incoming parameters.

(This is the constructor that you would use if you wanted to cause the size of the game window to

be something other than the default of 640 by 480 pixels.)

The setup method of the AppGameContainer class

The setup method is fairly long and complicated. Most of the code in the method has to do with

the creation and formatting of the game window. I will skip over that code and leave it as an

exercise for interested students to analyze.

Initialization of the game

Finally a statement near the end of the setup method calls a method named init on a reference to

the Game object, passing a reference to the object of type AppGameContainer as a parameter.

This is what we would refer to as a callback that uses the reference to the Game object that was

passed to the constructor to call the method named init on the Game object.

The effect is to call the method named init belonging to the game program shown in Listing 6 .

This causes the initialization code (if any) that you have written into the overridden init method to

be executed. If the overridden version of the method has an empty body (as in Listing 6 ) , it simply returns without doing anything. This is how your game gets initialized.

The getDelta method of the GameContainer class

The AppGameContainer class inherits a protected method named getDelta from its superclass

named GameContainer .

The getDelta method is called from the start method shown in Listing 2 .

What is delta?

An int parameter named delta is received by the update method shown in Listing 6 . (The update

method s a concrete version of the method having the same signature that is declared in the Game

interface.)

According to the documentation for the update method in the Game interface, delta is

"The amount of time that has passed since last update in milliseconds"

Having that time available can be valuable in some game programs. For example, you might like

for one of the actors to light a fuse on a bomb and have that bomb detonate some given number of

milliseconds later. In that case, the program would need real time information to know when to

detonate the bomb.

Listing 5 shows the source code for the getDelta method.

protected int getDelta() {

long time = getTime();

int delta = (int) (time - lastFrame);

lastFrame = time;

return delta;

}//end getDelta method

Figure 3.5. Listing 5. The getDelta method of the GameContainer class.

Listing 5. The getDelta method of the GameContainer class.

Without getting into the details, the method named getTime that is called in Listing 5 returns the amount of time, (with a resolution of one millisecond) , that has elapsed since a historical point in

time before the game started running.

The GameContainer class contains a protected instance variable of type long named lastFrame

that is used to store a time value.

The code in Listing 5

subtracts the time value stored in lastFrame from the current time,

converts the time difference to type int , saving it in delta , and

stores the current time in lastFrame .

stores the current time in lastFrame .

The difference between the two time values represents a time interval and that difference is

returned as type int .

Various methods in the AppGameContainer and GameContainer classes call the getDelta

method in such a way that the value of delta represents the time required to update and render one

frame when the program is running. (There are some other options as well that I may discuss in a

future module.)

When the method named update is called in Listing 6 , the incoming parameter named delta contains the number of milliseconds that have elapsed since the last time that the update method

was called.

When the method named getDelta is called in Listing 2 , the return value is discarded. This suggests that the call to the getDelta method in Listing 2 is made simply to cause the variable named lastFrame to be initialized with time that the start method was called.

The gameLoop method of the AppGameContainer class

That leaves us with one more method call from Listing 2 that we need to examine -- gameLoop . I anticipate that will be a fairly long discussion, so I am going to defer that discussion until the next

module.

3.6. Run the program

As explained earlier, the skeleton code in Listing 6 is different from the skeleton code that I presented in earlier modules. Therefore, I encourage you to copy the code from Listing 6 .

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.

3.7. Summary

The main purpose of this module was to analyze the behavior of the Slick2D game engine when

you call the start method to cause a Slick2D game program to start running.

You learned how and why you should extend the BasicGame class instead of implementing the

Game interface directly.

You learned about the behavior of the constructors for the AppGameContainer class.

You learned that the