We will make this game more sophisticated than the last by doing the following:

  • Add a second window that shows a view of our sphere from an orbiting satellite.

  • Use clickable screen buttons to move our sphere.

  • Keep track of remaining fuel.

  • Introduce timers.

  • Learn about different types of variables including integers, reals, strings, and Booleans.

  • Control the rate at which the window is refreshed.

Figure 4.11 shows a screenshot of the resulting game. Notice the second window and the additions made to the instrument display.

click to expand
Figure 4.11: Screenshot of Sphere7 game.

Adding a Child Window

You ll recall that in our last game we knew our sphere was over its target by watching the Z Distance parameter on our screen. If a camera were placed over the target plane, we could visually determine when the sphere is properly located; for instance, if our sphere were a spacecraft, this view might be achieved from a satellite orbiting overhead. With the view from an orbiting satellite available to us, the distance readings on the instrument panel would not be as critical; instead, they could serve as a back-up to the orbiting satellite and as a way to get as close as possible to the target.

Getting a second or child window to display on the screen is a simple task. First we will take control of the main window by adding the following statement:

 owindow = New Window("Spherelander",640,480,Window.STANDARD); 

In our previous programs, we never bothered creating the main window because Jamagic does this automatically for us. However, it s possible to do this ourselves with this statement. This will allow us to exercise more control over our game. Default values are useful, but there comes a time when we must grab some more control. We will be doing more and more of this as we progress through the rest of the chapter.

Aspect Ratio

The command discussed in the previous section tells Jamagic to make a new window called owindow , and to display the name Spherelander in the title bar (this is the name of our game). This window is set to the standard 640 480 pixels. This is usually a good size to use, and it ensures that the game window fits on your user s screen (covering most of it) with some room to spare. You can use smaller or bigger sizes, but you should try to keep the same ratio of width to height (640/480 = 4/3).

The ratio of width/height is known as the aspect ratio . If you stray too far from an aspect ratio of 4/3, your screen objects will seem distorted . If you are looking for a new window size, pick the width and multiply it by 3/4 (in decimal that s 0.75) to find the correct height. Or you could pick the height and multiply it by 4/3 (or 1.33 in decimal). If you need to change the height or width so that you deviate a bit from the 4/3 aspect ratio, it may not matter very much ”the best way to find out is to try it and see if the effect is important.

The portion of the line in the parentheses that says Window.STANDARD tells Jamagic to use a standard style of window. A standard window style means the window will have a border, a title bar, and a close box with an X to exit the program. You may consult the Jamagic Help section for more information on window commands.

This is one of the few cases where we have to destroy the default setting. Lines of code specifying the window size are automatically added to your program at runtime, so we have to comment these lines out. To do this, you must begin your new project (which we called Sphere7, but you can call it anything you want). Once the project is made, click the Project Icon on the Jamagic toolbar, and then on Settings. You will see a series of tabs with different names on them. Select Runtime Init., which is short for runtime initialization. You will see some code pop up in the window, as shown in Figure 4.12.

click to expand
Figure 4.12: Code to be commented out to prevent default window from being created.

This line automatically creates the default window when your program starts up. All you have to do is comment the second line out by adding two slashes in front, like so:

 // Default Win = New Window(640,480); 

Click OK when you are done.

Our child window (the window we will create inside the main window), will be slightly different from our parent window. It will be a GAME style window, which means that there will be no Close box; again, please consult the Help section in Jamagic for a list of all the possible windows . The child window is created with the following command (please note that the command is presented in two lines; when you type in the code, you should place it all on one line):

 owindow1 = New Window(owindow,"View From Orbiting Satellite",200,150,Window.GAME);// all on one line 

This statement tells Jamagic to create a new window called oWindow1 and that its parent window is oWindow . The title bar should say View from Orbiting Satellite, and the window itself should be 200 pixels wide by 150 in height (aspect ratio = 200/150 = 4/3). In addition, this window is to be a GAME style window.

Next, we want to position the child window so it doesn t interfere with the view of the sphere in the main window; to do this, we place the camera over the plane, and turn the camera down 90 degrees so that it looks straight down, as shown here:

 owindow1.SetPosition(390,5); ocamera2.SetPosition(0,2000,0); ocamera2.TurnDown(Pi/2.0); 

