October 19, 2018
Hot Topics:

# Our First 3D Game Program: Math for Java Game Programmers

Java Programming Notes # 1712

## Preface

### General

Next in a series

This tutorial is the next lesson in a series designed to teach you some of the mathematical skills that you will need (in addition to good programming skills) to become a successful game programmer. The first lesson was titled Math for Java Game Programmers, Getting Started. The previous lesson was titled Math for Java Game Programmers, Venturing into a 3D World (see Resources).

In addition to helping you with your math skills, I will also help you learn how to incorporate those skills into object-oriented programming using Java. If you are familiar with other object-oriented programming languages such as C#, you should have no difficulty porting this material from Java to those other programming languages.

Lots of graphics

Since most computer games make heavy use of either 2D or 3D graphics, you will need skills in the mathematical areas that are required for success in 2D and 3D graphics programming. As a minimum, this includes but is not limited to skills in:

• Geometry
• Trigonometry
• Vectors
• Matrices
• 2D and 3D transforms
• Transformations between coordinate systems
• Projections

Of course, game programming requires mathematical skills beyond those required for graphics. However, for starters, this series will concentrate on the last five items in the above list.

What you have learned

In the previous lesson, you learned how to update the game-math library to support 3D math, how to program the equations for projecting a 3D world onto a 2D plane, how to add vectors in 3D, about scaling, translation, and rotation of a point in both 2D and 3D, about the rotation equations and how to implement them in both 2D and 3D, and much more.

What you will learn

In this lesson, you will learn how to write your first interactive 3D game using the game-math library named GM01. You will also learn how to write a Java program that simulates flocking behavior such as that exhibited by birds and fish and how to incorporate that behavior into a game. Finally, you will examine three other programs that illustrate various aspects of both 2D and 3D animation using the game-math library.

### Viewing tip

I recommend that you open another copy of this document in a separate browser window and use the following links to easily find and view the figures and listings while you are reading about them.

#### Figures

• Figure 1. A school of red prey fish being attacked by a blue predator.
• Figure 2. Screen shot of the graphic output from GM01test04.
• Figure 3. Screen shot of the graphic output from GM01test03.
• Figure 4. Screen shot of the graphic output from StringArt04.
• Figure 5. Prey fish in a large swirling cluster.
• Figure 6. Formation starting to break up due to closeness of the predator.
• Figure 7. 100 prey fish trying to occupy the same location in 3D space.
• Figure 8. 100 prey fish maintaining a reasonable separation in 3D space.
• Figure 9. Twelve predators swimming in a hexagon formation in GM01test04.
• Figure 10. Six predators swimming in a diamond formation.

#### Listings

• Listing 1. Abbreviated constructor for the GUI class in GM01test08
• Listing 2. Beginning of the actionPerformed method in GM01test08.
• Listing 3. Code that services the Attack button.
• Listing 4. Code that services the Stop button.
• Listing 5. Beginning of the Animate class and the run method.
• Listing 6. Create the population of prey fish.
• Listing 7. Create and position the predator object.
• Listing 8. A few more housekeeping details.
• Listing 9. Beginning of the animation loop.
• Listing 10. Cause all of the prey fish to spiral toward the center.
• Listing 11. Create a rotated vector.
• Listing 12. Move the prey fish object based on the sum of the vectors.
• Listing 13. Save a clone of the relocated prey fish object.
• Listing 14. Save a normalized direction vector.
• Listing 15. Code for when the population contains only one prey fish.
• Listing 16. Separate the prey fish.
• Listing 17. The remainder of the inner loop.
• Listing 18. Save the primary prey fish object and do another iteration.
• Listing 19. Prey fish objects react to the predator.
• Listing 20. Prey fish object takes evasive action.
• Listing 21. Test the separation again.
• Listing 22. Restore the prey fish to the population.
• Listing 23. Keep the prey fish in the playing field.
• Listing 24. Erase the off-screen image and draw the circle.
• Listing 25. Draw the prey fish on the off-screen image.
• Listing 26. Cause the predator to slowly circle the cluster of prey fish.
• Listing 27. Execute the attack.
• Listing 28. Draw the predator on the off-screen image.
• Listing 29. Copy off-screen image, insert time delay, etc.
• Listing 30. Source code for the game-math library named GM01.
• Listing 31. Source code for the game program named GM01test08.
• Listing 32. Source code for the program named GM01test04.
• Listing 33. Source code for the program named GM01test03.
• Listing 34. Source code for the program named StringArt04.

### Supplementary material

I recommend that you also study the other lessons in my extensive collection of online Java tutorials. You will find a consolidated index at www.DickBaldwin.com.

## General background information

In this series, I will frequently refer you to an excellent interactive tutorial on Vector Math for 3D Computer Graphics by Dr. Bradley P. Kjell (see Resources) for the required technical background. Then I will help you learn how to incorporate the knowledge that you gain from Kjell's tutorial into Java code with a heavy emphasis on object-oriented programming.

In the process, I will develop and explain a game-math library that you can use to experiment with and to confirm what you learn from the Kjell tutorial. The library will start out small and grow as we progress through more and more material in subsequent lessons.

For this lesson, I recommend that you concurrently study the Kjell tutorial through Chapter6 - Scaling and Unit Vectors.

## Preview

You've been working hard if you've been studying these tutorials from the very beginning. It's time to kick back and have a little fun with what you have learned. In this lesson, I will present four programs, (one of which is an interactive 3D game), that use the current capabilities of the game-math library to produce some fairly serious animation. The programs are named:

• GM01test08
• GM01test04
• GM01test03
• StringArt04

I will explain the game program in detail and will provide a brief description of the other three programs.

GM01test08

The first three programs are based on an artificial life (see Resources) concept frequently referred to as boids (see Resources), which is "a computer model of coordinated animal motion such as bird flocks and fish schools."

 Flocking fish I got the idea for this game while watching a segment of a documentary series named Blue Earth on one of the cable channels. I even surprised myself as to how well I was able to simulate some of the scenes in that TV program using programming concepts similar to those used by Reynolds in boids.

A flocking game program

Although the flocking algorithms won't be the same as those used in the original boids program written by Reynolds, the general concepts will be similar.

The first program named GM01test08 is an interactive 3D game program based on the flocking behavior of fish. This game simulates a school of prey fish being attacked and eaten by a predator. The objective for the player is to cause the predator to catch and eat all of the prey fish in a minimum amount of time.

A preview

I will get into a lot more detail about the game aspects of the program later. For a preview, Figure 1 shows the instant in time when a school of 500 prey fish (shown in red) have just been attacked by the blue predator shown near the center right.

Figure 1. A school of red prey fish being attacked by a blue predator.

The predator in Figure 1 has penetrated the school of fish by swimming from left to right, eating fish all along the way, and has emerged from the right side of the school. The prey fish, which were originally in a small tight cluster, have reacted to the presence of the predator by scattering in an attempt to avoid being eaten.

Fully three-dimensional

This game program is fully three-dimensional. Any or all of the fish are capable of swimming in any direction at any time in the 3D world. This is evidenced by the prey fish near the center that appear to have very short or non-existent tails. The fish that appear to have no tails actually have tails that are the same length as the tails on all of the other fish. These fish appear to have no tails because they are swimming either directly toward or directly away from the viewer. I will have more to say about this later.

