Java Game Programming Part I: The Basics



Java Game Programming Part I:

The Basics

The purpose of this article is to cover the basics of programming java applets, how to make some simple games, as well as some advanced topics including double buffering. A few of the simple games that I will use are tetris, nibbles, pacman, and pong. I will use these and others as examples to illustrate the thought process and steps that can and should be followed when approaching the development of code for a game.

The Basics

Everyone loves to jump right in and start trying to code complicated, but cool programs. Without a little background knowledge this can be a frustrating experience which is why I gave you the original packet on the bouncing balls. I am going to outline the basics again here before we dive into writing a series of full blown java applets and discussing more advanced topics. Even if you are already familiar with the basics I would encourage you to read on because you never know when you might read something interesting even if it is only a different perspective on something you already know.

Before I proceed, I want to mention how you can go about working through the ideas and concepts that I will be discussing.

Whenever you learn a new programming language the first program you will always write is "Hello World!". This gives you the opportunity to see what is the same and what is different between the programming languages. I don't want to break with tradition so here is a Hello World applet.

import java.applet.*;

import java.awt.*;

public class HelloWorld extends Applet

{

public void paint (Graphics g)

{

g.drawString("Hello World!", 50, 25);

}

}

One important point to take note of here is that you need to call this file "HelloWorld.java". You will notice that this is identical to the name I gave my class on the fourth line of the program above. If these two names differ at all you will get an error during compilation.

Packages and the import statement

Java is totally based on classes. It allows related classes to be grouped together into something called a package. Two examples of packages are java.applet and java.awt. The import statement allows you to include in your program one or more classes from a package.

import java.applet.*; // includes all of the classes from the java.applet package

As we progress I will mention what we need to import to allow our programs to function. For now importing all of the classes from the java.applet and java.awt packages will do.

Classes

As I mentioned before, everything in Java is centered around the use of classes. You will notice that the next line in our sample program is a class declaration.

public class HelloWorld extends Applet

This is a class declaration which represents our applet. Two important points need to be noted here. First of all, the name of your class must be identical to the name of the file in which it is located. In this case, our class is called HelloWorld so the filename must be HelloWorld.java. I know that I just mentioned this a moment ago, but you would be suprised with the number of times simple errors like this crop up in people's programs. The other important point to take note of is the end of the class declaration, "extends Applet", what it means is that our class (HelloWorld) receives and can expand on variables and methods found in the Applet class. The end result of this is that we will get a program which can function as an applet.

Methods

Methods in Java reside inside classes. Since this is only our first program and it is very basic we only have one method.

public void paint (Graphics g)

The "public" keyword allows the method to be called from within other classes. If the keyword "private" is used instead then the method cannot be called from within other classes. There exists a third possibility here, "protected", but we won't look at that for the time being. The next keyword after public is used to tell us what sort of information will be returned by the method. In this particular case we have used "void" which means that nothing will be returned. I could have used int, char, etc. instead, but given the nature of this method they were unnecessary.

We will look at methods which require a return type as we advance into more complicated examples.

The paint method will be an important fixture in all of our programs since this is where we display on the screen everything we want the user to be able to see. The paint method is always passed a graphics context, "Graphics g", which is later used by the method to enable painting to the screen.

Displaying Information on the Screen

The last statement which we need to take a look at is g.drawString(....).

g.drawString("Hello World!",50,25);

Guess what this does? That's right! This statement draws a string to the screen (represented by the graphics context) at the x (50) and y (25) coordinates. The coordinate (0,0) would give you the top left of the applet not the top left of the web page the applet is on. One other important point to note here is that we don't have to draw strings to the screen. This will be an important fact when we get into double buffering later on, but for now lets move on.

The first step toward running our applet is to compile it. You should end up seeing a file created in called HelloWorld.class. Java is an interpreted language which means that it doesn't create standard executable files. Instead we get files with a .class ending.

Special Note

The next step before we run our program in an open environment is to create a HTML file which will display our applet. If you haven't used HTML before don't worry about it because you will be able to use the sample page I give you here over and over with one or two minor modifications. I would recommend that you look into learning it though because it is very useful. So without any further delay here is the code we want in our HTML file which I have called Hello.html.

Hello World Applet

Hello World Applet

Once you have saved this HTML file you can then open it up in the browser of your choice.

The only part of the above HTML that you really need to be concerned with is the .. lines. You will notice that for the applet I have entered three pieces of information: width, height, and the name of the .class file. The width and height specify the dimensions of the applet on the web page and the code attribute specifies the name of the applet we want to display. For the code attribute you want to make sure that you have the full name of the compiled java applet with the proper capitialization.

There you have it. That is all there is to writing a basic java applet. Now that we know the basics lets pick up the pace and introduce some more interesting capabilities of java applets.

Altering the Appearance of Text

Java provides the ability to change the font of the text which you are displaying. While it only supports a small number of fonts it is still a useful capability which you may want to take advantage of in your games.

private Font a_Font;

public void init()

{

a_Font = new Font("Helvetica", Font.BOLD, 48);

setFont(a_Font);

}

The addition of the above lines to our applet will allow us to change the font in which our text appears. These lines should be placed after the opening curly brace of the class and before the paint method. You will also notice the use of a new method here. The init method is called automatically when the applet is first loaded. If you have some initialization code and code that only needs to happen when the applet is first loaded then this is the place to put it. You must make sure that you spell init the same way that I have done. If you spell it incorrectly or use improper capitalization then it will appear to Java as a different method and will not be called automatically. This problem will not be flagged for you by the compiler so you must be extremely careful when you write your methods to get spelling and capitalization perfect. Trying to track this problem down can be annoying.

The first parameter in the above font creation code is the name of the font that we want to use. These could be "TimesRoman", "Helvetica", "Courier", "Dialog", and "DialogInput" which work, but are being replaced by "Serif", "SansSerif", and "Monospaced". The second parameter specifies the style of our new font. This can be Font.BOLD, Font.PLAIN, Font.ITALIC, or Font.BOLD + Font.ITALIC. The last parameter specifies the size of the new font.

In order to put our new font to work we must use the setFont method with our new font passed as a parameter. This will change the font for the entire applet for the entire life span of the applet to our font. If you want to bounce back and forth between fonts you will set the font in the paint method instead using the setFont method prefixed by the graphics context (g.setFont(...);).

Another way you can alter the appearance of text is by changing the color of it.

g.setColor (Color.red);

This line of code is normally found in the paint method because you need a graphics context in order to use it. The code itself is not bad and some of the colors that are available to you are listed below.

Color.black Color.blue Color.cyan Color.darkGray

Color.gray Color.green Color.lightGray Color.magenta

Color.orange Color.pink Color.red Color.white

Color.yellow

