Monday, 9 July 2012

Programming in C# with XNA – Object Orientated Basics


We now have some of the basics under our belt, we can create variables, we can assign values to them and make conditional decisions on our code. We are now going to look at the Object Orientated side of Programming (OOP).

What is OOP?


In as simple terms as I can muster, it’s a way of structuring our data, we can take our variables and put them in anObject. “But Why?” I hear you ask… Well, it’s a great way of structuring your programs so that they resemble more obvious entities, for example, in the last post we created a number of variables, player name, score, lives etc… Rather than have a load of disbanded variables roaming about our game code we can put these variables into a single Object and we could call that object Player.

How to define an Object


To define an object we are going to use a ‘class’. I think a good way to think of a ‘class’ is to see it as a blueprint of your Object. If we were to create a more complex Object than a player, lest say a Vehicle then we need to think about what a Vehicle will need, so, for starters it will need an engine, a fuel tank, a navigation system to turn, accelerate and break. As a basic vehicle goes, that’s pretty much all we need. So a definition of a vehicle class could look like this:

public class BaseVehicle
{
public BaseEngine Engine;
public BaseFuelTank FuelTank;
public BaseNavSystem NavSystem;
public Vector3 Velocity;
public Vector3 Acceleration;
public BaseVehicle()
{
Engine = new BaseEngine();
FuelTank = new BaseFuelTank();
NavSystem = new BaseNavSystem();
}
}

To add a new class to the project, right click on the project name (RCProgrammingTutorial) select ‘Add’ then ‘Class’ from the drop down menu.

You can see in our class we contain other objects here, BaseEngine, BaseFuelTank and BaseNavSystem, Velocity (we need to know what direction and speed we are heading) and Acceleration. You can also see what is called a ‘constructor’ (ctor). This is used to set up our member objects so they are ready to use. You don’t have to create a constructor, but it can help if you have member objects you want initialized when you create an instance of the object.

As I am sure you can imagine, we could take this example a long way, there are lots more things a vehicle needs, but this help show the basic layout of a class and the sort of things you need to think about when creating them. So how will our Player class look?

public class Player
{
protected string playerName;
protected int score;
public Player(string name)
{
playerName = name;
score = 0;
}
}

We have defined the Player class with two members playerName and score. We have also created a constructor with a parameter of string name, this means when we create an instance of the object we can load the playerName variable at the same time. We have also defined the members as ‘protected’, I will explain this a bit more in the section Inheritance, but in essence, the protected access modifier means that only this class can see this variable. So it has restricted the ‘scope’ of this variable to this class, which means that when we create an instance of the object, we can’t access the variables, “Why would we want to do that!!” I hear you shout, well this is a form of encapsulation..

Encapsulation


Sometimes we don’t want to give full access to our member variables or we may want to do some extra processing when member variables are accessed or set and this can be done with ‘encapsulation’. So, how do we give the outside world access to those protected or private member variables? With Properties or functions. Personally I prefer to use properties, but in some cases you will need to use functions to do this depending on the kind of actions you are trying to impose. How do we set up properties then, like this:

public string PlayerName
{
get
{
return playerName;
}
set
{
// Code to detect if bad language has bee used
// ...
// ...
playerName = value;
}
}
public int Score
{
get { return score; }
set { score = value; }
}

I have created two properties, one for playerName and one for score, as you can see the layout is different but they both do the same job really, the ‘get’ section allows access to the variable and the set allows the variables content to be changed. As you can see in the set part of the PlayerName property you could have code there to make sure that the players name is not set using bad language, I am sure if you had a global high score table you don’t want any old text going up there… I have also made sure that the properties have a logical name for the member they are the property for, you can see I have named the member variables with camel case and the external property with initial upper case letters, this means I have the same name for the two entities but I can easily distinguish between the two in code.

The properties are great for this, but what if you wanted to then display a message or indicate to the rest of the program that a bad name was given? You could still use properties and have another variable that indicates if a bad name has been used, the rest of the program could then check this and act accordingly, or you could use a function like this:

public bool SetPlayerName(string name)
{
bool badName = false;
// Code to detect if bad language has bee used
// ...
// ...
if (!badName)
playerName = name;
return badName;
}