Recall that the plane is centered at (0,0,0), so placing the second camera at (0,2000,0) puts it directly over the plane. However, by default, the camera points towards the positive z direction (into the screen), so you must rotate it down 90 degrees (pi/2 radians).

Controlling Window Refresh Rate

Jamagic automatically updates (autorefreshes) all windows every 0.02 seconds; in other words, the image on the screen is left untouched for this seemingly short time. However, if you were to run this particular program with two windows, the result would be a jerky, non-smooth motion of the sphere. We will turn this autorefresh mode off so that we can tell Jamagic when to update the screens at a rate faster than the default value of 0.02 seconds.

The following statements, placed before the game loop, turn off the autorefresh feature:

 owindow1.SetAutoRefresh(OFF); owindow.SetAutoRefresh(OFF); 

However, after we turn off this feature, we must still tell Jamagic to update the screen; we will include the following lines of code inside the game loop. The second line tells Jamagic to update or refresh the window, owindow , and the first line activates ocamera1 . Whenever you have two cameras viewing the same scene, you must remember that only one camera can be on at a time. So, you must first turn one camera on, refresh the window that the camera is associated with, and then turn the other one on. The following code handles this:

 ocamera1.ActivateRender(); owindow.Refresh(); ocamera2.ActivateRender(); owindow1.Refresh(); 

Adding Clickable Buttons

Next we want to add the ability to click a button with the mouse in order to move the sphere (thrusters on), instead of using the arrow keys. Please refer back to the Instrument Panel portion of Figure 4.11 if you do not understand this.

Adding clickable buttons is a very simple task with Jamagic . Consider the following commands:

 myButton=New Button(owindow,"Ahead",220,30,65,20); myButton.OnClick = Doahead; 

You have enough background now to figure out the first line: a button called myButton is being made inside the window owindow . On the button, the word Ahead will be written, and the button will be located at (220,30). The button will measure 65 pixels wide by 20 in height. Pretty easy!

The second line says that function Doahead will be performed if myButton is clicked. You might recall that in the previous program, we did this by pressing the up arrow key, and that an if statement was used instead of a function. Here s the function that will cause the sphere to move forward:

 Function doahead {   mysphere.Move(5000,50); } 

That was pretty straightforward! In the code at the end of this section, you will see similar thruster buttons and functions for Back, Right, Left, and Stop commands. The Stop button stops the motion of the sphere, which was accomplished by pressing the S key in the previous program.


One of the other new features of this game is its ability to monitor the remaining fuel. In fact, careful use of fuel will be one of the key elements of this game. How do we monitor this? By simply keeping track of the amount of time that the thrusters are on. This game is set up so that once the thrusters have been on for 25 seconds all of the fuel is consumed. This was a figure arrived at by trial and error. Once you learn how to run the game, you can easily land the sphere using less than 25 seconds of thruster activation.

In our display, it makes more sense for the user to know the remaining fuel time ”in other words, how many more seconds of fuel are left. This is easily obtained by subtracting the elapsed time from 25.

So we need some form of timing mechanism so that we can know how long the thrusters are on. In other words, when either the Ahead, Left, Right, or Back buttons are clicked, a timer must start, and the timer should stop when the Stop button is clicked. The elapsed time must then be subtracted from the 25 seconds just mentioned so that we can determine the remaining time.

Consider the following line of code:

 ftime = System.GetElapsedTime; 

In this line, System.GetElapsedTime will give the number of milliseconds (thousands of a second) that have elapsed since the computer was turned on. If System.GetElapsedTime were to have a value of 35000, then 35 seconds would have passed since the computer was turned on (just move the decimal point 3 positions to the left, starting from the far right of the number of milliseconds, to get the number of seconds).

When it is reached in the execution of the program, this code line sets ftime equal to the number of milliseconds elapsed since the computer was turned on.