The colors listed previously will probably suit your needs, but you may be interested in creating your own colors on occasion. The code to do this is very simple and you will probably notice similarities between it and the code used to create a new font.

Color adamBlue = new Color (80,124,224);

g.setColor(slateBlue);

The three numbers in the constructor of our new color are the RGB values. Each of the numbers must be between 0 and 255. If you are unsure about what a constructor is and why I use the keyword new don't worry for now. Take everything I say as unquestionable truth and we will talk about it later when we discuss classes and how they fit into our games. One thing that I didn't mention previously is that when you call the setColor method it also changes the color of everything you do after that point including more text and shapes which we will talk about in a minute.

Two other useful functions that are available for you use are setBackground(..) and setForeground(..).

setBackground(Color.yellow);

setForeground(Color.blue);

These two lines, if used, are generally found in the init method. The setForeground method has the same effect as the g.setColor statement except that the effect is permanent for everything in your applet.

I recommend that you use g.setColor because as you make your games you are going to want to frequently change colors. The setBackground method is good for now, but in a little bit I will introduce the idea of double buffering and with it a new way to set the background color.

Shapes

Shapes are a very important topic because they will play a crucial role in quite a few of the games that you will write. There is no built in 3D segment in Java, Java provides several built-in methods to allow for quick and easy drawing of shapes. Each of these will usually be called from your paint method.

drawRect (int x, int y, int width, int height)

eg g.drawRect (5,8,49,29);

drawOval (int x, int y, int width, int height)

drawLine (int x1, int x2, int y1, int y2)

drawRoundRect (int x, int y, int width, int height, int arcWidth, int arcHeight)

drawArc (int x, int y , int width, int height, int startAngle, int arcAngle)

draw3DRect (int x, int y, int width, int height, boolean raised) - Draws a 3-D highlighted outline of the specified rectangle. The edges of the rectangle are highlighted so that they appear to be beveled and lit from the upper left corner. The boolean parameter determines whether the rectangle appears to be raised above the surface or sunk into the surface. It is raised when the parameter is true. 

For all of the above shapes, except for the line, there is a filled alternative.

fillRect (....);

fillOval (....);

If you want to have different colors for each of your shapes then you must make sure that you call g.setColor before you draw the shape. I recommend going through and playing around with the above shapes a bit because they will be important in many games that you write and will be important in some of the examples that I show you.

Conditionals, Control Statements, and Operators

There will be plenty of examples of them later when I get into more coding related information about specific games.

Images

Image coolImage;

....

coolImage = getImage(getCodeBase(), "dx3d.GIF");

....

g.drawImage(coolImage,0,0,this);

Everyone likes images and it is relatively simple to display them in a java applet. The top line of sample code is our variable declaration which can go at the top of our applet class with our font variable and any others you may have inserted at that location. The second line of code will go in our init method and the last line will go in our paint method.

The syntax of the Java language is very intuitive with many of the method names it uses. For example, by just looking at the code above you can probably get a good idea of what is going on. As I mentioned before, the first line is our variable declaration. The next line involves the loading of our image from it's file. In this case the name of the file is dx3d.GIF. When you are loading images ensure that you have the right capitalization and spelling of the filename. As you start to write bigger applets you are going to be confronted with more and more errors so if you can eliminate some by being cautious in areas like this then your job will be easier. The last line of importance here is responsible for the drawing of the image. You will notice that we are using our graphic context and rather than drawing a string or filling an oval we are drawing an image. The first three parameters are what interest us here. The first parameter is our variable which holds the image that we want to display. Next we have the x and y coordinates of where we want the image to be located on the screen. You will notice that we don't have to give the size of the image at any point. When we load the image in we take on the width and height of the image as it was in the file. It is a good idea to get familiar with images now because we will come back to images in a bit and add some new wrinkles when we talk about double buffering.

Random Numbers

Random numbers are used quite frequently in game programming as well as regular programming. To get random numbers in Java is a simple task as you can see in the code excerpt below.

public void paint(Graphics g)

{

int x;

int y;

x = (int)(Math.random()*100);

y = (int)(Math.random()*100);

g.setColor(Color.red);

g.drawString("Hello World",x,y);

}

Let's look at the code for the random numbers before we see how they are used in this particular bit of code. You will notice that we make a call to Math.random(). This returns a number between 0 and 1. We then multiply by the range of numbers that we want to have. In the example above I multiply by 100 to give myself a number between 0 and 100. If I wanted a number between 1 and 100 I would multiply by 99 and then add 1. The reason that works is because the multiplication by 99 gives me a number between 0 and 99 and then I add one to put it in the proper range. The last thing you will notice is the int keyword out in front of the random number code. This is called casting. All that is happening is that I am telling the compiler that I am going to stick a float into an int and I am aware of the fact that I will lose some information by using this code.

The purpose of this applet is really simple. Every time it loads it will randomly place the string "Hello World" on the applet somewhere between 0 and 100 for the x and y coordinates. This may seem fairly bland at the moment, but take note of what we are doing because when we discuss threads we will be able to have text randomly appearing and disappearing on the screen and moving around with only slight modifications to the code above.

Adding sounds to Java applets

 

Introduction

We all know that multimedia, used properly, can make any web site more entertaining.  In this article, we'll present a brief example of a Java applet that plays a sound file when it is downloaded.  The compiled applet class file is very small - only 559 bytes - and can be downloaded quickly into a user's web browser.

 

The SoundApplet applet

Fortunately, using sounds in Java applets is very simple.  The SoundApplet.java code example shown below demonstrates the minimal requirements needed to play a sound file when an applet is downloaded.  Notice that to make the applet easy to hide on a web page, we resized it's visible dimensions to zero pixels wide, zero pixels tall.

 

| import java.applet.AudioClip.*; |

| public class SoundApplet extends Applet { |

|    public void init() { |

|       super.init(); |

|       resize(0,0); |

|       AudioClip gong = getAudioClip(getDocumentBase(), "gong.au"); |

|       gong.play(); |

|    } |

| } |

|  |

 

|Example a: | SoundApplet.java - a simple applet that plays the "gong.au" sound file. |

| | |

 

Notice from this example that there are really only two steps required to play a sound in a Java applet:

(1) loading the sound file into an AudioClip object, and

(2) playing the sound using the play() method of the AudioClip class.

Also notice that there is no path leading to the gong.au file - this applet expects to find the gong.au file in the same directory as the class file. 

Special Note: If instead the gong.au file was located in a sub-directory named sounds, the file would have been loaded with this statement:

       AudioClip gong = getAudioClip(getDocumentBase(), "sounds/gong.au");

 

More on Sound

Playing Sounds with AudioClip (excerpt from Java in a Nutshell)