GM01test04 and GM01test03

These two programs provide essentially the same behavior (as one another), except that one is a 2D program and the other is a 3D program. They are not game programs. Rather, they are demonstration programs that show another aspect of flocking behavior. In these two programs, a large number of predators pursue a single prey object.

A screen shot of the animated output from the 2D version is shown in Figure 2 and a screen shot of the output from the 3D version is shown in Figure 3.

Figure 2. Screen shot of the graphic output from GM01test04.

Figure 3. Screen shot of the graphic output from GM01test03.

I included these two programs in this lesson primarily to provide a good comparison between 2D and 3D, both from a programming viewpoint and a behavior viewpoint.

Eight predator objects and one prey object

In both versions shown in Figure 2 and Figure 3, you see eight predator objects (colored black) pursuing a red prey object. The actual length of every predator object is the same as the length of every other predator object and the length of the prey object is the same as the length of the predator objects.

Some significant differences

At first glance, the two versions may look the same. However, they are significantly different. In Figure 2, the prey and the predators are constrained to move in a single plane. Hence, the apparent length of each predator is the same as the length of every other predator and is also the same as the length of the prey regardless of the direction in which the object is moving.

However, in Figure 3, the predators and the prey are free to move in any direction in a 3D world. As a result, at any point in time, the objects may be moving parallel to or at an angle to the x-y plane. When the 3D world is projected onto the viewing plane, those objects that are moving at an angle to the viewing plane will be foreshortened. In the extreme case, (as shown earlier in Figure 1), when an object is moving directly toward or directly away from the viewer, its apparent length will go to zero. (At least one predator is almost in that state in Figure 3.)

In Figure 3 the apparent lengths of the predator and prey objects are all different indicating that they are all moving in slightly different directions in the 3D world.

The flocking algorithms in these two programs are much simpler than the algorithm used in Figure 1. I encourage you to experiment with the flocking algorithms for these two programs to see what you can produce that is new and different from my version of the two programs.

StringArt04

The fourth program named StringArt04 is completely different from the other three programs. In a previous lesson, I presented and explained a program that rotates a disk around an arbitrary anchor point in 3D space with the ability to rotate by different angles around each of the three axes. It can be very difficult to visualize exactly what is happening with such complex rotations. This program animates the rotation process and runs it in slow motion so that you can see exactly what is happening as the disk moves and rotates from its initial position and orientation to its final position and orientation.

A stop-action screen shot

Figure 4 shows a screen shot of the disk in motion somewhere between the beginning and the end of its very complex trajectory for one particular set of rotation and anchor point parameters.

Figure 4. Screen shot of the graphic output from StringArt04.

## Discussion and sample code

 Testing All of the programs in this lesson were tested using JDK 1.6 running under Windows XP.

### The game-math library named GM01

The game-math library named GM01 has not been modified since I presented and explained it in an earlier lesson. A copy of the source code for the library is provided in Listing 30 for your convenience. However, since I have explained the code in the library in earlier lessons, I won't repeat those explanations here.

### The game program named GM01test08

This is an interactive 3D game program. In fact, this is the first game program that I have presented in this series on game math. This game simulates a school of prey fish being attacked and eaten by a predator. The objective for the player is to cause the predator to eat all of the prey fish in a minimum amount of time.

Player initiates attacks

The player in this game initiates a series of attacks by the predator by clicking the Attack button shown in Figure 5. The winning strategy for the player is to time the attacks in such a way as to minimize the elapsed time required for the predator to eat all of the prey fish. The final elapsed time depends on both strategy and chance. In other words, as is the case in many games, this game is part strategy and part chance.

A swirling cluster of prey fish

The prey fish, (shown in red), tend to swim in a large swirling cluster as shown in Figure 5 when they aren't being harassed by the predator.

Figure 5. Prey fish in a large swirling cluster.

Predator behavior

The predator, (shown in blue in Figure 5), swims around the cluster, darting into and through the cluster when the user clicks the Attack button. During an attack, the predator attempts to eat as many prey fish as possible. When the predator attacks, the prey fish tend to scatter (as shown in Figure 1), breaking up their tight formation and making it more difficult for the predator to catch them. If the predator is allowed to swim around them again for a short period following an attack, they will form back into a cluster.

Proper timing is important

The attacks should be timed so that the prey fish are in a reasonably tight cluster at the beginning of each attack so that multiple prey fish will be eaten during the attack. However, allowing too much time between attacks is detrimental because it increases the total elapsed time. Thus, the player must make a tradeoff between elapsed time between attacks and the tightness of the cluster at the beginning of each attack. Another disadvantage of waiting too long between attacks will be explained later.

The graphical user interface

In addition to some other controls, the GUI provides an Attack button and an elapsed-time/kill-count indicator, shown in the bottom right in Figure 5. At any point in time, this indicator displays the elapsed time in milliseconds when the most recent prey fish was eaten along with the total number of prey fish that have been eaten. The two values are separated by a "/" character.

When all of the prey fish have been eaten by the predator, the elapsed time indicator shows the time in milliseconds required for the predator to catch and eat all of the prey fish. It also shows the number of prey fish that were eaten, which should match the value that the player entered into the upper-right text field before starting the game. (This field contains 500 in Figure 1 and 200 in Figure 5).

Not initially in attack mode

When the player clicks the Start button and the game begins, the predator is not in attack mode. Rather, the predator swims around the school of prey fish encouraging them to bunch up into a smaller and tighter cluster as shown in Figure 5. The purpose of this behavior is to increase the number of fish that will be eaten when an attack is made.

The attack will be more or less successful

This circling behavior on the part of the predator continues until the player clicks the Attack button, at which time the predator enters the attack mode and makes an attack on the cluster as shown in Figure 1. If the player clicks the Attack button too early, or doesn't wait long enough between attacks, the prey fish will be in a loose cluster and the predator will eat very few fish during the attack.

The predator always has an impact

Even when the predator is not in attack mode, its presence has a significant effect on the behavior of the school of prey fish. As the predator swims around the prey fish, they tend to bunch up into a smaller and tighter cluster, but when the predator swims too close, the prey fish tend to panic and scatter, breaking up the tight cluster as shown in Figure 6.

Therefore, if the player waits too long to click the Attack button or waits too long between attacks, the predator will have spiraled in so close to the prey fish that they will break formation and begin to scatter, making it difficult for the predator to eat a large number of fish during the next attack. This is the other disadvantage of waiting too long to attack that I mentioned earlier.

Formation starting to break up due to closeness of the predator

Figure 6 shows the formation starting to break up because the predator has strayed too close to the cluster of prey fish.  (The fish in the upper right of the formation have pulled out of the cluster and have started to flee the predator.)

Figure 6. Formation starting to break up due to closeness of the predator.

An effective defense mechanism

 Cleaning up the leftovers The final three or four prey fish are often the most difficult to catch because they have more room to run without colliding with another fish.

The prey fish have a fairly effective defense mechanism and can do a reasonably good job of escaping the predator. The predator can increase the odds of catching the prey fish by attacking very fast. (You can modify the code that controls the speed of the attack.) If the predator goes into the formation slowly, the prey fish will simply run away as is starting to be the case in Figure 6.

Captured fish are removed from the population