We now have a way of knowing the time when one of the thrusters is activated. We add this line of code to our doahead function, which, as you may recall, moves the sphere forward. When the function is called, ftime will be set to the number of milliseconds since the computer was turned on. This process is shown here:

 Function doahead {   ftime = System.GetElapsedTime;   mysphere.Move(5000,50);   fuelflag = 1 } 

The last line of the function is a flag activated when the timer starts. At the beginning of the program, fuelflag will be initialized to zero, and once the Ahead button is clicked, fuelflag will be set to a value of one.

If we know at what time the thruster was turned on ( ftime seconds after the computer was turned on), and if we can find out at what time the thruster was turned off, then we just have to subtract one from the other to know how long the thruster was on.

Consider the following lines of code which will be contained in our game loop:

 if(fuelflag == 1) {   time2 = System.GetElapsedTime;   fueltime = (time2 - ftime)/1000.;   fuelleft = fuel - fueltime; } 

This piece of code is performed only if the value of fuelflag is 1. The value of time2 is set to the number of milliseconds since the computer was turned on, which will be just a little more than the value of ftime given in the earlier code. By calculating the difference between the current time and the previous time, we know how much time has elapsed. The elapsed time is fueltime , and it s divided by 1000 in order to convert the milliseconds to seconds. The parentheses are applied to (time2 - ftime) to ensure that the subtraction is done first, followed by the division.

To calculate the remaining time (which is what we are interested in displaying on our game s instrument panel), we simply subtract the elapsed time fueltime from fuel ( fuel will be initialized to a value of 25 at the beginning of the program).

There s still some unfinished business: the timer should stop when the thrusters are turned off. Consider the function dostop , which will be called when the Stop button is clicked on the user s screen:

 Function dostop {   mysphere.Stop();   fuelflag = 0;   fuel = fuelleft; } 

When this function is called, two things happen: the sphere stops, and the value of fuelflag is set to zero. If you go back a few steps to the previous piece of code, you will see that the amount of fuel left is only changed if fuelflag is set to one. By setting fuelflag to zero we are instructing the computer to cease subtracting time from the initial 25 seconds ( fuel ).

We must now explain the mysterious -looking last line of the function. It is there to update fuel , which is the amount of fuel remaining. By setting fuel equal to the value of fuelleft, we are resetting fuel to the most current tally. Let s say that it s the first time you activate the thrusters, and that you do so for 5 seconds. The value of fueltime is then 5 seconds, and the value of fuelleft is then 25 “ 5 = 20 ( fuel “fueltime=fuelleft ). Everything seems OK so far.

The problem arises when you activate a thruster again, let s say for another 5 seconds. fueltime will again be 5 seconds, but fuel would still be 25 if it were not updated. fuelleft would then be 25 “ 5 = 20. There will then be 20 seconds of fuel left instead of the correct 15! The computer has forgotten to keep a running tab of the time. By telling the computer to change the value of fuel to the remaining fuel, fuelleft , we are updating the amount of fuel in the tank.

You may find it hard to follow the reasoning above. Sometimes it s easier to come up with your own algorithm (calculation scheme) than to follow what someone else has done! You are encouraged to do this, or you can just use this code for now and hope it clicks later.

Variable Initialization and Functions

Near the beginning of the Sphere7 program you will see the following lines:

 //Initialize variables. flag = 0; fuel = 25.; fuelleft = 25.; ftime = 0.0; fuelflag = 0; 

We have seen this type of thing before, but there s more to it than meets the eye. First of all, recall that we are using functions, and that the functions use values from the main program. Functions are strange in that after they are performed and control of the program is returned to the game loop, any value that is calculated by a function will not make it back to the game loop unless you specifically instruct the function to make sure that it does! In the same way, the function won t know values calculated by the main program unless you specifically send those values to the function.

If you want, you can think of functions as separate computers far away from you; this may make it easier to remember that they must be passed all the information they require in order to do their job.

There are several ways to pass information to and from a function; the easiest is to declare or initialize any values that may be passed to or from functions at the beginning of the main program. This lets the computer know that the initialized values will be global to the entire program, meaning that they will be known by the entire program. Please consult the language reference section of the Help file in Jamagic if you would like more detail on how to make this work.

Variable Types: Integers and Floating Point Numbers