The sound capabilities of modern computer hardware are usually much more advanced than the dumb terminals of yesterday, and typical users expect their computers to make sounds that are prettier than the coarse console bell. Java programs can do this by loading and playing a file of audio data with the java.applet.AudioClip interface. As the package name implies, the AudioClip interface was originally intended only for applets. Since Java 1.0, applets have been able to call their getAudioClip( ) instance method to read an audio file over the network. In Java 1.2, however, the static java.applet.Applet.newAudioClip( ) method was added to allow any application to read audio data from any URL (including local file: URLs). This method and the AudioClip interface make it very easy to play arbitrary sounds from your programs, as demonstrated by Example 17-2.

Invoke PlaySound with the URL of a sound file as its sole argument. If you are using a local file, be sure to prefix the filename with the file: protocol. The types of sound files supported depend on the Java implementation. Sun's default implementation supports .wav, .aiff, and .au files for sampled sound, .mid files for MIDI, and even .rmf files for the MIDI-related, proprietary "Rich Music Format" defined by Beatnik. [1]

When you run the PlaySound class of Example 17-2, you may notice that the program never exits. Like AWT applications, programs that use Java's sound capabilities start a background thread and do not automatically exit when the main( ) method returns. [2] To make PlaySound better behaved, we need to explicitly call System.exit( ) when the AudioClip has finished playing. But this highlights one of the shortcomings of the AudioClip interface: it allows you to play( ), stop( ), or loop( ) a sound, but it provides no way to track the progress of the sound or find out when it has finished playing. To achieve that level of control over the playback of sound, we need to use the JavaSound API, which we'll consider in the next section.

Example 17-2. PlaySound.java

package je3.sound;

/**

* Play a sound file from the network using the java.applet.Applet API.

*/

public class PlaySound {

public static void main(String[ ] args)

throws .MalformedURLException

{

java.applet.AudioClip clip =

java.applet.Applet.newAudioClip(new .URL(args[0]));

clip.play( );

}

}

Finding Music Files

Before you can test PlaySound, you'll need some sound files to play. You probably have lots of sampled audio files sitting around on your computer: they are often bundled with operating systems and applications. Look for files with .wav, .au, or .aif extensions. You may not have MIDI files on your computer, but many MIDI hobbyists make files available for download on the Internet; a quick Internet search will locate many samples you can use.

The JavaSound web page is also worth a visit (). This page includes links to an interesting JavaSound demo application that includes source code and its own set of sampled audio and MIDI files. Also of interest here are free downloadable MIDI soundbank files, which may give you richer MIDI sound.

Java Game Programming Part II

Making a Simple Game

So far we looked at how to create a basic applet, display text and shapes, and get random numbers. In this second part I will cover how to use threads and double buffering as well as how to get input from the mouse and keyboard and you will create a simple air hockey game.

Threads

As I mentioned before we need to have a loop capability that will allow us repeat the input, processing, and output phases over and over again. In order to do this in our java applet we are going to use a concept called threads. By creating a thread and starting that thread we gain access to a new method called run. Inside this new method we will create a loop that will enable our game to run properly (ie. allowing for a repeat of input, processing, and output).

Let’s now work through a simple example applet in order to get familiar with the basics of threaded applets. This applet should be in a file called SampleThread.java. The first lines to appear in any of our applets are the imports. In this particular example because we aren’t doing anything fancy all we need is the following import.

import java.applet.*;

This allows us to create our basic applet and use the methods necessary to get an applet up and running. The next line in our code is where we actually create our class that represents our java applet. What name do we give to our class? If you said SampleThread then you are absolutely right. The reason we name our class SampleThread is that we have named our code file SampleThread.java and the name of the class must always match up with that filename.

public class SampleThread extends Applet implements Runnable

{

You will notice that after we stated that the class would extend the Applet class we add on the fact that the class will implement Runnable. By implementing Runnable we allow our applet to be threaded and gain access to the method run which we will talk about in a minute. Next we must declare a couple of variables which will allow our thread demonstration to produce something to show what is happening.

Thread t;

int i;

The first variable is going to be our thread that will be created in our applet. The second variable is simply an integer counter that is used to demonstrate that we now have the capability to loop which was the fourth phase that I discussed before.

Now it is time to write the code for the methods that our class will contain. In the past we have used two main methods: init and paint. We will be using these again, but this time we will also be including a new method, run, which will become a key portion of our applets in the future.

public void init()

{

t = new Thread(this);

t.start();

i = 0;

}

The init method contains a couple of lines that get our thread up and running. The first line is responsible for creating a new thread for the applet. The reason we type this inside the brackets is that when our applet is actually running we have an instance of our SampleThread class. The init method is then run for this instance and the thread is created for this instance. It is possible to have multiple instances of a class so each instance will have a thread setup for that particular instance. If that still isn’t 100% clear don’t worry about it for the time being and hopefully with a little time it will become clearer. After we create our new thread we need to make sure that we start the thread so that our run method is called. I also initialize my counter, i, to zero so that I know the exact value that it begins with. I know that you will find many programmers who just assume that their variables are going to be initialized to zero if they are integers, but this is a dangerous assumption. I have on occasion forgotten to initialize a variable to zero when programming and lucky for me my computer has been nice enough to fix this by doing it for me. One time after programming for a couple of days at home and having no problems with the code or how the program was running I took the program to school. When running the program at school I had no idea why a particular part of the program that ran perfectly at home had a small glitch in it when I ran it at school. After a lot of debugging I finally stumbled across a variable that I was assuming was initialized to zero. It turns out that while my computer at home was nice enough to initialize variables to zero for me the computer at school thought it would be nice to initialize my integer variables to some number other than zero. The moral of this story is don’t assume anything and always make sure that you initialize your variables before you use them.

The next method that we need to include in our program is the run method. The run method is called when we create a thread in our applet. Inside the run the method we want to loop until the applet is done or the user leaves. For now in order to ensure that we stay in the run method until the user leaves our page we will use a while loop with the keyword true used as the condition. By using the keyword true as the condition we create an infinite loop that will go until either we explicitly break out of it or the user leaves the page containing the applet. It is important not to be scared of infinite loops because they can be very useful to us if we use them properly.

public void run()

{

while(true)

{

i++;

repaint();

try {

t.sleep(1000/30);

} catch (InterruptedException e) { ; }

}

}

You will notice that inside the while loop I always increment the variable i and call the repaint method. The repaint method calls my paint method and if I didn’t call repaint in the while loop any changes that were made to the game (ie. puck moving) would not show up on the screen. The reason I have the variable i is so that each time paint is called the variable i contains a new value which will be displayed on the screen in place of the old one to show that we are looping and that we now know how to implement the fourth phase of our four important phases.

The last method that we need to implement is the paint method. The paint method is where we take care of the output that we would like to have displayed on the screen. For this applet the paint method is very simple because the only thing that it does is print a string out to the screen which shows the value of i and makes it look marginally pretty by concatenating (the operation of joining two character strings end to end) the string i = to the front of it.

public void paint(Graphics g)

{

g.drawString("i = "+i,10,20);

}

}