When the predator is successful in eating a prey fish, that fish is removed from the prey-fish population causing the prey-fish population to decrease over time. Also, each time the predator eats a prey fish, the program emits an audible beep to provide feedback to the player.

The other GUI components

In addition to the elapsed time indicator and the Attack button, the GUI contains an input text field by which the player can specify the number of prey fish that will be in the population when the game begins. The GUI also contains a Start button and a Stop button. Finally, the GUI contains check boxes that allow the player to display points only, direction vectors only, or both for the prey fish. (Only the direction vector is displayed for the predator.)

Playing the game

In practice, the player specifies the number of randomly-placed prey fish that will be in the population and clicks the Start button to start the game. At this point, the prey fish swim in a large swirling cluster and the predator swims around them encouraging them to form a tighter cluster as shown in Figure 5.

Prey fish motion is random but each prey fish generally tends to spiral toward the center of the cluster. When the user clicks the Attack button, the predator turns and swims rapidly into and through the center of the cluster of prey fish. If the predator manages to get to within a specified distance from a prey fish, that prey fish will be deemed to have been eaten, and will be removed from the population. However, each prey fish will sense that the predator is coming and will try to escape. Whether or not an individual prey fish manages to escape the predator when an encounter between the two occurs is based on a random number generator.

The Start and Stop buttons

The animation continues until all of the fish have been eaten or the user clicks the Stop button. The user can click the Stop button at any time, change any of the parameters, and then click the Start button again to re-start the game with zero elapsed time and different parameters.

Displaying the vectors

The animation is most impressive when the direction vectors are displayed for the prey fish because the vectors provide a lot of visual information about how the prey fish are reacting to the predator.

A spherical playing-field boundary

In addition to the school of prey fish and the predator, the graphical output also shows a large circle drawn with broken lines, as shown in Figure 5. This circle represents the intersection of a sphere and the x-y plane.

The purpose of the sphere, which is centered on the origin, is to provide a soft boundary for keeping the prey fish and the predator inside the 3D playing field. The prey fish and the predator all have a tendency to remain inside the sphere, but they may occasionally stray outside the sphere. If they do, the program code will encourage them to return to the 3D playing field inside the sphere.

A 3D display

As mentioned earlier, this game is fully three dimensional. The prey fish and the predator are free to swim in any direction in 3D space. The tails on the prey fish and the predator appear longest when they are swimming parallel to the x-y plane. As they change their angle relative to the x-y plane, the tails appear to become shorter or longer and in some cases, they appear not to have a tail at all. The fish that appear to have no tails are either swimming directly toward the viewer or swimming directly away from the viewer.

This can best be observed by clicking the Stop button just as the fish scatter during an attack and freezing the state of the fish in the 3D world as shown in Figure 1. If you examine the image at that point, you are likely to see some fish with shorter tails than other fish. This is evident by some of the prey fish near the center of Figure 1 that appear to have no tails at all.

Enough talk, let's see some code

A complete listing of this program is provided in Listing 31 near the end of the lesson.

Portions of this program are very similar to programs that I have explained in earlier lessons in this series. I won't repeat those explanations here. Rather, I will concentrate mostly on the code that is new and different in this lesson.

Abbreviated constructor for the GUI class

Listing 1 shows an abbreviated constructor for the GUI class. (Much of the code in the constructor has been explained before, and was deleted from Listing 1 for brevity.)