Believe it or not, there s even more to the command lines discussed in the preceding section. If you look closely at this section of code, you will notice that some of the numbers have decimal points after them and some do not. For example, the variable flag has none, while the fuel variable does.

The reason for this is that flag will always be an integer value (that is, it will never have a decimal point and will always be a whole number like 0, 1, or 2, and it will never be a number like 2.11 or 3.6). By leaving out the decimal point, we are telling the computer that flag will be an integer.

In contrast the variable fuel will have noninteger numbers like 19.386 associated with it, so by initializing it with a decimal point, we are telling the computer that fuel will be a floating-point number. Floating-point just means that there will be numbers after the decimal point.

Why bother distinguishing between the two? Why not just make all numbers floating point? There are two reasons for this: speed and precision.

Calculations involving integers are faster than those involving floating point numbers. Speed is something you should be very concerned about when making a game, particularly when the number of polygons gets large, so there s no need to add floating point numbers if adding integers will do. The difference in time for one calculation is extremely small by our standards, but if performed thousands or millions of times, these time differences add up and could affect your game.

Also extremely important is the concept of precision. With floating point numbers, if you say x = 25.0, the actual number stored may be 25.0001 ”which means that the precision is not infinite. Later on, when you make a statement like if x = = 25.0 and you are expecting this condition to be met, you may be in for a surprise. This is because even though you may have set x to 25, the if statement may not catch it because the computer thinks x = 25.0001. By using integers, you can ensure that an if statement will know the exact value.

Most of the time you will need to use floating point numbers since you will need to keep track of decimal values.

So far, we ve covered a lot in this chapter. Let s type in the code that we have come up with so far and run the program. But first, you must import the sound called strong from the Pacman game in the Advanced Examples section, as well as the catch sound from the Rolling Ball 3D game.