If you compile and run this applet you will see that the applet displays the ever increasing value of i on the screen. While this may not seem very exciting it is a fundamental concept that will be critical to getting our air hockey game up and running.

Keyboard

The next topic we need to look at is how to get the users input from the keyboard. It is actually very easy to check out input from the keyboard because java provides us with two new methods: keyDown and keyUp.

One important point that I should mention before we continue is that before an applet will receive keyboard events (ie. call the keyDown or keyUp methods) the user will need to click on the applet area in order to get the focus. The applet only receives keyboard events when it has the focus.

The following is the syntax for the keyDown method. The keyUp method is indentical except of course the method name is changed to keyUp from keyDown.

public boolean keyDown(Event e, int key)

{

message = "value = " + key;

return true;

}

You will notice that the keyDown method takes an integer parameter. This integer value contains the value that represents the key which was pressed. I would recommend that you set up a method similar to the one that I have outlined above and then in your paint method you can print out message (a string) so that you can see the values that are associated with each key.

We now have the capability of accepting input from the keyboard and I think that you will agree with me that it is fairly straight forward.

Mouse

Now that we are up to speed on how to get input from the keyboard we can turn our attention to the mouse. The mouse is just as easy as the keyboard to use, but we have more methods at our disposal when we deal with the mouse.

The following are the methods that we can use for the mouse:

public boolean mouseEnter(Event e, int x, int y)

public boolean mouseExit(Event e, int x, int y)

public boolean mouseDown(Event e, int x, int y)

public boolean mouseUp(Event e, int x, int y)

public boolean mouseMove(Event e, int x, int y)

public boolean mouseDrag(Event e, int x, int y)

One important point to note here is that for each of the above methods you can simply return true.

All of the methods for the mouse are fairly clear and understandable. You will notice that each method receives the x and y coordinates of the mouse location with respect to the upper lefthand corner of the applet window.

The mouseEnter and mouseExit methods are called automatically when the mouse enters or exits the applet area. The mouseDown and mouseUp methods are called when a mouse button is pressed or released. The mouseMove method is called when the mouse is moved in the applet area. The final method, mouseDrag, is called when the mouse is moved in the applet area while a mouse button is pressed.

The best way to test out these different mouse methods is to set up a basic applet that prints a string out to the screen. The string should be changed by the various mouse methods to reflect which method was called and the current x, y location of the mouse.

You should now have a fairly good idea about what it takes to work with input from the keyboard and mouse in java applets. It is now time to go back into the realm of output.

Double Buffering

We have been focusing on the input and looping phases, but now it is time to get back into looking at the output phase. Double buffering is a technique that will be familiar to anyone who has done much in the way of game programming. A common problem that you will notice as soon as you make an applet with threads is that you get an annoying flicker. This flicker comes from the fact that we are drawing bits and pieces of our game when we should be drawing all of our game at once.

The trick to fixing the flicker problem is actually very simple. The first step that we want to take is to setup a couple of variables.

Image offscreenImage;

Graphics offscr;

The variable offscreenImage is going to be our backbuffer where we will draw what will be appearing next on screen. We need the variable offscr to hold the graphics context of our backbuffer so that we can later draw a variety of stuff into it just like we normally would in our paint method.

width = size().width;

height = size().height;

offscreenImage = createImage(width, height);

offscr = offscreenImage.getGraphics();

The next important step that we need to take is to setup our backbuffer so that we actually have some space to draw into and then we need to get its graphics context which we will store in offscr. You will notice that I use width and height to store the dimensions of my applet space and that I then use these values to create a backbuffer that is the same size. In order to get the graphics context of this backbuffer I simply have to call the method getGraphics in my variable offscreenImage and we are set. One other note that you need to watch out for is that these four lines of code should be placed inside the init method so that they are executed when the applet first starts up.

offscr.setColor(Color.black);

offscr.fillRect(0, 0, width, height);

offscr.setColor(Color.blue);

offscr.fillArc(0,0,width,height,startAngle,arcAngle);

g.drawImage(offscreenImage, 0, 0, this);

This next segment of code is an example of what will be in your paint method. The first step is to clear the backbuffer. We do this by simply creating a filled rectangle that has the dimensions of our applet space. Take care to note that rather than using the usual graphics context g we are instead using our backbuffer which is represented by offscr. You can clear the backbuffer to any color that you want, but I decided here that black was a good color. We can then proceed to draw any shapes or text that we would like to display while making sure that we use offscr rather than g. Once we are ready to flip our buffers and display what is in the backbuffer on the screen we then use g.drawImage to display the buffer on the screen. This way we draw everything at once. If you don’t remember what the parameters of the drawImage method stand for make sure you check back to part one of this series.

public void update(Graphics g)

{

paint(g);

}

This piece of code that we need to add to our program to get double buffering working is the update method. Normally the update method clears the screen and then paints, but this will again lead to flicker so we need to make sure that all it does is paint. If you try writing a program with the above code, but leave out the update method change that I have shown you will still see flicker and all your work will be for naught so take care.

The Game

Most of you are probably familiar with the popular game of Pong. For those of you who aren’t, the game consists of two paddles and a puck which is hit back and forth until someone misses it. A miss by a player equals a point for their opponent and after a predetermined number of points one player will win. The one change that we will make is that we will make our game into an air hockey game which I’m sure many of you are familiar with. The only real difference between our game and a game of Pong will be that our air hockey game could have smaller goal areas, but we will discuss this in more detail later on. Now, this may seem rather simple, but it can be an extremely fun little game to play and is an excellent place for us to start our journey into the world of java games.

There are four important phases that we will need to look for in most games: input, processing, output, and loop.

The input phase consists of getting the intended actions from the players by looking at the states of the keyboard and mouse. We can then interpret the states of these input devices and have the game respond appropriately to them. For our game of air hockey the main action that the user is going to want to perform is moving their paddle up and down. While it is not found in standard pong like games we may want to also allow for the paddle to moved left and right. It is not immediately apparent that we may want to use the mouse, but we will find a possible use for it later on.

The processing phase consists of moving the paddles based on the input, moving the puck, and checking for collisions. The types of collisions that we are looking at are puck to paddle, puck to goal area, and paddle to edge of board. If we allow for the user to move the paddles left and right we also have to check for paddle to paddle collisions. Another item that we should keep track of in the processing phase is the current state of the game. There are three main states that we will probably want to have for our game: puck moving, puck set to go after goal or at beginning of game, and game over.