We can now use this method to set the playerName, and the return result can then be used to know if we where successful or not. If you are doing this, what do you think you should do with the code in the constructor?? :)

Inheritance


We have seen how we can create a definition for the objects we are going to use in our programs and games, but wouldn't it be nice if we could create some basic building block objects and be able to extend and build a hierarchy of objects from it…? And that’s just what Inheritance is. Lets go back to our BaseVehilcle class, from this we could create a BaseRoadVehicle class which could look something like this:

public class BaseRoadVehicle : BaseVehicle
{
protected int wheels;
protected int Passengers;
protected int cargoArea;
protected int doors;
public BaseRoadVehicle()
: base()
{
}
}

How easy was that! We created a new class of BaseRoadVehicle and inherited (derived) from BaseVehicle, this means that instances of the BaseRoadVehicle automaticaly get the Engine, FuelTank and all the other members of BaseVehicle as well as extending it with Wheels, Passengers, CargoArea and Doors, we could derive again from BaseRoadVehicle and create a BaseRoadBike class where we ensure that Wheels are never greater than 2 and Passengers > 1 and on and on, we can also, at the same time create a BaseWaterVehicle deriving from BaseVehicle or a BaseAirVehicle giving us a hierarchy of vehicle objects.

Polymorphism


Another awesome feature of OOP is Polymorphism. With this we can change the behavior of the base class methods (but only for this instance of the hierarchy), so for example, with out BaseRoadVehicle class we could have created a SetNumberOfWheels function to alter the number of wheels on a vehicle we could replace this function in our derived BaseRoadBike class by using the new keyword when defining out new method giving us two classes that look like this:

public class BaseRoadVehicle : BaseVehicle
{
protected int wheels;
protected int Passengers;
protected int cargoArea;
protected int doors;
public BaseRoadVehicle()
: base()
{
}
public int SetNumberOfWheels(int wheelCount)
{
wheels = wheelCount;
return wheels;
}
}
public class BaseRoadBike : BaseRoadVehicle
{
public BaseRoadBike()
: base()
{
}
public new int SetNumberOfWheels(int wheelCount)
{
if (wheelCount != 2)
wheels = 2;
else
wheels = wheelCount;
return wheels;
}
}

Now when an object of the type BaseRoadVehicle uses the SetNumberOfWheels method, the wheels will ALWAYS be set to 2 no matter what number is passed in the wheelCount parameter. But what if we wanted to keep the original functionality of a method and add to it, then we would need to use an override. Lets create a class called BaseRoadBuss when setting wheel numbers for our bus we want them to always be an even number (I am yet to see a buss with an add number of wheels)

public class BaseRoadVehicle : BaseVehicle
{
protected int wheels;
protected int Passengers;
protected int cargoArea;
protected int doors;
public BaseRoadVehicle()
: base()
{
}
public virtual int SetNumberOfWheels(int wheelCount)
{
wheels = wheelCount;
return wheels;
}
}
public class BaseRoadBuss : BaseRoadVehicle
{
public BaseRoadBuss()
: base()
{ }
public override int SetNumberOfWheels(int wheelCount)
{
if ((wheelCount % 2) != 0)
wheelCount -= 1;
return base.SetNumberOfWheels(wheelCount);
}
}

So, we have changed the BaseRoadVehicle method and made it a virtual function, this means that any classes deriving from this class can override this method with there own version of it, which is what we have done in the BaseRoadBuss calss. In this overriden function we make sure that the wheel count is an even number, we then use the base class version of this function to set the wheels variable with the newly corrected value for wheelCount.

There is a lot to take in in this post, OOP is a huge subject and I recommend you doing more reading on it to get a better handle that what I have given here, this is just the tip of the ice burg as far as OOP goes, but I hope I have given you enough to be getting on with and enough to help you see what we are going to d next….write a basic XNA game

As ever, if you have ANY questions then please post them here or PM me and I will do my best to help you out. I have put the solution to this post here, it wont do much other than illustrate what we have gone over thus far. Oh, and if you see anything that you think I have worded badly, please let me know and I will correct it ASAP.

Previous | Next

No comments:

Post a Comment