Chapter 5: Object-Oriented Programming
In the first chapter we briefly mentioned the Object-Oriented programming (OO) paradigm. We talked about how C# is an Object-Oriented language. C# is actually what is called a pure Object-Oriented language, in that everything is an object. In C# the number 5 is an object, the different art resources, and everything we work with is an object. In this chapter we'll take a closer look at Object-Oriented programming. We will cover:
l The main concepts of OO programming
l How OO programs are designed
l How to create and use classes of objects
Object-Oriented Programming 101
Objects are everywhere in life. When using the term object here it means the same thing as in “real” life. This book is an object, the computer you work with C# on is an object, etc. Object-Oriented programming is an attempt to take these objects we find in the real world and translate them into computer code. This works for most types of programming, but really makes sense for game programming. When making games it's easy to think of things in terms of objects, since our games are full of “real” objects. Like in the example we mentioned in chapter one with the racing game; in making a racing game we can have the car be an object, opponents be objects, the terrain be an object, etc.
This thinking of the game in terms of objects is helpful, but how do we write the code to create objects? What we'll be doing to create objects is to create classes. Classes are templates for objects, they define the different attributes, variables and functions the object has. Classes are not objects themselves; they are descriptions for types of objects. Similar to the animal kingdom phylum, where classes are used to describe type of animals, like there is a class of animals called lions. But an individual lion, such as the lion at the local zoo, is a lion object while the class lion is not an individual object. The actual objects, like the lion at the zoo, are called instances of an object. Classes are the heart of Object-Oriented programming; all of our coding in C# will be in defining classes.
Classes are a combination variables and functions (the functions are called methods.) The variables and methods of a class are called the members of the class. OO programming works by taking an object in the real or game world we want to model (instead of a car let's use the example of a spaceship) then defining it in terms of attributes , variables holding information about it (like it's speed, position), and what the object does (like slow down, turn left, turn right).
Classes are usually defined in their own .cs source files. The declaration for a class is:
access-specifier class className
{
// variables
// methods
}
A basic ship class would be like:
public class SpaceShip
{
private float positionX;
private float positionY;
private float speed;
public IncreaseSpeed()
{
speed += 10.0f;
}
}
Again, this only defines a template for an object, but doesn’t create one. To create an instance of the SpaceShip class we declare a variable of the SpaceShip type and use the new keyword to create it. Then we can access members with the public keyword in front of them just like we do with a struct.
SpaceShip ship;
ship = new SpaceShip();
ship.IncreaseSpeed();
Going back to the class definition, the first question when looking at this might be what all of those public and private words are. One of the major concepts of OO programming is encapsulation, grouping together data and methods (creating objects.) One of the issues with encapsulation is who can access the data. When we have a variable like the ship's speed we probably don't want other objects going in and changing it. If we had a player that controlled the ship it would make sense to allow the player to tell the ship to increase in speed (call the IncreaseSpeed method) but it wouldn't be good to allow the player class to just set the speed to some arbitrary values, like change it from 0 to 25 (especially if we have physics setup.) To set who can modify what with our class we set access specifiers. For the class and every field and method we give it the specifier of private, public, or protected. Private means nothing outside of the class can do anything with it. With the ship's speed being private no one but the ship can access its speed variable. Public says that anyone else can access it. The ship's IncreaseSpeed method is public, which means any other object can tell the ship to increase its speed. (We'll look at what protected means in moment.) Usually we have all of our data for a class be private and all of its methods be public. This way each class manages its own data (state), but can take messages (have methods called) by other objects.
In programming terms, objects can be thought of as a group of data and actions. This makes sense, as a common way to think about classifying objects is to think about they're characteristics and what they do. For instance, we classify a Ferrari as different from other cars because of attributes it has (different engine, body) and that it can perform actions differently than other cars (go faster.) The data and functions (actions) are called the members of the object, and the variables are called member variables or properties. The functions contained in the object are called methods of the objects, though a lot of times people will refer to them as member functions. All of these members, variables and methods, are defined in classes.
Where the work is done
When I first learned about OO programming the most confusing wasn't defining classes or setting them up, but figuring out where the work is actually done. We can set up a bunch of objects and methods but how are these used. In most OO programs the main function does almost nothing but create a single object and start it (such as in a windows form application the main function just creates a form class.) So it can be hard to tell how the code is executed.
We can tell how code runs by the arrangement of objects. A common method for organizing programs is to have one (or only a few) main class that everything else is contained in. In XNA we have a Game class. This class holds all of the objects for our game. Inside the main class we create objects like game objects (players, enemies, and so fourth) and objects to handle playing our game, such as a keyboard object to read in key input and graphics device object to handle drawing onscreen. After the objects are created in the game class they run and are updated in the main game loop. To tell how a game is being run we can look in it's main loop and see all of the main objects and how they are being used.
Note that this business of how to “arrange” objects in our programs is pretty controversial. A lot of programmers want to make big objects that contain a lot of stuff so that they can have the object do a great variety of things. But when objects grow too huge, like having hundreds of members and thousands of lines of code they can be confusing and hard to work with. On the other hand, writing a program with hundreds of small objects in it can make things just as difficult. There is a huge variety and debate on how to organize objects in programs.
One thing to watch out for is the tendency a lot of amateur developers have to make wrappers for other objects. Many people want to make helper classes that aren't really needed. For example, in XNA there is a class Texture2D to handle loading pictures for sprites. Some might want to create a Picture class that will be the same as the Texture2D; it will have a Texture2D member and every method of the Picture class will just call the method from Texture2D. Making these wrapper classes is unnecessary and makes code more confusing. (There are special reasons for sometimes writing wrapper classes, like when making an advanced 3D engine, but most people who make them do so when they're not needed).
Inheritance
Getting back to our space ship class, let's say in our program we'll actually have two kinds of ships, a cruiser ship and a fighter ship. They will have many unique differences, like the fighter ship will have a laser cannon but the cruiser won't. But they will also have a lot of common functionality. Instead of repeating a lot of duplicate code to make two totally separate ship classes we can make a single base class called SpaceShip. Then we can make two more ship classes, CruserShip and FighterShip that both inherit from the SpaceShip class. This means the cruiser and fighter classes have everything that the SpaceShip class does but with some extra features too. The code for the fighter ship could look like:
public class SpaceShip
{
private float positionX = 0.0f;
private float positionY = 0.0f;
private float speed = 100.0f;
public IncreaseSpeed()
{
speed += 10.0f;
}
}
public class FighterShip : SpaceShip
{
private int ammo = 20;
public FireCannon()
{
ammo--;
}
}
The parent class that we are inheriting from is called the base class. The syntax for inheriting from a class is:
public class NewClass : BaseClass
Then we can make a FighterShip that has all the functionality of the SpaceShip too:
FighterShip ship = new FighterShip();
ship.FireCannon();
ship.IncreaseSpeed();
This is the basics on how to inherit from a class. One more note: if we have a member of a class that we want to be private except for the classes that inherit from it, we give it the protected access specifier.
Creating Classes
The previous sections have been a lot of theory, let's get to writing some code. Starting in the second part of this book, we'll begin to make a little space ship fighting game, where the player controls a ship and fights off enemies that fly by. The making of the player would seem to be the first thing we should create, and it makes sense to do so, but here why don't we start making a class to handle enemy ships that fly around, called enemyShip. The first thing we need to think about is what we want the enemies to be able to do. Obviously they must be able to fly, so let's give each enemyShip the methods to moveForword(), moveLeft(), moveRight() to change their position. And this is a 2D game, so we'll record each of their positions with an x coordinate and a y coordinate. We'll also need to handle the player shooting at them and we want them to be able to be destroyed, so let's give each an integer of hitPoints and a method to handle firedAt(). We'll add more to our enemy class, but for now a basic outline of our basic enemy class is:
class enemyShip
float x, y;
int hitPoints;
void setLocation( float xLocation. float yLocation );
void moveForward();
void moveLeft();
void moveRight();
void firedAt();
So that's the basic outline, let's create a real class.
Goto File->New Project and create a Console Application with the name enemyShipTest. Then right click on the name enemyShipTest in the Solution Explorer window on the right side of the program. From the right click menu select Add->New Item. A dialog box will appear, make sure Class is selected and in the class name type enemyShip and click Add. Notice in the Solution Explorer window you can click on Program.cs or enemyShip.cs to choose. Add the following to enemyShip.cs to have the file look like this:
using System;
using System.Collections.Generic;
using System.Text;
namespace enemyShipTest
{
class enemyShip
{
private float x, y;
private int hitPoints;
EnemyShip( int hitPointStart )
{
}
public void setLocation(float xLocation, float yLocation)
{
}
public void moveForward()
{
}
public void moveLeft()
{
}
public void moveRight()
{
}
public void firedAt()
{
}
public string GetInfoString()
{
return "Ship has location “ + x.ToString() + “ “ + y.ToString() + “ and has “ + hitpoints.ToString() + “ hitpoints”'
}
}
}
We're going to go through this class line by line and set it up.
The first two lines store variables for the ship.
private float x, y;
private int hitPoints;
We don't know where the ship will start at, but we should have each ship start with the same number of hitpoints. So change the hitpoints line to:
private int hitPoints = 5;
When we declare a variable in a class in C# we can go ahead and give it a default value, which is what we just did.
We can also assign default values by passing them to the class when it is created. This is done by a special method called the constructor of the class. The method EnemyShip( int hitPointStart ) is the constructor for the EnemyShip class. Change it to look like:
EnemyShip( int hitPointStart )
{
hitPoints = hitPointStart;
}
We can tell it is the constructor because the method's name is the same as the class name and it doesn't need any access specifiers in front of it. The constructor is the method called when creating the class:
EnemyShip ship = new EnemyShip( 25 ); // Creates a ship with 25 hitpoints
EnemyShip ship2 = new EnemyShip(); // This will cause an error, since we need to pass the //constructor a number of hitpoints.
Going down in code, the setLocation method will just let us set an x and y value for the ship, so all we'll do here is add:
public void setLocation(float xLocation, float yLocation)
{
x = xLocation;
y = yLocation;
}
Now that method is set, it just lets the user specify the location of the ship.
Our next function, moveForward(), will move the player forward in the y direction, so we'll increment the ship’s y value.
public void moveForward()
{
y++;
}
Similarly, for the moveLeft and moveRight functions we'll just have them change the x value for the ship:
public void moveLeft()
{
x--;
}
public void moveRight()
{
x++;
}
The choice to change the coordinate by 1 is purely arbitrary, we could have moved by more. Now the last method in the class is firedAt(), which when the ship is firedAt() (and we assume it is hit) we'll just decrease the hitPoints by one.
public void firedAt()
{
hitPoints--;
}
It may not be fancy, but now we have a working class of an enemy ship. Now let's put it to work.
Creating an Instance
In our main function we'll just create two ships, move them around, and then print out their info:
static void Main(string[] args)
{
EnemyShip ship1 = new EnemyShip(30);
EnemyShip ship2 = new EnemyShip(35);
ship1.SetLocation(10,10);
ship1.MoveLeft();
ship2.Fire();
ship1.PrintInfo();
ship2.PrintInfo();
}
The output will be:
Ship has
location 10 10 and has 30 hitpoints
Ship has
location 0 0 and has 34 hitpoints
Summary
In this chapter we looked at the basics of Object-Oriented Programming. This just scratched the surface of what OO is and what we can do with it. OO programming is a huge field; check the appendix for some resources to learn more about it. But we've learned enough to start making games: we know the basics of making classes and using them.
