Java Article 13 - Using Mulitple Classes Part 2

So this is part two of our Java article covering using multiple classes in object oriented programming(OOP). In today's article I will provide another example of object oriented design with two classes instantiated into objects in a hypothetical flight combat game. This example is not designed as a lesson in game design, rather it provides an example of the way you can:

  • Create (Instantiate) objects then make them interact
  • Instantiate multiple objects from the same class
  • See how the methods and variables from the Plane class are "reused" in each object.
  • Make each object from the same class unique (initializing each object to have its own state).

When OOP was conceived, the thinking was to model objects in OOP after objects in the real world in that they would have behaviors (its methods) and states (the values its variables are set to.) Thus a Cat class could be used to instantiate multiple cat objects each with their own states like:

  • Color
  • Weight
  • Name

and have a shared set of method (cat behaviors) such as:

  • scratchUpTheFurniture()
  • meow()
  • eatCatFood()

In a real flight combat game you would typically have a large number of classes representing different game objects, interfaces, the game world, and the loop running it all, but for this example we will have a main class (MainGameController) which will act as the overall controller for the game, then a plane class and a missile class.

Our plane will have three methods making up its behavior:

fireMissle()
aquiretarget()
evaluateCondition()

A plane object will also have a state at any moment in its life cycle, which is what makes it unique from any other plane object, just like in the real world. Some variables defining its state could be:

int damageLevel;
int numberOfMissiles;
Plane target; // this defines a Plane object as a variable. Cool huh?
String planeType;
String planeName;
boolean destroyed;

Among other things, it would also have a location in the game world:

int xCoordinateLocation;
int yCoordinateLocation;
int zCoordinateLocation;

When we create (instantiate) our plane object from the Plane class it will need its state set or initialized. This initialization often occurs in the constructor method in the class. Here some of our variables will be defined in the constructor but some of them will be passed into the constructor from the MainGameController class. We are passing three of the variables in to simulate what happens when a player starts a new game and selects a type of plane to play.

For this project you will have to create three classes in the same folder. Compile and execute the following:

MainGameController:

public class MainGameController {

	static String planeName;
	static int numOfMissiles;
	static String planeType;

	//in this example main controls the game:
	//it creates the plane objects and tells them to fire
	//their missiles.
	public static void main(String[] args)
	{
		//First we create the player's plane.
		planeName = "Player One Plane";
		numOfMissiles = 8;
		planeType = "F16";
		Plane playerOnePlane =
			new Plane(planeName, numOfMissiles, planeType);

		
		 // Now we are going to create a computer controlled enemy
		 // plane. Since it is a different object it has its own
		 // unique state which is its name, numOfMissiles
		 // and planeType.
		 

		planeName = "Enemy Plane";
		numOfMissiles = 6;
		planeType = "F14";
		Plane enemyPlane =
			new Plane(planeName, numOfMissiles, planeType);

		
		 // Now we tell playerOnePlane to shoot at enemyPlane.
		 // In a real game this would all be triggered by inputs
		 // on your user interface and would be handled
		 // in a game loop, which would continuously listen for
		 // your input fire missiles and evaluate Condition.
		 //
		 //  Since this is just a simple example look at this for
		 //  loop as simulating hitting the fire button 10 times.
		 

		for (int i = 0; i < 10; i++)
		{
			System.out.println();
			System.out.println("For loop iteration " + (i+1));
			playerOnePlane.aquiretarget(enemyPlane);
			playerOnePlane.fireMissle();
			playerOnePlane.evaluateCondition();
			enemyPlane.evaluateCondition();
		}

		
		 // Normally it would only take 3 iterations of the above
		 // loop to destroy a plane. Because our player is actually 12
		 // years old and has been drinking caffinated soft drinks
		 // all day he will continue to spam missiles even though his
		 // target is destroyed. We simulate this by using i < 10
		 // above.
		 

	}

}

The Plane class:

public class Plane
{
	int damageLevel;
	int numberOfMissiles;
	
	 // This allows us to store a Plane object as a variable.
	 // our target is another object. Cool huh?
	 
	Plane target;
	String planeType;
	String planeName;
	boolean destroyed;

	
	 // This is the constructor. When a new object is created in
	 // our mainGameController class this constructor method is
	 //  called. Here our constructor initializes the variables
	 //  for our object.
	 
	public Plane(String name, int numOfMissiles, String type)
	{

		//We set this to 0 since all planes start with 0 damage
		damageLevel = 0; 

		//our main class provides the values for these variables:

		planeName = name;
		//This value is passed in since i decided to make the
		//number of starting missiles vary depending
		//on the plane type
		numberOfMissiles = numOfMissiles;
		//Plane type is selected by the player so this is
		//passed in by the main class
		planeType = type;
		//the plane starts off not destroyed.
		destroyed = false;

		System.out.println(planeName + " has been instantiated");

	}

	//Our plane object just has these methods:
	
	 // This method takes an object as its parameter. Here
	 // we are passed the name of another object which is our target.
	 
	public void aquiretarget(Plane planeObject)
	{
		target = planeObject;

	}

	//This fires a missile
	public void fireMissle()
	{
		
		 // A new missile will only be created if the plane has
		 // missiles left.
		 
		if (numberOfMissiles > 0)
		{
			numberOfMissiles --;
			System.out.println(planeName
					+ " has fired a missile. It has "
					+ numberOfMissiles + " left.");
			//we create a new missile and send it
			//its target
			Missile missile = new Missile (target);
		}
		else
		{
			System.out.println(planeName + " is out of missiles!");
		}
	}

	
	 // Here is a bit of logic.
	 // Each missile hit adds one to damageLevel. When damageLevel
	 // >= 3 the plane will explode.
	 
	public void evaluateCondition()
	{
		//Only do the damage calculations if the plane is
		//not already destroyed.
		if (!destroyed)
		{

			if (damageLevel < 3 )
			{
				System.out.println(planeName
						+ "'s damage level is "
						+ damageLevel + " out of 3");
			}
			else
			{
				System.out.println(planeName + "'s damage level "
						+ damageLevel + " out of 3");
				System.out.println("BOOM! "  + planeName
						+ " explodes!");
				destroyed = true;
			}
		}
		else
		{
			System.out.println("Lay off the caffine. " + planeName
					+ " was destroyed and can no longer take damage.");

		}
	}

}

and the Missile class:

public class Missile
{
	Plane target;

	public Missile(Plane target)
	{
		this.target = target;
		
		 // after a missile
		 // is launched it does damage to the target.
		 
		doDamage();
	}

	public void doDamage()
	{

		
		 // Here the Missile checks to see if its target is destroyed
		 // The missile will only hit if the target is not destroyed.
		 
		if (!target.destroyed)
		{
			System.out.println("The missile hit its target and does 1 damage");
			//Missiles do one point of damage.
			//This adds one to the target's damageLevel variable.
			target.damageLevel ++;
		}
	}

}

To understand what is going on you will have to trace the sequence of execution from MainGameController to the Plane class instances to the Missile class then back to MainGameController. Notice how the Plane class is used as a blueprint to create two plane objects which function independently?

That is pure OOP goodness.