|
When developing a 3D game, the first thing you need to create are 3D models. After all, every visible object in a 3D game is some sort of three dimensional model. Therefore, it should come as no surprise that the player's ship, the rocks, the bullets and the ufo are all three dimensional models that are loaded into the game and converted into an OpenGL display list. Even the stars field in the background is a 3D model. It is merely a plane with the an image of space mapped as a texture to it. Thus, before doing anything, we need some models.
For a simple game like Asteroids, models are not difficult to aquire. You can download free models from the web, like I did for the space crafts or you can make your own like I did for the rocks, bullets, and background. Once you have your models, they can be easily loaded in the the game engine. The file, Asteroids.zip, contains the models you will need and it also contains the textures that those models use. So, let's go ahead and write some code.
The Code
import java.applet.*
This line should be obvious. We want our game running as an applet in a web browser. So, we must include the applet package.
public class Asteroid extends Applet implements Runnable
{ Thread gThread = null;
GameEngine gEngine = null;
boolean stop=false
Since we want this game running in a web browser, we simply extend our class from the Applet class. We implement the Runnable interface so that our game run as a thread. The Java Programming Language expects applets to halt on user input. As such, an applet cannot run for a long period time without running as a thread. Therefore, our game must be running as a thread and this is an easy way to create a thread.
The next three lines declare the global protected variables. gThread contains a reference to the game thread. You know, the one that we are implementing with the runnable interface. gEngine contains a reference to the 3D game engine (GameEngine.class) which powers this game. Therefore, you will need GameEngine.class in the same directory as this Asteroid.class when running the game. The variable stop is used as a flag to tell the thread to stop. This is the proper method of stopping threads.
Model mPlayer, mBullet, mRock;
Actor aPlayer
If you study all seven tutorials in this section, you will learn to make a watered down version of Asteroids. I intentionally kept it watered down to prevent the tutorial from becoming too complex. However, after reading through all seven turorials, you should gain the knowledge to make a very elaborate Asteroids or an etirely different game. So, for this tutorial, we will use only three models - the player, the bullets and the rocks. Thus, we need three variables to hold a reference to each model. Also, I have included a variable to create an actor. Actors are the topic of the next lesson but without an actor this program will appear to do nothing when it runs. Thus, an actor is included in this lesson to verify that the program is running correctly.
private void sleep(int t)
{ try { Thread.sleep(t); }
catch(Exception e){ };
I like to include a sleep method so that I can write easy one line delays in my code. Thread.sleep() is the best way to delay execution of your code because it returns the unused quantum time to the OS. Alternately, For and while loops use the entire quantum and can be a burden on your system.
public void start()
{ if(gThread == null)
{ gThread = new Thread(this);
gThread.start();
}
The start() method is required for the runnable interface. It is called automatically when the class is executed and should contain the code to start the thread. For most applications, the start() method will look similar to what is listed above.
public void stop()
{ if((gThread != null) && gThread.isAlive())
stop=true;
gThread = null;
gEngine.cvsDispose();
Stop() is called automatically to terminate execution of the thread. For most applications, the stop() method will look similar to what is listed above. Note that the last line, gEngine.cvsDispose(), attempts to free resources use by GL4Java. From what I know about Java, this isn't needed but it is still good practice especially for those whose code in C++, too.
public void init()
{
Init() is called automatically for all applets. I don't have any code that needs to be in this methods. So, it is empty.
public void run()
{ gEngine = new GameEngine(getSize().width, getSize().height, getCodeBase());
add("Center", gEngine);
gEngine.models.add(mPlayer = new Model(getCodeBase() + "Ship.glo"));
gEngine.models.add(mBullet = new Model(getCodeBase() + "Bullet.glo"));
gEngine.models.add(mRock = new Model(getCodeBase() + "Rock1.glo"))
This last method, public void run(), is also required by the runnable interface. It contains the code that you want running indefinitely in a thread or, in this case, the code for our asteroids game.
The first line in the run() method creates an instance of the gEngine and stores the reference to it in the variable gEngine. Then, the game engine [component] is added to the applet [container] using the method add(string, component) method inherited from the container class. The next three lines load the models from glo files. They are pretty much self explainatory. However, the glo files are not. So, I will detail glo files later.
while(gEngine.modelCount<3) sleep(100)
The Java Programming Language likes to perform tasks, like file loading, in their own threads. And loading models is no exception. So, when we get to this line, the models are probably still loading in the background. So, before we can use them, we need to wait for them to finish loading. We can do this with a while loop but be sure to add a call to sleep. Otherwise, your while loop will compete for clock cycles with the file loading code!
aPlayer = gEngine.addActor(mPlayer, 0, 0, -200)
In order to tell if the code is working, we need to see at least on of the models. Therefore, an actor is added.
while(!stop)
{ gEngine.sDisplay();
sleep(10);
} }
Now, call gEngine.sDisplay() to see your OpenGL game screen. You only need to call sDisplay() once to see your screen. However, I wrapped it in a loop in preparation for the next lessons.
The glo file format
The glo file format is a custom file format that my game engine uses to discribe model data so that it may be loaded into an OpenGL display list. The file format derives its name from the phrase OpenGL object which is what the file describes. Basically, it is a text file that loosely resembles a set of OpenGL functions. So, if you are familiar with OpenGL, the glo file should be fairly readable.
Let's look at some examples
Perhaps the best way to learn how to write a glo file is by looking at some examples. Don't get too excited thinking you will write the next Quake. The glo file format, as well as my game engine, is still fairly underdeveloped. Thus, only static objects can be modeled at this writing. My next project will involve animation. So, I will be extending the glo format to allow for hinges and greater use of texturing. Anyhow, lets look at some files.
Bullet.glo
COMPILE
COLOR3F
1.0 0.65 0
BEGIN_TRIANGLES
-1.5 -0.75 0 1.5 -0.75 0 0 1.5 0
0 -1.5 0 1.5 0.75 0 -1.5 0.75 0
END
ENDLIS
Bullet.glo is a very simple model and doesn't even contain a texture. In fact, the entire model file is listed above. It begins with COMPILE, which initiates the display list. Then, COLOR3F is used to change the working color to yellow. BEGIN_TRIANGLES is basically saying, glBegin(GL_TRIANGLES) and each succeeding line should contain nine number describing a 3D triangular plane or the END statement. ENDLIST terminates the COMPILE command. That's all there is to it. In case you are wondering, BEGIN_QUADS is a valid command and expects twelve number respectively.
Rock1.glo
GENTEXTURES
1.0
Texture.png
COMPILE
COLOR3F
0.3 0.3 0.3
BINDTEXTURE
1.0
BEGIN_EZTEXTRIANGLES
0.250000 -23.75 0.0 12.125000 -20.568104 0.0 6.1875 -20.568104 10.284052
6.000000 18.0 1.0 6.187500 18.818104 8.534052 13.125 20.568104 -0.5
0.250000 -23.75 0.0 6.187500 -20.568104 10.284052 -5.687500 -20.568104 10.284052
... [deleted data] ...
10.534052 11.875 -17.812500 13.125000 20.568104 -0.5 20.818104 11.875000 0.0
END
ENDLIS
Rock1.glo is similar to Bullet.glo but it contains more points and a texture. I didn't feel it was neccessary to display all the vertex data. So, I replaced deleted data with "... [deleted data] ...". Otherwise, the entire file is listed. The first three lines shows how to create a texture. The first line tells the model loader that we want a text. The second is the name, which is always a number and the third line is the file name of the texture. It must be a png file and must conform to the dimensions and any to texture requirements of OpenGL. To get the texture on the the polygons, we first call BINDTEXTURE and tell it what texture to map to the polygons. Then we use BEGIN_EZTEXTRIANGLES which is a no brain way to get the texture mapped to each polygon. The texture is applied to each polygon individually. It is not wrapped around the model. I have not implemented that feature yet. So, don't try. BEGIN_EZTEXTRIANGLES is used as an easy way to take advantage of lighting effects on textures. It also gives an easy means for mapping bitmaps into your game. For example, you may use it to put an image of space in the background or to create icons for the number of player ups remaining.
Ship.glo
GENTEXTURES
1.0
Flat.png
COMPILE
COLOR3F
0.45 0.45 0.5
BINDTEXTURE
1.0
BEGIN_EZTEXTRIANGLES
-2.4 0.3 0.2 -2.4 0.1 0.2 -1.9 0.3 0.5
... [Deleted Data] ...
2.4 0.6 0.2 1.9 0.6 0.5 2.4 0.4 0.2
END
COLOR3F
1.0 0.7 0.0
BEGIN_TRIANGLES
-2.4 0.6 0.2 -1.9 0.6 0.2 -2.4 1.3 0.199
... [Deleted Data] ...
1.9 0.6 0.2 1.9 1.3 0.5 1.9 0.6 0.5
END
COLOR3F
0.5 0.0 0.0
BEGIN_TRIANGLES
-1.4 -1.3 0.8 -2.4 -2.4 0.2 -1.4 -2.8 0.8
... [Deleted Data] ...
1 -1.3 0.4 1 -2.1 0.4 1.4 -1.3 0.8
END
ENDLIS
Ship.glo uses both textured and non-textured polygons to create a model with multiple colors. If you understand the first two models, this one should reveal no surprises.
Your applet should displays the player's space craft motionless and centered in the display.
|