[size="5"]Arrays
If you have programmed in other languages or have worked with Java you have probably seen arrays in action. 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;
Eg. Array method of storing an 8x8 board
int board[][] = new int[8][8];
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 in this article you will quickly pick up on how they work.
[size="5"]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()
Vector sample = new Vector(0,1);
When new elements are added via the addElement method they are added to the end of the Vector.
int number = 5;
sample.addElement(number);
int number2 = (int)sample.elementAt(1);
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.
[size="5"]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();
};
Eg. Creating an array of SpaceShips
SpaceShip aliens[] = new SpaceShip[10];
[size="5"]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 java.net package so at the beginning of our program we need to have an import statement for this.
import java.net.*;
...
URL url;
try
{
url = new URL(getCodeBase(), "test.txt");
}
catch (final MalformedURLException e)
{
return;
}
import java.io.*;
...
InputStream stream;
try
{
stream = url.openStream();
}
catch (final IOException e)
{
return;
}
StreamTokenizer tokenizer;
try
{
tokenizer = new StreamTokenizer(stream);
}
catch (final IOException e)
{
return;
}
tokenizer.wordChars(0,' ');
tokenizer.whitespaceChars(' ',' ');
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;
}
}
[size="5"]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.
[size="3"]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. You will notice in my version of the game on my website I used evil pacmen rather than ghosts.
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
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);
- 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
In order to display the coins all we have to do is draw a filled oval at the appropriate 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.
This covers the basic topics that you will need to know when sitting down to program PacMan for yourself. You can also look at my web page (www.cpsc.ucalgary.ca/~kinga) to take a look at my solution.
[size="3"]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
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:
[font="Courier New"][color="#000080"]**[nbsp][nbsp][nbsp]*[nbsp][nbsp][nbsp][nbsp][nbsp]*[nbsp][nbsp][nbsp]*[nbsp][nbsp][nbsp]***
**[nbsp][nbsp][nbsp]*[nbsp][nbsp][nbsp][nbsp][nbsp]*[nbsp][nbsp][nbsp]*[nbsp][nbsp][nbsp][nbsp]*
[nbsp][nbsp][nbsp][nbsp][nbsp]**[nbsp][nbsp][nbsp]**[nbsp][nbsp][nbsp]*
[nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]*[/color][/font]
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.
[font="Courier New"][color="#000080"]
(1)[nbsp][nbsp][nbsp][nbsp](2)[nbsp][nbsp][nbsp][nbsp](3)[nbsp][nbsp][nbsp][nbsp](4)
1[nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]321[nbsp][nbsp][nbsp][nbsp][nbsp]43[nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]4
2[nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]4[nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]2[nbsp][nbsp][nbsp][nbsp]123
34[nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp][nbsp]1[/color][/font]
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 (in my code this method is 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. If you have any questions about this you can check out my version on my web page (www.cpsc.ucalgary.ca/~kinga) or you can email me ([email="kinga@cpsc.ucalgary.ca"]kinga@cpsc.ucalgary.ca[/email]).
[size="3"]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 a lot of you have probably seen coded in Basic. The game 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 some of you may remember from the old Commodore days. The game 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.
[size="5"]Coming Soon!!!
Now that we are comfortable with arrays we can move on to discussing AWT, artificial intelligence topics, and how to pass parameters to our java applets. These topics will be discussed in the next part of this series as we move away from the basics into the more advanced topics. If have any questions or comments feel free to email me at [email="kinga@cpsc.ucalgary.ca"]kinga@cpsc.ucalgary.ca[/email]. All of the sample programs related to what I have discussed here are located on my web page (www.cpsc.ucalgary.ca/~kinga). Thanks again for reading this series and sending me numerous emails with your comments and questions.