Listing 1. Abbreviated constructor for the GUI class in GM01test08.

 GUI(){//constructor //Code deleted for brevity //Register this object as an action listener on all // three buttons. startButton.addActionListener(this); attackButton.addActionListener(this); stopButton.addActionListener(this); //Make the drawing color RED at startup. g2D.setColor(Color.RED); }//end constructor

If you examine Listing 31, you will see that the GUI class implements the ActionListener interface. Therefore, an object of the GUI class can be registered as an action listener on a button. Listing 1 registers the object of the GUI class as a listener on all three of the buttons shown in Figure 6.

Beginning of the actionPerformed method

The actionPerformed method that begins in Listing 2 is called whenever the player clicks any of the three buttons shown in Figure 6.

Listing 2. Beginning of the actionPerformed method in GM01test08.

 public void actionPerformed(ActionEvent e){ if(e.getActionCommand().equals("Start") && !animate){ //Service the Start button. //Get several user input values. numberPoints = Integer.parseInt( numberPointsField.getText()); if(drawPointsBox.getState()){ drawPoints = true; }else{ drawPoints = false; }//end else if(drawVectorsBox.getState()){ drawVectors = true; }else{ drawVectors = false; }//end else //Initialize some working variables used in the // animation process. animate = true; baseTime = new Date().getTime(); killCount = 0; //Enable the Attack button. attackButton.setEnabled(true); //Initialize the text in the timer field. timer.setText("0 / 0"); //Cause the run method belonging to the animation // thread object to be executed. new Animate().start(); }//end if

Servicing the Start button

The code in Listing 2 is executed when the user clicks the Start button. All of this code is also very similar to code that I have explained in previous lessons, so further explanation beyond the embedded comments should not be necessary. However, I will emphasize the two statements shown in boldface.

The first boldface statement enables the Attack button. The second boldface statement instantiates a new object of the Thread class named Animate and causes the run method of the animation thread to be executed. I will explain the animation thread later.

Code that services the Attack button

The code in Listing 3 is executed when the player clicks the Attack button after it is enabled by the code in Listing 2.

Listing 3. Code that services the Attack button.

 if(e.getActionCommand().equals("Attack") && animate){ attack = true; predatorVec = predator.getDisplacementVector(preyCenter). scale(1.0); //Disable the Attack button. It will be enabled // again when the attack is finished. attackButton.setEnabled(false); }//end if

Listing 3 begins by setting the value of the variable named attack to true. As you will see later, this causes the predator to attack the prey fish in the animation loop.

Control the speed and direction of the predator attack

Then Listing 3 calls the getDisplacementVector method and the scale method of the game-math library to set the value of the variable named predatorVec to a direction that will be used to point the predator toward the center of the prey-fish cluster. In Listing 3, the scale factor is unity, so the application of the scale factor does nothing. However, you can modify this value to control the speed of the predator during the upcoming attack. If you increase the scale factor, the predator will move faster. If you decrease the scale factor, the predator will move more slowly.

Temporarily disable the Attack button

Finally, Listing 3 temporarily disables the Attack button so that it will have no effect if the player clicks it again during the attack. You could also remove this statement, slow down the attack, and experiment with multiple clicks on the Attack button during the course of an attack to see what happens.

Code that responds to the Stop button

The code in Listing 4 is executed when the player clicks the Stop button.

Listing 4. Code that services the Stop button.

 if(e.getActionCommand().equals("Stop") && animate){ //This will cause the run method to terminate and // stop the animation. It will also clear the // attack flag so that the next time the animation // is started, the predator won't be in the // attack mode. animate = false; attack = false; }//end if }//end actionPerformed

No explanation beyond the embedded comment should be required for this code.

Beginning of the Animate class and the run method

When the Start button is clicked, the code in Listing 2 instantiates a new object of the Thread class named Animate and causes the run method belonging to that object to be executed. Listing 5 shows the beginning of the Animate class and its run method.

Listing 5. Beginning of the Animate class and the run method.

 class Animate extends Thread{ //Declare a general purpose variable of type Point3D. // It will be used for a variety of purposes. GM01.Point3D tempPrey; //Declare two general purpose variables of type // Vector3D. They will be used for a variety of // purposes. GM01.Vector3D tempVectorA; GM01.Vector3D tempVectorB; //--------------------------------------------------// public void run(){ //This method is executed when start is called on // this Thread object. //Create a new empty container for the prey objects. //Note the use of "Generics" syntax. preyObjects = new ArrayList(); //Create a new empty container for the vectors. The // only reason the vectors are saved is so that they // can be displayed later displayVectors = new ArrayList();

Importance of the game-math library named GM01

The code in Listing 5 is straightforward and shouldn't require an explanation beyond the embedded comments. However, I will emphasize the heavy use of the game-math library classes named GM01.Point3D and GM01.Vector3D in the code in Listing 5.

This entire game program is heavily dependent on the use of the ColMatrix3D, Point3D, Vector3D, and Line3D classes along with some of the static methods in the game-math library named GM01. If you were to start from scratch and write this game program without the availability of the game-math library, the program would be much longer and would be much more complex.

Create the population of prey fish

Listing 6 creates the population of prey fish and positions them at random locations in 3D space. Listing 6 also stores references to the prey fish objects in the preyObjects container, which was created in Listing 5.

Listing 6. Create the population of prey fish.

 for(int cnt = 0;cnt < numberPoints;cnt++){ preyObjects.add(new GM01.Point3D( new GM01.ColMatrix3D( 100*(random.nextDouble()-0.5), 100*(random.nextDouble()-0.5), 100*(random.nextDouble()-0.5)))); //Populate the displayVectors collection with // dummy vectors. displayVectors.add(tempVectorA); }//end for loop

In addition, Listing 6 populates a container named displayVectors that was also created in Listing 5 to store a direction vector belonging to each prey fish. This container is populated with dummy vectors in Listing 6, simply to set the size of the container to be the same as the size of the container containing references to the prey fish objects. (There is probably a more efficient way to set the size of that container.)

Note that each prey fish object is actually represented by an object of the game-math library class named GM01.Point3D.

Create and position the predator object

Listing 7 creates an object of the GM01-Point3D class that will represent the predator in the game.

Listing 7. Create and position the predator object.

 predator = new GM01.Point3D( new GM01.ColMatrix3D(-100,100,-100));

The initial position given to the predator in Listing 7 causes it to appear in the game near the top center of the screen when the user clicks the Start button. You could also make the initial position of the predator random if you think that would improve the game.

A few more housekeeping details

Listing 8 takes care of a few more housekeeping details before the animation actually begins.

Listing 8. A few more housekeeping details.

 //Create a reference point to mark the origin. Among // other things, it will be used as the center of a // sphere, which in turn will be used to attempt to // keep the prey and predator objects from leaving // the playing field. GM01.Point3D origin = new GM01.Point3D(new GM01.ColMatrix3D(0,0,0)); //Declare some variables that will be used to // compute and save the average position of all of // the prey objects. double xSum = 0; double ySum = 0; double zSum = 0;

Let the show begin

Listing 9 shows the beginning of the animation loop, which will continue to execute for as long as the value of the variable named animate is true.

Listing 9. Beginning of the animation loop.

 while(animate){ //Compute and save the average position of all the // prey objects at the beginning of the loop. Save // the average position in the Point3D object // referred to by preyCenter. xSum = 0; ySum = 0; zSum = 0; for(int cnt = 0;cnt < preyObjects.size();cnt++){ tempPrey = preyObjects.get(cnt); xSum += tempPrey.getData(0); ySum += tempPrey.getData(1); zSum += tempPrey.getData(2); }//end for loop //Construct a reference point at the average // position of all the prey objects. preyCenter = new GM01.Point3D( new GM01.ColMatrix3D(xSum/preyObjects.size(), ySum/preyObjects.size(), zSum/preyObjects.size()));

The value of animate is set to true by clicking the Start button and is set to false by clicking the Stop button. Setting the value to false causes the run method to terminate, thereby stopping the animation.

Attack toward the geometric center

When the predator attacks, it will move to and through the geometric center of the cluster of prey fish, eating prey fish all along the way. The geometric center of the cluster of prey fish is computed and saved by the code in Listing 9 as the average location of all the prey fish in the population.

Note that a vector that points to the geometric center may or may not be a good indicator of the best direction for eating large numbers of prey fish during an attack. In Figure 1, for example, a predator attacking from the 11:00 o'clock position and heading toward the center would encounter quite a few prey fish. However, a predator attacking from the 8:00 o'clock position and heading toward the center would encounter far fewer prey fish.

You will also learn that although the predator will always attack in a direction pointing toward this geometric center, the positions of the prey fish can change after the center is computed and before the attack begins, causing the position of the actual geometric center to change, before the predator has an opportunity to attack. That can decrease the probability of a successful attack by the predator.

These issues form part of the strategy of the game that must be mastered by the player in order to earn a good score.

Cause all of the prey fish to spiral toward the center

Listing 10 shows the beginning of a fairly complicated algorithm that causes all of the prey fish to have a tendency to spiral toward the center of the cluster of prey fish.

Listing 10. Cause all of the prey fish to spiral toward the center.

 for(int cnt = 0;cnt < preyObjects.size();cnt++){ if(preyObjects.size() > 1){ //Get the next prey object tempPrey = preyObjects.get(cnt); //Find the displacement vector from this prey // object to the preyCenter tempVectorA = tempPrey.getDisplacementVector( preyCenter);

The code in the body of the for loop that begins in Listing 10 is executed once during each animation cycle for each of the prey fish in the population of prey fish.

To begin with, the if statement prevents the tendency to spiral toward the geometric center from being applied when there is only one prey fish remaining in the population. I will leave it as an exercise for the student to think about this and to decide whether or not this is a good decision.

The code in Listing 10 gets a reference to the next prey fish object in the population and then gets and saves a reference to a displacement vector pointing from the prey fish to the geometric center.

Create a rotated vector.

Listing 11 creates, scales, and saves a new GM01.Vector3D object having the same component values as the displacement vector from Listing 11, but assigning those values to different axes.

Listing 11. Create a rotated vector.

 tempVectorB = new GM01.Vector3D( new GM01.ColMatrix3D( tempVectorA.getData(1), tempVectorA.getData(2), tempVectorA.getData(0))).scale(1.2);

Once again, I will leave it as an exercise for the student to think about the length and direction of such a vector in relation to the original displacement vector. It might help to begin by drawing some vectors in 2D that swap components and then extending the same thinking to 3D. One thing is for certain, unless all of the components have the same value, the new vector created in Listing 11 has a different length and direction than the displacement vector created in Listing 10, and that would be true even if the vector from Listing 11 had not been scaled.

Move the prey fish object based on the sum of the vectors

Listing 12 begins by adding the two vectors that were created in Listing 10 and Listing 11, scaling the vector from Listing 10 before performing the addition. Note that the scale factors applied to the rotated vector in Listing 11 is different from the scale factor applied to the original displacement vector in Listing 12. The ratio of these two scale factors influences the tendency of the prey fish object to move toward the center relative to its tendency to move around the center.

Listing 12. Move the prey fish object based on the sum of the vectors.

Then Listing 12 calls the GM01.Point3D.addVectorToPoint method to relocate the prey fish object to a new position based on the scaled sum of the two vectors. The bottom line is that this will cause the prey fish object to spiral toward the geometric center that was computed in Listing 9 as the animation progresses.

Another scale factor is applied to the sum vector before using it to relocate the prey fish object. This scale factor controls the overall speed of the prey fish. Increasing the scale factor causes the prey fish to spiral faster. (Increasing the scale factor also causes some interesting patterns to appear and if you increase it too much, the prey fish will all leave the playing field.)

Save a clone of the relocated prey fish object

Listing 13 creates a clone of the relocated prey fish object and uses it to replace the original prey fish object in the container.

Listing 13. Save a clone of the relocated prey fish object.

 preyObjects.set(cnt,tempPrey.clone());

Creating and saving a reference to a clone instead of saving a reference to the relocated prey fish object may be overkill. However, I simply wanted to guard against the possibility of ending up with a corrupted object later due to the repeated use of the reference variable. I will leave it up to the student to think about this and to decide if this was a good or a bad decision.

Save a normalized direction vector

Listing 14 calls the GM01.Vector3D.normalize method to create a vector having a length of 15 units and the same direction as the vector that was used to relocate the prey fish object.

Listing 14. Save a normalized direction vector.

 displayVectors.set( cnt,tempVectorA.normalize().scale(15.0));

This vector is saved and used for drawing later. Drawing this vector in conjunction with the point that represents the prey fish object produces the tails shown on the prey fish objects in Figure 1.

Code for when the population contains only one prey fish

Listing 15 shows the else clause that matches up with the if statement in Listing 10. This code is executed when the population has been reduced to only one prey fish object.

Listing 15. Code for when the population contains only one prey fish.

 }else{ displayVectors.set( cnt,new GM01.Vector3D( new GM01.ColMatrix3D(10,10,0))); }//end else }//end loop to spiral prey objects toward center.

Listing 15 saves a dummy direction vector for drawing later. This is necessary to prevent a null pointer exception when the user specifies only one prey fish object and then clicks the Start button.

Not the end of the story

Listing 15 also signals the end of the for loop that causes the prey fish to spiral toward the center. The code in this for loop is sometimes referred to as a cohesion algorithm in flocking terminology. It causes the prey fish to stay together as a group.

This is not the end of the story, however. If the code in this cohesion algorithm were the only code controlling the behavior of the prey fish in this animation, they would simply continue spiraling toward the center, eventually producing a cluster that looks something like that shown in Figure 7 where 100 prey fish are trying to occupy the same location in 3D space.

Figure 7. 100 prey fish trying to occupy the same location in 3D space.

Interesting but also boring

While it may be interesting to watch the animation progress to this point, the animation becomes very boring when all of the prey fish cluster at the center. What we need is an additional algorithm that will cause each prey fish to attempt to maintain a respectable distance between itself and the other prey fish.

Prey fish maintaining a reasonable separation

For example, Figure 8 shows the result of temporarily making each prey fish immune to the presence of the predator, telling each prey fish to spiral toward the center, and also telling each prey fish to maintain a separation of ten units (pixels) between itself and all of the other prey fish.

Figure 8. 100 prey fish maintaining a reasonable separation in 3D space.

A 3D world

Remember, Figure 8 is a 3D world projected onto on a 2D screen display. Even if every prey fish is separated from every other prey fish by at least ten pixels (which is probably not the case as I will explain later), the projection of the 3D world onto the 2D display can make it appear that two or more prey fish occupy the same location.

Separate the prey fish

Listing 16 shows the beginning of a pair of nested for loops that attempt to keep the prey fish from colliding with one another by moving each prey fish object away from its close neighbors if necessary. In flocking terminology, this is often referred to as a separation algorithm.

Listing 16. Separate the prey fish.

 GM01.Point3D refPrey = null; GM01.Point3D testPrey= null; for(int row = 0;row < preyObjects.size();row++){ refPrey = preyObjects.get(row); //Compare the position of the reference prey // object with the positions of each of the // other prey objects. for(int col = 0;col < preyObjects.size();col++){ //Get another prey object for proximity test. testPrey = preyObjects.get(col);

This algorithm gets a reference to each prey fish object (primary object) and compares its position with the positions of all the other prey fish objects (secondary objects). If the primary object is too close to a secondary object, the primary object is moved away from the secondary object.

Not a perfect algorithm

This is not a perfect algorithm however. A primary object can be moved away from all of its neighbors early in the execution of the algorithm, but a neighbor could be moved closer to the primary object later in the execution of the algorithm. While not perfect, the algorithm does a pretty respectable job of keeping the prey fish separated as evidenced by comparing the positions of the prey fish in Figure 7 with the positions of the prey fish in Figure 8. This separation algorithm was disabled in Figure 7 and was enabled in Figure 8.

Gets two objects that will be compared

Listing 16 gets a reference to a primary prey fish object in the outer loop that iterates on the counter named row and gets a reference to one of the secondary prey fish objects to which it will be compared in the inner loop that iterates on the counter named col.

The remainder of the inner loop

Listing 17 shows the remainder of the inner loop.

Listing 17. The remainder of the inner loop.

 //Don't test a prey object against itself. if(col != row){ //Get the vector from the refPrey object to // the testPrey object. tempVectorA = refPrey. getDisplacementVector(testPrey); //If refPrey is too close to testPrey, move // it away from the testPrey object. if(tempVectorA.getLength() < 10){ //Move refPrey away from testPrey by a // small amount in the opposite direction. refPrey = refPrey.addVectorToPoint( tempVectorA.scale(0.2).negate()); }//end if on proximity test }//end if col != row }//end loop on col

It wouldn't make any sense to compare the position of a prey fish with itself, so the boldface if statement in Listing 17 prevents that from happening.

Code is relatively straightforward

Since you already know how to use most of the methods in the game-math library, you should have no difficulty understanding the code in Listing 17. This code:

• Gets the displacement vector that defines the separation between the primary object and the secondary object.
• Compares the length of that vector with 10 units.
• Moves the primary prey fish object in the opposite direction by 20-percent of the length of the separation vector if the separation is less than 10 units.

As mentioned earlier, however, moving the primary fish object away from one prey fish could cause it to be moved closer to a different prey fish object, so the separation algorithm is far from perfect.

Listing 17 signals the end of the inner loop that began in Listing 16.

Save the primary prey fish object and do another iteration

The primary prey fish object may or may not have been moved. Regardless, a clone of that object is created and saved in the container that contains the prey-fish population in Listing 18.

Listing 18. Save the primary prey fish object and do another iteration.

 preyObjects.set(row,refPrey.clone()); }//end loop on row

After the object is saved in Listing 18, control is transferred back to the top of the outer for loop in Listing 16 and the next prey fish object in the population is compared with all of the other prey-fish objects in the population, making corrections to the position of the prey fish as necessary.

Prey fish objects react to the predator

As I mentioned earlier, each prey fish object has a reasonably good defense mechanism to avoid being eaten by the predator. However, in the final analysis, whether or not a prey fish will escape when it encounters the predator face to face is a random process. To some extent, success or failure to escape depends on how quickly the prey fish senses the presence of the predator.

Listing 19 is the beginning of a for loop in which the proximity of each prey fish to the predator is tested and evasive action on the part of the prey fish is taken if the distance between the two is less than 50 units.

Listing 19. Prey fish objects react to the predator.

 for(int cnt = 0;cnt < preyObjects.size();cnt++){ tempPrey = preyObjects.get(cnt); //Get a displacement vector from the prey object // to the predator. tempVectorA = tempPrey.getDisplacementVector( predator);

The code in Listing 19 gets the next prey fish in the population and gets a displacement vector describing the separation of the prey fish from the predator.

Prey fish object takes evasive action

If the prey fish is at least 50 units away from the predator, the prey fish simply goes on swimming without concern for the predator. However, if the prey fish is less than 50 units away from the predator, the prey fish takes evasive action in an attempt to escape the predator. The evasive action is accomplished in Listing 20, and this is where some of the random behavior comes into play.

Listing 20. Prey fish object takes evasive action.

 if(tempVectorA.getLength() < 50){ tempVectorA = tempVectorA.negate().scale( random.nextDouble()); tempPrey = tempPrey.addVectorToPoint(tempVectorA);

The prey fish moves by a random distance

In listing 20, the negative of the displacement vector separating the prey fish from the predator is scaled by a random value ranging from 0 to 1. Then the prey fish is moved by the distance and direction specified by the resulting vector. If the random value is 0, the prey fish won't be moved at all. If the random value is 1, the distance between the prey fish and the predator will be doubled.& For values between 0 and 1, the prey fish will be moved different distances away from the predator.

Did the prey fish escape?

Once the prey fish has taken the evasive action and has (possibly) moved away from the predator, the separation between the prey fish and the predator is tested again in Listing 21.

Listing 21. Test the separation again.

 tempVectorA = tempPrey.getDisplacementVector(predator); if(tempVectorA.getLength() < 25){ tempPrey = preyObjects.remove(cnt); displayVectors.remove(cnt); Toolkit.getDefaultToolkit().beep(); killCount++; timer.setText( "" + (new Date().getTime() - baseTime) + " / " + killCount);

If the new separation is at least 25 units, the escape attempt is deemed successful and the prey fish will continue to live. (You can decrease or increase the threshold distance used in the test in Listing 21 to cause the prey fish object to be more or less successful in the escape attempt. If you make the threshold distance small enough, the prey fish will almost always escape.)

When the escape attempt is not successful...

When the escape attempt is not successful, the remaining code in Listing 21 is executed. The prey fish is deemed to have been eaten by the predator. Therefore, the prey fish and its direction vector are removed from the containers that contain them. This causes the prey fish to be removed from the population.

In addition, the program sounds a beep to notify the user of the successful attack by the predator and the kill count that is displayed in the bottom right of Figure 1 is incremented by one.

Display elapsed time and number of fish eaten

Finally, the code in Listing 21 displays the elapsed time in milliseconds since the Start button was clicked along with the number of fish that have been eaten. Note that the value that is displayed is the elapsed time when the most recent prey fish was eaten by the predator and is not the total elapsed time since the Start button was clicked. (The elapsed time value won't change again until another prey fish is eaten.)

When all of the prey fish have been eaten, the final time that is displayed is the time that was required for the predator to eat all of the fish in the population and the final kill count that is displayed is the same of the original number of prey fish in the population. (Also note that the player must click the Stop button before starting a new game. Fixing this inconvenience will be a good exercise for the student.)

When the escape attempt is successful...

When the escape attempt is successful, a clone of the prey fish in its new location is stored in the preyObjects container and a normalized version of the fish's direction vector is stored in the displayVectors container as shown in Listing 22.

Listing 22. Restore the prey fish to the population.

 }else{ //The escape attempt was successful. Restore // a clone of the prey object in its new // location along with its direction vector // to the population. preyObjects.set(cnt,tempPrey.clone()); //Save a normalized version of the direction // vector for drawing later. It will // overwrite the previously saved vector and // if you watch closely it will show the // prey object running away from the // predator. displayVectors.set( cnt,tempVectorA.normalize().scale(15.0)); }//end else }//end if distance < 50 }//end for loop on population

Listing 22 also signals the end of the for loop that began in Listing 19.

Keep the prey fish in the playing field

I have one more section of code to explain that deals with the behavior of the prey fish during each iteration of the animation loop. The code in Listing 23 causes prey fish that stray outside the playing field to return to the playing field.

Listing 23. Keep the prey fish in the playing field.

 for(int cnt = 0;cnt < preyObjects.size();cnt++){ tempPrey = preyObjects.get(cnt); if(tempPrey.getDisplacementVector(origin). getLength() > 0.5*osiHeight){ tempVectorA = tempPrey.getDisplacementVector(origin); tempPrey = tempPrey.addVectorToPoint( tempVectorA.scale(0.1)); preyObjects.set(cnt,tempPrey.clone()); }//end if prey object is out of bounds }//end for loop to process each prey object

The playing field is the interior of a sphere

The playing field is defined to be the interior of a sphere that is centered on the origin with a radius that is one-half the height of the off-screen image. The for loop in Listing 23 iterates once for each prey fish remaining in the population. The if statement in Listing 23 tests to determine if a prey fish is outside of the sphere.

If the prey fish is outside the sphere, the code in Listing 23 gives that prey fish a nudge back toward the origin and saves a clone of the prey fish in its new location.

Erase the off-screen image and draw the large circle

Listing 24 erases the off-screen image and draws the large circle that defines the playing field shown in Figure 1.

Listing 24. Erase the off-screen image and draw the large circle.

 //Erase the screen g2D.setColor(Color.WHITE); GM01.fillRect(g2D,-osiWidth/2,osiHeight/2, osiWidth,osiHeight); //Draw a broken-line circle that represents the // intersection of the spherical boundary with the // x-y plane. Although the prey objects and the // predator can stray outside this sphere, they // prefer to be inside the sphere. GM01.Point3D tempPointA = new GM01.Point3D( new GM01.ColMatrix3D(osiHeight/2,0,0)); GM01.Point3D tempPointB; //The circle is defined by 39 points around the // circumference. g2D.setColor(Color.BLACK); for(int cnt = 0;cnt < 39;cnt++){ tempPointB = new GM01.Point3D(new GM01.ColMatrix3D( osiHeight/2*Math.cos((cnt*360/39)*Math.PI/180), osiHeight/2*Math.sin((cnt*360/39)*Math.PI/180), 0)); //Connect every third pair of points with a line if(cnt%3 == 0){ new GM01.Line3D( tempPointA,tempPointB).draw(g2D); }//end if //Save the old point. tempPointA = tempPointB; }//end for loop //Draw the final line required to close the circle new GM01.Line3D(tempPointA,new GM01.Point3D( new GM01.ColMatrix3D( osiHeight/2,0,0))).draw(g2D);

Although somewhat tedious, the code in Listing 24 is straightforward and shouldn't require an explanation beyond the embedded comments.

Draw the prey fish on the off-screen image

Depending on the states of the two check boxes in Figure 1, Listing 25 draws the GM01.Point3D objects that represent the prey fish or GM01.Vector3D objects that represent the direction vectors for the prey fish, or both on the off-screen image.  (Note that only the vectors were drawn on the off-screen image in Figure 1.)

Listing 25. Draw the prey fish on the off-screen image.

 g2D.setColor(Color.RED); for(int cnt = 0;cnt < preyObjects.size();cnt++){ tempPrey = preyObjects.get(cnt); if(drawPoints){ tempPrey.draw(g2D);//draw circle around point }//end if if(drawVectors){ //Draw the vector with its tail at the point. displayVectors.get(cnt).draw(g2D,tempPrey); }//end if }//end for loop

There is nothing new in Listing 25, so I won't bore you with a detailed explanation of the code.

It's time to deal with the predator

So far, we have been dealing exclusively with code that controls the behavior of the prey fish. The time has come to deal with the code that controls the behavior of the predator.

Cause the predator to slowly circle the cluster of prey fish

When the player clicks the Attack button, a boolean variable named attack is set to true, causing the predator to enter attack mode during the next iteration of the animation loop. However, when the value of this variable is false, the predator is not in attack mode and its behavior is to swim slowly around the cluster of prey fish encouraging them to bunch up into a smaller and tighter cluster.

The code that accomplishes this circling behavior is shown in Listing 26.

Listing 26. Cause the predator to slowly circle the cluster of prey fish.

 //When the predator is not in attack mode, cause // it to slowly circle the cluster of prey // objects. if(!attack){ //Get a displacement vector pointing from the // predator to the preyCenter. predatorVec = predator.getDisplacementVector(preyCenter); //Create a vector that is rotated relative to // predatorVec. Note how each component in // this new vector is set equal to a different // component in predatorVec tempVectorB = new GM01.Vector3D( new GM01.ColMatrix3D( predatorVec.getData(1), predatorVec.getData(2), predatorVec.getData(0))).scale(0.15); //Scale predatorVec and add the two vectors. // Then move the predator according to the sum // of the vectors. //Moving the prey object in the direction of // the sum of these two vectors produces a // motion that causes the predator to // spiral toward the preyCenter instead of // simply moving in a straight line toward the // preyCenter. The scale factors control the // relative motion between the two directions, // and are fairly sensitive. predatorVec = predatorVec.scale(0.095).add(tempVectorB); predator = predator.addVectorToPoint( predatorVec.scale(1.0));

The code in Listing 26 is very similar to code that I explained earlier in conjunction with the behavior of the prey fish. Therefore, no explanation beyond the embedded comments should be required.

Execute the attack

When the user clicks the Attack button, the value of the variable named attack is set to true, causing the code in Listing 27 to be executed during subsequent iterations of the animation loop.

Listing 27. Execute the attack.

 }else{//attack is true predator = predator.addVectorToPoint( predatorVec.scale(0.25)); //Check to see if the predator is outside the // spherical boundary that defines the playing // field. if(predator.getDisplacementVector(origin). getLength() > 0.5*osiHeight){ //Shift out of attack mode and start circling // the prey fish again. attack = false; attackButton.setEnabled(true); }//end if }//end else

The predator is in attack mode

The predator is in attack mode at the beginning of Listing 27, and will remain in attack mode using the same displacement vector to control its speed and direction until it leaves the spherical playing field. When it leaves the playing field, the value of the attack variable will be set to false, causing the predator to revert to non-attack mode.

As the predator encounters prey fish along its trip toward the edge of the playing field, the code in Listing 21 (explained earlier) will determine whether those prey fish escape or get eaten by the predator.

Control of speed and direction

The displacement vector that controls the speed and direction of the predator (predatorVec in Listing 27) is created in the actionPerformed method in Listing 3 in response to a click on the Attack button.

This vector is constructed to point to the most recently computed geometric center of the cluster of prey fish. Note however, that the vector may no longer point to the exact center of the cluster because the exact center of the cluster may have changed since it was last computed. In other words, the position of the geometric center of the prey-fish cluster changes as the predator attacks and causes the prey fish to scatter. As programmed, the predator is unable to respond to such changes and continues to move in the same direction at the same speed until it leaves the playing field.

 An interesting upgrade A program upgrade to cause the predator to accommodate such changes in the geometric center would be an interesting exercise for the student.

In other words, even though the prey fish scatter, the predator is constrained to move in a straight line across the playing field once an attack has begun.

Draw the predator on the off-screen image

Listing 28 sets the drawing color to BLUE and draws the predator's direction vector on the off-screen image.

Listing 28. Draw the predator on the off-screen image.

 g2D.setColor(Color.BLUE); //Enable the following statement to draw a circle // around the point that represents the predator. //predator.draw(g2D); //Draw the predator's vector. predatorVec.normalize().scale(15.0).draw( g2D,predator); g2D.setColor(Color.RED);//restore red color

Copy off-screen image, insert time delay, etc

Listing 29 copies the off-screen image to the canvas and then causes the animation thread to sleep for 166 milliseconds.

Listing 29. Copy off-screen image, insert time delay, etc.

 //Copy the off-screen image to the canvas and then // do it all again. myCanvas.repaint(); //Insert a time delay. Change the sleep time to // speed up or slow down the animation. try{ Thread.currentThread().sleep(166); }catch(Exception e){ e.printStackTrace(); }//end catch }//end animation loop }//end run method }//end inner class named Animate

Listing 29 also signals the end of the animation loop, the end of the run method, and the end of the inner class named Animate.

That is all of the code that I will explain for this program. You can view the remainder of the code in Listing 31 near the end of the lesson.

Not a graphics program

Even though this program produces quite a lot of 3D graphics, and those graphics are important in the playing of the game, this is not a graphics program. Rather, it is a 3D data-processing program that produces graphics as a side effect.

That is not to say that the graphics are unimportant. To the contrary, the graphics provide visual feedback to the player, which allows the player to implement a strategy for success. Without graphics, the game would be very boring. However, the programming effort to produce the graphics represents an almost trivial part of the total programming effort for this game.

An almost trivial part of the programming effort

Exclusive of the code required to draw the large circle shown in Figure 1, all of the graphics shown in Figure 1, Figure 5, Figure 6, Figure 7, and Figure 8 are produced by only two calls to game-math library methods named draw. In other words, all of the required graphics are produced by only two statements in the program code. (One additional statement is required if you want to display the small circles that represent the locations of the prey fish.)

If those two statements are removed, the program will still compile and run, and the game can still be played. However, without visual feedback, the game isn't much fun. The lack of visual feedback eliminates the strategy aspect of the game, causing it to be solely a game of chance. Be that as it may, with or without visual feedback, the player can still click the Start button and then repetitively click the Attack button until the display in the bottom-right of Figure 1 shows that all of the prey fish have been eaten.

Why am I telling you this?

I'm telling you this to emphasize that the essential skills required to program this game (and probably most games for that matter) consist of skills in mathematics, programming logic, and several other technical areas. The ability to produce on-screen graphics is necessary for an enjoyable game, but, (given a good game-math library that supports graphics), producing on-screen graphics is almost a trivial part of the programming effort. In this series of lessons, you need to be mainly concentrating on learning mathematics and programming logic and treating the production of on-screen graphics almost as an afterthought.

### The program named GM01test04

This animation program is designed to exercise many of the 2D features of the GM01 game-math library. The animation is generally based on the idea of a flocking behavior similar to that exhibited by birds and fish. A set of GM01.Point2D objects is created with random locations to act as predators. An additional GM01.Point2D object is also created to play the part of a prey object.

The prey object is drawn in red while the predators are drawn in black as shown in Figure 2. An algorithm is executed that attempts to cause the predators to chase the prey object without colliding with one another.

Even though the algorithm causes the predators to chase the prey object, it also tries to keep the predators from colliding with the prey object.

The user input GUI

A GUI is provided that contains an input text field for the number of predators plus a Start button and a Stop button. The GUI also contains check boxes that allow the user to elect to display points only, direction vectors only, or both. (Both the point and the direction vector is always displayed for the prey object.)

The user specifies the number of randomly-placed predators and clicks the Start button, at which time the animation begins and the predators start chasing the prey object. Prey-object motion is random.

The animation continues until the user clicks the Stop button. The user can click the Stop button, change any of the input parameters, and then click the Start button again to re-start the animation with different parameters such as the number of predator objects.

Swimming in formation

An unexpected result is that the algorithm seems to cause the predators to come together and swim in formation while chasing the prey object. The most common formation is hexagonal as shown in Figure 9, which shows 12 predators swimming in a hexagonal formation.

Figure 9. Twelve predators swimming in a hexagon formation in GM01test04.

Other formations appear as well

Some triangles, diamonds, and incomplete hexagons also appear. For example, Figure 10 shows six predators swimming in a diamond formation.

Figure 10. Six predators swimming in a diamond formation.

No explanation for this behavior

I haven't given the matter a lot of thought, but at this point, I have no explanation for this behavior. Note that the tendency to swim in formation is more visually obvious when only the points are displayed. When the vectors are displayed, it is more difficult to pick out the formation.

Dogged determination

On the other hand, the animation is most impressive when the direction vectors are displayed, with or without points, because the vectors illustrate the dogged determination and undying focus that the predators maintain while chasing the prey object.

Won't explain the code

Once you understand the code in the program named GM01test08 that I explained earlier in this lesson, you should have no difficulty understanding the code in this program. Therefore, I won't explain the code in this program. I included this program in this lesson mainly to illustrate the differences between 2D and 3D from both a visual and programming viewpoint.

A complete listing of this program is provided in Listing 32 near the end of the lesson.

### The program named GM01test03

This is a 3D update of the 2D program named GM01test04 discussed above.

A comparison of programming requirements

Basically all that was required to perform the update was to specify 3D classes from the game-math library in place of the 2D classes used in the 2D version of the program. In some cases, this in turn required that argument lists for constructors and methods be expanded from two dimensions to three dimensions. Just about everything else took care of itself.

A comparison of these two programs illustrates the value of the game-math library named GM01 and the ease with which you can switch back and forth between 2D and 3D programming when using the library.

A comparison of visual behavior

The visual behavior of this 3D version, as shown in Figure 3, is more realistic than the 2D version. This is particularly true when the prey object gets in the middle of the predators and the display is showing vectors. In the 2D version, a predator is constrained to swing around only in the plane of the screen.; However, in this 3D version, a predator is not subject to that constraint and is free to swing around in the most appropriate way as the prey object passes by.

This constraint causes the motion in the 2D version to be less fluid than the motion in the 3D version. This can best be demonstrated with only one predator because that makes it easy to see the behavior of an individual predator as the animation is running.

No swimming in formation

One very interesting thing that I have noticed is that unlike the 2D version, the predators in this 3D version don't seem to have a tendency to form up and swim in formation while chasing the prey object. This may be because they have more options in terms of avoiding collisions while giving chase. However, that is pure speculation on my part since I don't know why the predators tend to swim in formation in the 2D version anyway.

Won't explain the code

As is the case with the earlier program named GM01test04, once you understand the code in the program named GM01test08, you should have no difficulty understanding the code in this program. Therefore, I won't explain the code in this program. I included this program and the earlier 2D version in this lesson mainly to illustrate the differences between 2D and 3D from both a visual viewpoint and programming viewpoint.

A complete listing of this program is provided in Listing 33 near the end of the lesson.

### The program named StringArt04

This program animates the behavior of the earlier program named StringArt03 that I explained in an earlier lesson (see Resources). See the comments at the beginning of that program for a description of both programs.

The only significant difference in the behavior of the two programs is that this program slows the rotation process down and animates it so that the user can see it happening in slow motion. Of course, quite a few changes were required to convert the program from a static program to an animated program.

However, if you understand the code in the earlier program named StringArt03 and you understand the code in the program named GM01test08 that I explained earlier in this lesson, you should have no difficulty understanding the code in the program named StringArt04. Therefore, I won't explain the code in this program. A screen shot of the program in action is shown in Figure 4. A complete listing of the program is provided in Listing 34.

## Run the programs

I encourage you to copy the code from Listing 30 through Listing 34, compile the code, and execute the programs in conjunction with the game-math library named GM01.  Experiment with the code, making changes, and observing the results of your changes. Make certain that you can explain why your changes behave as they do.

## Summary

In this lesson, you learned how to write your first interactive 3D game using the game-math library named GM01. You also learned how to write a Java program that simulates flocking behavior such as that exhibited by birds and fish and you learned how to incorporate that behavior into the game. Finally, you examined three other programs that illustrate various aspects of both 2D and 3D animation using the game-math library.

## What's next?

In the next lesson, you will learn the fundamentals of the vector dot product in both 2D and 3D. You will learn how to update the game-math library to support various aspects of the vector dot product, and you will learn how to write 2D and 3D programs that use the vector dot product methods in the game-math library.

## Complete program listings

Complete listings of the programs discussed in this lesson are shown in Listing 30 through Listing 34 below.

Listing 30. Source code for the game-math library named GM01.

Listing 31. Source code for the game program named GM01test08.

Listing 32. Source code for the program named GM01test04.

Listing 33. Source code for the program named GM01test03.

Listing 34. Source code for the program named StringArt04.

Copyright 2008, Richard G. Baldwin. Reproduction in whole or in part in any form or medium without express written permission from Richard Baldwin is prohibited.

Richard Baldwin is a college professor (at Austin Community College in Austin, TX) and private consultant whose primary focus is a combination of Java, C#, and XML. In addition to the many platform and/or language independent benefits of Java and C# applications, he believes that a combination of Java, C#, and XML will become the primary driving force in the delivery of structured information on the Web.

Richard has participated in numerous consulting projects and he frequently provides onsite training at the high-tech companies located in and around Austin, Texas. He is the author of Baldwin's Programming Tutorials, which have gained a worldwide following among experienced and aspiring programmers. He has also published articles in JavaPro magazine.

In addition to his programming expertise, Richard has many years of practical experience in Digital Signal Processing (DSP). His first job after he earned his Bachelor's degree was doing DSP in the Seismic Research Department of Texas Instruments. (TI is still a world leader in DSP.) In the following years, he applied his programming and DSP expertise to other interesting areas including sonar and underwater acoustics.

Richard holds an MSEE degree from Southern Methodist University and has many years of experience in the application of computer technology to real-world problems.

Baldwin@DickBaldwin.com

Comment and Contribute

(Maximum characters: 1200). You have characters left.

## Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

By submitting your information, you agree that developer.com may send you developer offers via email, phone and text message, as well as email offers about other products and services that developer believes may be of interest to you. developer will process your information in accordance with the Quinstreet Privacy Policy.

## Most Popular Developer Stories

Thanks for your registration, follow us on our social networks to keep up-to-date