Sphere7 Code

 // // Sphere7: 7th Spherelander game. This program will //(a) Insert a second window with a view from above. //(b) Add control with buttons. //(c) Keep track of fuel. //(d) Introduce timers. // New Main Window owindow = New Window("Spherelander",     640,480,Window.STANDARD);//one line // New Child Window owindow1 = New Window(owindow,"View From Orbiting    Satellite",200,150,Window.GAME);// one line owindow1.SetPosition(390,5); owindow1.Show(); owindow.Show(); owindow1.SetAutoRefresh(OFF); owindow.SetAutoRefresh(OFF); // New World oworld = New World(); //Initialize variables flag = 0; fuel = 25.; fuelleft = 25.; ftime = 0.0; fuelflag = 0; // New Cameras ocamera1 = New Camera(oworld,owindow); ocamera2 = New Camera(oworld,owindow1); ocamera1.SetPosition(500,500,5000); ocamera2.SetPosition(0,2000,0); ocamera2.TurnDown(Pi/2.0); // Instruments mytext6 = New StaticText(owindow,     "INSTRUMENTS",130,0,170,20);//one line mytext6.SetBackColor(GetRGB(0,0,0)); mytext6.SetColor(GetRGB(0,255,255)); fnt = New Font("ARIAL",20,Font.BOLD); mytext6.SetFont(fnt); mytext7 = New StaticText(owindow,"Remaining Fuel:",0,150,170,20);//one line mytext7.SetBackColor(GetRGB(0,0,0)); mytext7.SetColor(GetRGB(255,0,0)); mytext = New StaticText(owindow," ",0,30,110,20); mytext.SetBackColor(GetRGB(0,0,0)); mytext.SetColor(GetRGB(255,255,255)); mytext2 = New StaticText(owindow," ",0,60,110,20); mytext2.SetBackColor(GetRGB(0,0,0)); mytext2.SetColor(GetRGB(255,255,255)); mytext3 = New StaticText(owindow," ",0,90,110,20); mytext3.SetBackColor(GetRGB(0,0,0)); mytext3.SetColor(GetRGB(255,255,255)); mytext4 = New StaticText(owindow," ",0,120,140,20); mytext4.SetBackColor(GetRGB(0,0,0)); mytext4.SetColor(GetRGB(255,255,255)); mytext5 = New StaticText(owindow," ",170,130,220,20); mytext5.SetBackColor(GetRGB(0,0,0)); mytext5.SetColor(GetRGB(255,0,0)); //Buttons myButton=New Button(owindow,"Ahead",220,30,65,20); myButton.OnClick = Doahead; myButton2=New Button(owindow,"Off",220,60,65,20); myButton2.OnClick = Dostop; myButton3=New Button(owindow,"Back",220,90,65,20); myButton3.OnClick = Doback; myButton4=New Button(owindow,"Right",300,60,65,20); myButton4.OnClick = Doright; myButton5=New Button(owindow,"Left",135,60,65,20); myButton5.OnClick = Doleft; // Sounds osound = New Sound("catch"); osound.SetLoop(ON,1); osound2 = New Sound("strong"); // Create a sphere with Gouraud shading. mysphere = oworld.CreateSphere(100,100,100,20,20); mysphere.SetPosition(500,500,1000); omaterial = New Material     (oworld,GetRGB(50,150,200),"lightblue");//one line omaterial.SetFlat(ON); mysphere.SetGouraud(ON); mysphere.ReplaceMaterial(omaterial); // Create a plane and rotate it so it's flat. myplane = oworld.CreatePlane(100,100); myplane.SetPosition(0,0,0); matp =New Material(oworld,GetRGB(0,100,50)); matp.SetFlat(ON); myplane.ReplaceMaterial(matp); myplane.SetAngle(-Pi/2,0,0); myplane.SetStatic(); // Camera oworld.Optimize(ocamera1); oworld.Optimize(ocamera2); ocamera1.Follow(mysphere,3000); //Set collision detection. oworld.OnCollide = docollision; mysphere.SetCollision(TRUE,Object.COLLISION_TYPE_STOP); // Set gravity on. mysphere.SetGravity(0.002); //Game loop While(1) { // Distance calculations distx = (mysphere.GetX) ; mytext.SetText("X Distance: " + distx); disty = (mysphere.GetY) ; mytext2.SetText("Y Distance: " + disty); distz = (mysphere.GetZ) ; mytext3.SetText("Z Distance: " + distz); totaldist = Sqrt(distx*distx + disty*disty     + distz*distz);//one line mytext4.SetText("Total Distance: " + totaldist); if(disty <= 0) { flag = flag+1; mysphere.SetGravity(0.002); mysphere.Stop(); if(flag ==1) { osound.Play(); } mytext5.SetText("Game Over. You Failed to Land on the Plane!");//one line } // Refresh     ocamera1.ActivateRender();     owindow.Refresh();     ocamera2.ActivateRender();     owindow1.Refresh(); //Fuel calculations if(fuelflag == 1) {    time2 = System.GetElapsedTime;    fueltime = time2 - ftime;    fuelleft = fuel - fueltime/1000.; } mytext7.SetText("Remaining Fuel Time: " + fuelleft); //End of game loop follows. } Function docollision {       flag = flag + 1;      if(flag==1)       {          osound2.Play();       }    mytext5.SetText("Congratulations! You landed on    target!");// on one line } Function doahead {     ftime = System.GetElapsedTime;    mysphere.Move(5000,50);    fuelflag = 1; } Function doback {    mysphere.Move(5000,50);    ftime = System.GetElapsedTime;    fuelflag = 1; } Function doright {    mysphere.MoveRight(5000,50);    ftime = System.GetElapsedTime;    fuelflag = 1; } Function doleft {    mysphere.MoveLeft(5000,50);    ftime = System.GetElapsedTime;    fuelflag = 1; } Function dostop {    mysphere.Stop();    fuelflag = 0;    fuel = fuelleft; } 

When you run the program, you will notice some very nice things: the sphere eventually comes into view in the child window, the buttons now control the fall of the sphere, and the remaining fuel time is displayed along with the other information in the instrument panel.

Elementary Game Programming and Simulators Using Jamagic
Elementary Game Programming & Simulations Using Jamagic (Charles River Media Game Development)
ISBN: 1584502614
EAN: 2147483647
Year: 2002
Pages: 105
Authors: Sergio Perez

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net