The third phase is to display the appropriate output on the screen. The means that we are going to have to display the two paddles, the puck, and the scores for each player. We might also want to display some additional information, but for now we will stick with the basics.

The fourth and final phase is what I loop. The purpose of loop is to perform the previous three phases over and over. How much fun would our air hockey game be if we only accepted input once, only calculated puck and paddle movement once, and only drew the paddles and puck once? The answer of course is not very much fun at all. In order to ensure that the input, processing, and output occurs again and again until the game is over we will end up using threads which I will talk about in a moment.

Before we get our hands dirty with some coding I want to say that it is important to roughly sketch out what you want to do in your game before you start to code. It is natural for programmers to want to jump right in and code , but it is important to at least spend a little time going over what you want to do and how you are going to go about doing it. This time spent planning will ensure that you have a clear and focused goal, that your idea is feasible, and that you go about your programming in an organized manner.

Coding the Game

So now that we have a basic idea of how to use threads, double buffering, and the mouse and keyboard we can start working on an air hockey game.

First off we are going to need to accept input so that the players can move their paddles up and down. The easiest way to accomplish this is going to be to set up a keyDown function with 4 if statements that test the key pressed against the up and down keys for the two players. These if statements will end up changing the value in a variable holding the y position of the appropriate paddle. This y position will be used for the drawing of the paddle and for collision tests to ensure that the paddle doesn’t go off the edge of the board.

If you want to expand the game you could allow the paddles to move left and right as well as up and down. This means that you will have to add some extra collision detection code in to check the paddles against the edges of the board and the other paddle. You could also try adding in some mouse support for your game. This could include the ability to use the mouse to move one of the paddles around or you could use the mouse to create some sort of menu system.

The third step that we need to cover is the output on the screen. Now you are probably saying what happened to step number two, but don’t worry it is coming shortly. The output that we need for the screen is very, very simple. All we really need are two filled rectangles for the paddles, an oval for the puck, and some text to display the score. If we wanted to we could make things more complicated by having a nice background for the game to be played on (an image or something dynamic like a star field), having a puck whose color is constantly changing, or even obstacles on the board (this could get complicated so don’t start off trying to do this).

The second phase which I talked about at the very beginning is the processing stage. So what needs to be processed in our game and what variables will we most likely need? The goal of the game is to have a puck bouncing back and forth on the board and it should bounce off the walls and the paddles. First off we are going to need to have the coordinates of the puck and the two paddles. This means that we will need to have an integer x,y value for each of them. Next we are going to need to have a speed value for the puck. The speed values will be integers which represent the change in the x and y position of the puck in each time step. With this much information we can move the puck in our run method and test the positions of the puck and paddles to see if there is a collision.

This brings up a good point: how do we detect collisions. There are a lot of complicated formulas for checking collision, but there are some simple methods that will be sufficient for us. If you like you can go back to the bouncing ball applets we began with.

Paddle-Board Collision

For our purposes I’m going to assume that the board is the size of the entire applet area. This means that the extents of the board are from 0 to size().width and 0 to size().height. You will notice that here that I have used size().width and size().height here to represent the maximum height and width of the applet space. If you use these exactly as I have written here in your programs they will provide you with the values that you desire. Testing the paddle for collision with the edge of the board is as simple as having if statements to check whether or not the y value of the paddle is greater than the maximum height or less then zero. If you allow the paddle to move left and right then you can check to see if the x value of the paddle is less then 0 or greater than the maximum width. If we discover that there is a collision or we have gone over the edge of the board then all we have to do is move the paddle back onto the board (eg. 0 or max. height – paddle size).

Paddle-Paddle Collision

We only need to worry about paddle to paddle collision if we allow the user to move his paddle to the left and right. You could even avoid having to worry about this type of collision if you said that a player couldn’t move his paddle past the center of the board. Another option would be to let the paddles simply pass through each other with no collision at all. I would recommend one of the above options since the number of times the two paddles are going to get close enough for a collision is almost zero. If you are still interested in pursuing this type of collision then I would recommend that you follow the process that I will discuss in a moment for paddle and puck collisions. This method involves taking the x and y position of the paddle and comparing it against the four edges of the other paddle to see if it is inside the other paddle. If a collision has occurred then we want to move it out of collision.

Paddle-Puck Collision

Checking for collisions between a paddle and the puck isn’t easy, but it is a very important part of the game. You could try to get fancy here, but I recommend that you keep it simple. I believe that the easiest way to check for a collision is to check the x and y coordinates of the puck against the four edges of the paddle. The four edges of the paddle are: paddlex, paddlex + paddle_width, paddley, and paddley + paddle_height. With this knowledge in hand it is a simple matter of creating an if statement for each paddle which will consist of four parts testing the four aforementioned edges against the x and y of the puck to see if the puck is inside the paddle. If it is unclear what is going on I would recommend drawing a little diagram of what the situation looks like and this should make things easier for you. Once we have discovered that a collision has occurred then we need to come up with some means of resolving the collision. The best way to do this is to take the speed of the puck in the x direction and negate it. By negating it we get very believable bouncing of the puck off of the paddle. You could also try to add in the possibility of the puck bouncing off the bottom part of the paddle so the x would stay the same, but the y would change. While this might initially sound like a great thing to do it doesn’t necessarily add a lot to the game so try it out and decide for yourself whether it is worth the extra effort.

One problem that you might come across is a shimmy. This is where the puck collides repeatedly with the paddle and ends up shimmying up or down the paddle before it finally breaks away. This is something that you want to avoid at all cost because it makes your game seem unprofessional and incomplete. A possible solution for this problem is to check the speed of the puck in the x direction. If the puck is coming towards the paddle then it can be checked for a collision. If on the other hand it is going away from the paddle then we can ignore any collisions that we might think we have.

Puck-Board Collision

Testing for collisions between the puck and the edge of the board is identical to the test that we performed when testing for collisions between the paddles and the board. The only tricky part that we have to watch out for here is that we must check the x and y values of the puck against the extents of the board. Again to resolve this particular type of collision all we have to do is negate the x or y speed of the puck depending on the edge of the board that we hit.

Puck-Goal Collision

This is going to be the same process as what we had to carry out for the paddle to board collision test except in this case we want to check the puck against a limited part of the board. As a starting point lets assume that the goal area consists of the entire left and right hand parts of the board. This means that we can check the x value of the puck against 0 and the maximum width of the board. If we register a collision here then we must increase the score for the appropriate player and put the puck back at the center of the board.

The fourth and final phase is to loop. The loop as we discussed before is accomplished by using threads and the run method. If you are unsure about this you can either refer back to the threads section or check out the sample code for the air hockey game which is available on my web site.

You are now well on the way to creating a great air hockey game. Once you have a basic game up and running you should start playing around and trying to make additions and alterations to your game.

Java Game Programming Part III:

The Power of Arrays

We have talked about the steps necessary to create an air hockey game. In order to make this game we needed to know about double-buffering, threads, and how to use the mouse and the keyboard. Now we will talk about arrays, you may be thinking that arrays are pretty trivial and have no big impact on making games, but this couldn’t be farther from the truth. In our exploration of arrays we will look at how to create Tetris, and PacMan games. We will also look at some basic file handling concepts as well as some important points about object-oriented programming.

Arrays

For those of you who are unfamiliar with arrays and how they work I will briefly outline them here.

When we are programming we use single variables which can be viewed as a lone box in memory. A typical problem that we will come across when programming is the need to store the information for an 8x8 board. By following the single variable method we would be required to declare 64 variables and wouldn’t be allowed to use for loops for our initialization or when we wanted to traverse the whole board. This hardly seems like an efficient way to program and would not only result in large programs, but also in frustrated programmers.

Eg. Single variable method of storing an 8x8 board

int board1;

int board2;

...

int board63;

int board64;

In order to make our lives easier and to make our code at least semi-readable by others we are fortunate that there is a better way. An array is a collection of variables that can be referred to by one name and accessed with an index. For example, if we wanted to access the second element in the second row of the board we would type a line of code that contained board[1][1]. You will notice that the indexes are one less than the number that I was looking for originally. The reason for this is that in an 8x8 array the indexes go from 0-7 for both dimensions. By using arrays we can now use for loops to traverse through the index values of the array and initialize or make changes to all of the data in the array.

Eg. Array method of storing an 8x8 board

int board[][] = new int[8][8];

Now doesn’t that look a lot nicer than the 64 lines that would have been required with the other method? You will notice that in my example above I used a two dimensional array. One, two, three, and maybe even four dimensional arrays are the ones that you will generally see, but for the most part you will only use one and two dimensional arrays.

You will notice that when we create our arrays we must use memory allocation to set up the dimensions that we would like. This means that with the name of the variable we include an empty set of square brackets, [], for every dimension that we want the array to have. We then have the choice of allocating the memory for the array at the time we declare the variable or we can defer this until later on in our program when we are going to use it. Whenever we decide to allocate the memory we set the variable equal to the keyword new, followed by the type of the data which will be found in the array, and then sets of square brackets for every dimension of the array. This time, however, the sets of square brackets instead of being empty will contain the number of "squares" in each dimension.

This may seem a little unfamiliar at first for those of you who have never seen arrays before, but once we start to use arrays in the games later on you will quickly pick up on how they work.

Vectors

Now that we have taken a look at how to work with arrays in Java it is important to take a look at a useful data structure that is also available to us, the Vector. The Vector is fancy array that Java allows us to use. This fancy array dynamically alters its size to fit the data that we store in it and also makes access to the elements within extremely easy. This means that we can keep track of a number of elements without knowing in advance how many there will be.

Useful Vector methods are:

• addElement()

• elementAt()

• firstElement()

• lastElement()

• isEmpty()

• size()

To initially create a Vector we declare our variable as normal and then set that variable equal to a new Vector.

Vector sample = new Vector(0,1);

The first parameter in the constructor for the new Vector is the initial capacity that we want it to have. The second parameter is the number of new elements that will be added to our Vector when we run out of elements that we can store information in. If you think that you will be adding a large number of elements to your Vector you may wish to have larger values for the initial capacity and the capacity increase. A problem that we have to watch out for here is that we don’t end up wasting space (ie. having empty elements sitting around).

When new elements are added via the addElement method they are added to the end of the Vector.

int number = 5;

sample.addElement(number);

In order to get elements out of the Vector we use the elementAt method and pass to it the index of the element that we wish to access. Another useful method that can be used in conjunction with the elementAt method is the size method. The size method allows us to find out the number of elements that we have in our Vector and the value passed back from this method could be used as an upper limit in a for loop.

int number2 = (int)sample.elementAt(1);

The methods firstElement and lastElement are similar to elementAt, but they return either the first or last element and are not passed an index value.

While you may not immediately find a use for the Vector in your games it is a powerful tool that is available to you to use. I am sure that you will find a point where a Vector will fill an important position in one of your games and make your life easier.

Object-Oriented Programming

When you hear programmers talking about Java one of the phrases that comes up frequently is object-oriented programming. It seems that everyone who is anyone is on the OOP bandwagon these days so we should take a look and see if there is anything we can get from this.

Now you may think that working with classes is going to be complicated and it can be, but we have actually already been working with classes. That’s right! If you look back at our Java applets they have been located inside a big class and we have also used classes created by others to make our lives easier. You can actually get away with only ever using this one class and never creating any additional ones, but if you want to make a game of any depth or with any complexity you should learn to love classes or at least realize that they are worth the effort it takes to learn them.

Classes consist of two main parts: member variables and methods. Member variables are variables which can be accessed by all of the methods within the class and possibly by methods outside the class. The member methods are basically the same as member variables in that they can be accessed by all of the methods within the class and possibly by methods outside the class. Now you are probably asking why is it possible that the methods and variables might not be accessible from outside the class. The answer to this question is that methods inside a class can either be private or public. If they are public then any method outside the class can access/call it, but if they are private then only methods inside the class can access/call it.

Now that we have a basic idea of what classes are why should we bother using them? The reason we want to use classes is that they help us to organize our code, make our code more intuitive, and enable us to make complex programs with greater ease. An example of a situation in which a class would be useful is in an arcade shoot’em-up with spaceships. What is the difference from one spaceship to the next? The location and what the ships happen to be doing are different from one to ship to the next. Is the type of information that we keep track of for the spaceships different from one to the next? Can one spaceship do anything different from the next spaceship? The answer to both of these questions is generally no. By creating a class for a spaceship we can create a template of information and actions that can be called upon for all of our ships without having to type out a bunch of code for each ship.

Eg. SpaceShip class

class SpaceShip

{

private int amount_of_ammo;

private float x_coordinate;

private float y_coordinate;

private float z_coordinate;

public void MoveShip();

public void Shoot();

};

Now this is only a sample class so I haven’t included any code for the methods in the class, but this gives you an idea of the setup that we would need. Once I have this class laid out I could then create an array of ships that I could access and manipulate.

Eg. Creating an array of SpaceShips

SpaceShip aliens[] = new SpaceShip[10];

This is all that we need to worry about for the time being in terms of classes. There are even more modifiers available for you to use so if you are interested I would encourage you to read more about this subject. I hope that one point you will take from this section is that classes can be used effectively in your code to make your life easier and to make your code neater and more organized.

Files in Java Applets

Before we get into discussing the two games we will make we need to first look at how to read in information from files. Due to security issues and considerations with applets we have to go about our file input in a slightly different manner.

The first step we need to take is to open up a connection to our file which will be located on our computer, not the user’s computer. In order to set up this connection we must create a variable of type URL. URL is in the package so at the beginning of our program we need to have an import statement for this.

import .*;

...

URL url;

When we want to open up the connection to the file we create a new URL object and pass the parameters getCodeBase() and the name of the file we want to open. The name of the file can either be a constant string or a variable. In order to get our program to compile we need to make sure that we embed the URL creation in a try…catch statement. The exception that we need to catch is MalformedURLException.

try

{

url = new URL(getCodeBase(), "test.txt");

}

catch (final MalformedURLException e)

{

return;

}

Once we have created our connection to our file we need to then open it up so that we get input from it. The first step that we need to take here is to create a variable to represent our input stream which will be of type InputStream. In order to use the class InputStream we need to make sure that we import everything in the java.io package at the top of our program.

import java.io.*;

...

InputStream stream;

The next step that we need to take is to take our connection to the file and open it up for input. In order to do that we take the variable that represents our connection and call the method openStream() to get a stream that we can get input from. We then store this stream in our variable of type InputStream. Just as before when we were dealing with creating a URL connection we need to make sure that we use a try…catch statement to catch the IOException error.

try

{

stream = url.openStream();

}

catch (final IOException e)

{

return;

}

Now that we have our input stream we need a nice way to be able to read in the input from this file. In order to do this we are going to create a variable of type StreamTokenizer. The class StreamTokenizer has some nice features that allow us to specify characters that will signify that comments are to follow; characters that are whitespace; characters which are parts of words or units that we will be reading out, and it also provides a nice means of grabbing tokens from the file.

StreamTokenizer tokenizer;

In order to create our stream tokenizer we need to create a new StreamTokenizer object to which we will pass our input stream variable that we set up previously. Again this piece of code needs to be inside a try…catch statement that will catch the exception IOException.

try

{

tokenizer = new StreamTokenizer(stream);

}

catch (final IOException e)

{

return;

}

Now that we have our StreamTokenizer set up to perform input from our file we need to make sure that we set a couple of options so that our tokens will be read out properly. The first option that we need to set is the range of characters that will make up all of the words that we will be reading in from the file. The first parameter to the wordChars method that we will use for this task is the start of the character range and the second parameter is the end of the character range. These same parameters are used for the whitespaceChars method. This method stores the range of characters that will be considered to be whitespace in our input file.

tokenizer.wordChars(0,' ');

tokenizer.whitespaceChars(' ',' ');

In order to get the next token from the file we need to use the nextToken method. The nextToken method returns an integer that represents either the end of file, or tells us that the token is a number/string. As you will see in the sample code below there are constants provided that we can use for these (TT_EOF, TT_NUMBER, TT_WORD).

int token;

if ((token = tokenizer.nextToken()) != tokenizer.TT_EOF)

{

switch (token)

{

case tokenizer.TT_NUMBER:

message = "Number: " + tokenizer.nval;

break;

case tokenizer.TT_WORD:

message = "Word: " + tokenizer.sval;

break;

}

}

Armed with this ability we now have the capability to store board configurations or other information in text files that our games can read in. This will be important in the next couple of sections when we discuss how to make PacMan and Tetris.

Arrays in Games

Now you might be saying to yourself that this is all fine and good, but are arrays really that useful when I’m making a game. Well the answer is yes. There are several games that can be built with arrays as the fundamental element and we will cover some of them here. An important skill to learn is taking the new concepts that you learn with any programming language and then thinking about how you could use them when doing game programming or just doing programming in general. By taking the concepts and looking at them from all angles you end up building yourself a toolkit of options and skills that can be brought into action on command.

PacMan

I think that most people have played Pacman or a game similar to it. The goal of the game is to collect as many coins as possible without being killed by the evil ghosts that come around and try to kill you.

The first part of our PacMan game that we want to look at is how to setup our array and how to draw what the array represents on the screen. The squares on our board will consist of either black (not a wall) or blue (wall) squares. On top of those squares that are not a wall there is the possibility for a coin or one of our pacmen to be there. So we will have one of 4 possibilities:

• square is a wall

• square is not a wall and has nothing on it

• square is not a wall and has a coin on it

• square is not a wall and has a pacman on it

Now you might be saying, "Isn’t it possible for a pacman and a coin to be on the same square?" This will happen, but for drawing purposes we would only draw the pacman because he will have eaten the coin.

To store the information for our board we will want to have a two dimensional array of characters. We will then store one of five characters in each element of this array: ‘e’ for enemy pacman, ‘p’ for our pacman, ‘c’ for coin, ‘n’ for a wall, and ‘b’ for nothing.

In the init portion of our applet we will need to initialize our array with these character values to setup our board. We have two options when considering how to go about initializing our board: read the board characters in from a file or hardcode the values in the source code. Either of these options is perfectly fine, but you will find that by reading the board in from a file you make it easier to rotate through a variety of maps; your code will be easier to read; and it will be easier to edit your boards. Now that we have our board initialized the next step is to figure out how we can draw it. In order to draw the base portion of our board (wall or no wall) we need the following chunk of code.

switch (board[count1][count2])

{

case 'n': offscr.setColor(Color.blue);

break;

default: offscr.setColor(Color.black);

break;

}

offscr.fillRect(boardx+count2*blocksize,boardy+count1*blocksize,blocksize, blocksize);

The code block above is going to be located inside a double for loop with the first for loop using count1 and the second one using count2. The switch statement should be fairly understandable. The goal of the switch is to pick the color we will draw for the square (blue for the wall; black for nothing). The switch statement uses the character in our two dimensional array as a basis for its decision. After we have picked the color all that is left to do is draw the rectangle that represents the element of the array that we are currently on. In order to draw this rectangle we use the following variables:

• boardx holds the offset from the edge of the applet area to the edge of the board on the x-axis

• boardy holds the offset from the edge of the applet area to the edge of the board on the y-axis

• count1 is the counter for the first for loop

• count2 is the counter for the second for loop

• blocksize is the size of the rectangle (square) that I want to draw

The result of this code by itself would be to draw the board that we have created without displaying the coins or pacmen.

In order to display the coins all we have to do is draw a filled oval at the appropiate spot on our board if the square has the letter c in it. The last part left, as far as the display is concerned, is how we will draw the pacmen. This part is really up to you and your creativity but what I decided to do is use a filled arc. By using a filled arc and a little timer I was able to set up my little pacmen with mouths that open and close. You will notice in my code, however, that I have put the drawing code for my pacmen inside a class that is used for my pacman and the enemy pacmen. The reason for this is that it makes my code a lot neater and also gives me the ability to easily change the number of enemy pacmen that I have.

The next part that we need to discuss is how to represent the data and actions that are associated with the pacmen in the game.

The actions that a pacman can perform are: move, draw, and change direction. The first two are fairly obvious in what they do, but the use of the third one might not be immediately apparent. The reason a pacmen has to be able to change directions is that I want the mouth to be pointed in the direction that he is going. If you were to leave this ability out the game will still be okay, but it will be visually lacking. When you are making games like this you have to remember that the user will notice little details like this and if it is at all possible you should take care of them. The direction change will happen when the user presses one of the arrow keys on the keyboard so we will need to use what we learned last time about the keyDown method. The drawing of the pacman will be fairly simple and will involve drawing a filled arc that may be a circle if the mouth is closed or have a gap if the mouth is open. Out of the three actions that a pacman will perform the ability to move is the most complex in terms of all the options that must be considered. The options that we must keep in mind are: will he hit a wall ,another pacman, a coin, or nothing at all if he moves to the next square based on his current direction.

The data that we need to keep track of for a pacman are: whether the mouth is open; how long it takes for the mouth to open or close, the size of the mouth angle; the direction he is facing; whether he is controlled by the user or the computer; the current speed and maximum speed; the x and y location on the board; the color that he is drawn in; and a counter to keep track of how long it has been since he opened or closed his mouth. You may wish to add or remove items from this list of data that we are keeping track of, but I think that you will find that this is a good place to start.

After we have the pacmen set up, all that is left is to increase the score when a player goes over a coin and deal with what happens when the player dies. If you wanted you could also add in an introduction screen and even multiple levels for the user to play on.

Tetris

Tetris, for those of you who have not played it before, is a game in which pieces of different shapes fall from the top of the screen and land on the bottom of the board area or on top of other pieces. When an entire row of the board is filled in with parts of pieces the row disappears, the rows above it shift down, and the player gets points. As the user gets more and more rows disappearing the speed at which the pieces fall is increased. If the user should ever fill the board to the very top without being able to eliminate any rows then they lose.

The first part of the tetris game that we will look at is the board. The representation and setup of the board will be almost identical to that of the PacMan board. The following are the various possibilities for a square on our board:

• empty square

• square contains part of a piece

For my version of the game that I created I had 5 different piece types that could fall down so I had the following characters that I looked for: y for yellow, r for red, b for blue, g for green, p for magenta, and n for black. To take this information stored in my two dimensional array and use it to draw the board on the screen I need to use the same style of switch statement that I showed you for pacman with the following as the case statements:

case 'y': offscr.setColor(Color.yellow); break; case 'r': offscr.setColor(Color.red); break; case 'b': offscr.setColor(Color.blue); break; case 'g': offscr.setColor(Color.green); break; case 'p': offscr.setColor(Color.magenta); break; case 'n': offscr.setColor(Color.black); break;

I also end up using the same line of code to draw these rectangles (squares) on the screen.

The biggest part of making your Tetris game is creating the shapes that will fall and enabling them to rotate and collide properly. For the version that I have made I used the following shapes:

|** * * * *** |

|** * * * * |

|** ** * |

|* |

After you figure out how the basic shapes work you can experiment with your own shapes and variations.

The first consideration that we must look at is how do we pick the type of piece that will fall. I recommend setting up some random number like we discussed in part I and assigning a range of values to each of the pieces that you have. Once you have decided on the piece type that you want to have you can then place it on the board. You will notice that in my code I have stored the four board array locations of my piece in an array so that I can keep track of my piece locations with ease. Every time the piece moves we need to clear its previous location on the board and then put it in its new location on the board.

Once we have a piece moving down the board we need to allow the user to move and rotate it. The first piece of this puzzle is to watch for keyboard events. You will need to allow the user to rotate his piece as well as move it left and right. Moving a piece left or right isn’t hard, but whenever you are moving a piece (left, right, down, rotating) you will need to make sure that you check to see if the new position is legal. An illegal position would be one where the piece overlaps another piece or moves off the board. If, when we try to move our piece down, we discover that it is illegal then we know that this piece cannot be moved any lower and we can stop moving it and start the next piece. The hardest part of dealing with the movement of pieces is allowing them to rotate. You will notice that in my code for the rotation I break it down into the cases for each piece. Obviously we don’t need to worry about rotating the square since its appearance never changes no matter how much we rotate it, but we need to look at all the other pieces.

We will take a look at the rotation for one piece and then you can look at my code for how the others will work.

|(1) (2) (3) (4) |

|1 321 43 4 |

|2 4 2 123 |

|34 1 |

When dealing with the rotation of the piece it is important to just look at the relative position change. In the transition from the first diagram to the second you will notice that the y-value of block number four decreases by one and the x-value decreases by one. In the transition from diagram two to three the y-value of block four again decreases by one, but the x-value increases by one. In order to code the rotations in for all the pieces I recommend that you draw out the transition diagram for every piece and go step by step through your code accounting for every possibility. Since we are dealing with our tetris game in terms of arrays rather than just screen positions this rotation process is more complicated, but this representation makes a lot of other parts of the game (row elimination and collisions) much easier. As with the regular piece movement you need to make sure that before you let a piece be rotated you first check to see whether the new position is legal or not. In fact you will end up performing this task so often I would recommend that you set up a method that can perform this task (write this method called colorCheck(). )

After we have moved the current piece as far down as it can go we must check the current state of the board and see if there are any rows that are completely full and if so then we must eliminate them. You will notice that in my example solution on my web page I start at the bottom of my board and work my way up. For every row that you eliminate you will give the user additional points based on the difficulty level and the number of rows they were able to eliminate at one time.

You now should have a basic understanding of what is involved in creating your own Tetris game.

Other Potential Array Based Games

In addition to the two games that we have looked at here in detail there are many other games that could be created by using an array base. A couple examples of other games are Nibbles and Snafu.

Nibbles is a popular game that consists of two or more "snakes" moving around the board area trying to beat the other snakes to the power-ups that appear randomly on the board. The more power-ups you eat the longer your snake gets and the more difficult it is to maneuver around. This game can be expanded to allow for a variety of maps to be used and to allow for different power-ups that could shrink your "snake" or reverse the direction in which all of the "snakes" are moving.

Snafu is a game that also consists of two "snakes" moving around a map like nibbles, but the snakes leave a trail behind so the number of valid squares that the "snakes" can move to is continuously decreasing. The first "snake" to hit itself, the other "snake", or the wall loses.

You can even create maze style games or variations on PacMan and Tetris. The main idea to remember when you are deciding what game you would like to create is to be creative.

The information regarding game programming and code that has been included is based on a series of technical articles written by Adam King, a professor at the University of Calgary.

For additional Information try the following Search criteria:

creating a game using java

game development using java



................
................

In order to avoid copyright disputes, this page is only a partial summary.

Google Online Preview   Download