OOPs concepts are the vital basics that every Game Programmer should know for effective programming. This blog helps you understand the key OOPs concepts and how they are applied in game programming.
What is OOPs?
Let’s begin with what OOPs (Objected Oriented Programming) is. In short, OOPs is a way of programming that eases coding a lot. It is the successor of the ‘C’ language, which is a procedural language with well-structured steps and procedures to compose a program. OOPs revolve around Objects and Classes.
• Class: A Class is a user-defined data type that has its own data members and member functions. It is a blue print of data and functions (methods). A class can’t do anything without an instance because a class does not hold any space in the memory.
• Object: Object is the basic unit of Object Oriented Programming. An instance of a class is the declared object of that particular class. Unlike a class, objects hold memory space and have an associated address. When a program is executed, objects do the work defined in the class. That is, the defined methods and data variables of a class are used by the object, which is an instance of that particular class.
Key Features of OOPs
Now let’s go through the key features of the Object Oriented Programming that makes it special: Abstraction (Data binding), Inheritance, Encapsulation, Polymorphism, and Message Passing.
• Abstraction: Abstraction is hiding what is not necessary and showing what is necessary. It can be accomplished through the use of access specifiers: protected, private, and public. Access specifiers tell the objects which methods or data variables of a particular class should be abstracted. In other words, they form the insulation of data from direct access. For example, the variables and functions that should be exposed shall be declared as ‘public’.
• Encapsulation: Binding of data variables with the functions is known as encapsulation. This defines the data variables that are used by a particular function as those associated with that function. This binding helps the function to work in the way it is designed.
• Inheritance: Inheritance simply means inheriting the data and functions of a class by another class. Inheritance makes the code reusable. This helps in adding features without modifying it. The inherited class is called derived class whereas the class from which derived class is inherited is called base class.
• Polymorphism: The ability of a function to behave in different ways for different objects is called polymorphism. We have two types of polymorphism: one is function overriding and the other is function overloading. When we need to add more functions to a method that is already defined in the base class, we use function overriding. The name itself makes it clear that overriding a method to add more functionality is called function overriding. In function overloading, we use the same function name for doing different tasks. That means calling same functions to perform different tasks.
• Message Passing: While executing a code, different objects will be needed to communicate with each other. This communication is made possible by passing messages to each other. A message is a request for the objects to invoke functions and generate result. When a message is passed, we should specify the interacting objects and the message to be passed.
So from the above mentioned features of OOPs, it is clear how OOPs ease the coding. These features are so important for a game programming, and hence a game programmer should be thorough with these features and how to use them in programming. Better usage of class, objects, abstraction, encapsulation, inheritance, and polymorphism will let the program save space in memory as well as help perform in an efficient way.
Sample Program with Concepts of OOPs
Class Person: MonoBehaviour
{
public String name;
public Int age;
public void SetDetails (string _name, int _age)
{
name = _name;
age = _age;
}
public void GetDetails ()
{
Debug. Log (“The name of the person is” + name);
Debug. Log (“The age of the person is” + age);
}
}
Class Example
{
Person p1, p2, p3;
p1. SetDetails (“PlayerA”, 30);
p2. SetDetails (“PlayerB”, 27);
p3. SetDetails(“PlayerC”,22);
p1. GetDetails ();
p2. GetDetails ();
p3. GetDetails ();
}
In the above example, we have two classes, one named ‘Person’ and the other one named ‘Example’. The ‘Person’ class is inheriting MonoBehaviour, because of which the class is able to use the function ‘Debug. Log’ (a function defined in MonoBehaviour). Also, we have two variables and two functions. The variables are ‘name’ and ‘age’. The first function ‘SetDetails’ accept two parameters (arguments) and it gives a value to the two variables. The second function ‘GetDetails’ will print the value of the variables in the console. In the ‘Example’ class, we have three objects of the class ‘Person’. We are calling the function ‘SetDetails’ (by passing two arguments) for all the three objects of the class ‘Person’. Thus, the values of the both variables ‘name’ and ‘age’ are assigned by that particular function for all the three objects. Then we call the function ’GetDetails’ for all the three objects which will print the values of the variables for each object. ‘p1’, ‘p2’, ‘p3’ are three objects of the class ‘Person’. ‘GetDetails’ and ‘SetDetails’ are the two functions (methods).
OOPs in Game Programming
Now that we are pretty clear about the concepts of OOPs, let’s discuss how a game works and how OOPs concepts are applied in game programming.
Any game consists of an environment where the game will be played and many objects that interact with each other. The objects include characters of the game, both playing character and many non-playing characters (NPC) like enemies. The playing character and the NPC’s interact with each other, with the environment and with the other objects in the environment. In most of the games, as a gamer progress through the levels, the difficulty of the levels increases. So, the enemies in the later levels should be more powerful than the earlier levels, but most of the properties and functions of the enemies will be the same with little modification in some properties or behaviour.
From the above explanation about a gameplay, it is very clear on how OOPs concepts ease game programming. Here we can have many classes like ‘playerController’ (which will control the movement and explain the behaviour of the character) and ‘enemyInstance’ (which will explain the behaviour of enemies and their properties). In the earlier levels, the enemies shall have some particular properties which may be modified in the later levels.
We can also see the use of Abstraction, Inheritance, Encapsulation, and Polymorphism in games. The data related to the class ‘playerController’ shall be hidden in that particular class (Data Abstraction) and it shall be bound with some functions in the class (Encapsulation). We can have a main enemy class with common properties and functions for all kind of enemies, so that we can inherit this class for the ‘enemyInstance’ (Inheritance). The same function in the enemy class can be called with different values; that is, the same function can be called to perform different actions (Polymorphism). Thus, we can see the use of Class, Objects, Data Abstraction, Encapsulation, Inheritance, and Polymorphism in games.
Given below is an example of using OOPs concepts in games.
Using Systems;
Using UnityEngine;
Using System.Collections; // These are the header files that we use in this program.
namespace ExampleGame.cs // This is the namespace
{
class Game: MonoBehaviour // This is the class Game which inherit from MonoBehaviour
{
Public void Start ()
{
Warrior PlayerA= new Warrior(“PlayerA”,1000,120,40); // Initializing an object PlayerA of the class Warrior with the following properties.
Warrior PlayerB= new Warrior(“PlayerB”,1000,120,40); //Initializing an object PlayerB of the class Warrior with the following properties.
Battle. Fight (PlayerA, PlayerB); // Calling the public function Fight in the class Battle
}
}
class Warrior
{
// Name, Health, Attack maximum, Block maximum.
public string Name {get; set;} = “Warrior”; // The name of the warrior which by default given as Warrior.
public double Health {get; set;} = 0; // public variable health which is a datatype double
public double AttackMax {get; set;} = 0;// public variable AttackMax which is a datatype double
public double BlockMax {get; set;} = 0; // public variable BlockMax which is a datatype double
Random rnd = new Random (); // ability to create random numbers.
Public Warrior (string name = “Warrior”, double health = 0, double attackMax = 0, double blockMax = 0) // Constructor Function. Which is called when the object of this class is made.
{
Name = name; // assigning name
Health = health; // assigning health
AttackMax = attackMax; // assigning maximum attack value
BlockMax = blockMax; // assigning maximum block value.
}
public double Block () // ability to block. (Generate a block attack value from 1 to block maximum)
{
return rnd. Next (1, (int) BlockMax);
}
public double Attack () // ability to attack. (Generate a random attack value from 1 to attack //maximum)
{
return rnd. Next (1, (int) AttackMax);
}
}
class Battle: MonoBehaviour// it is a utility class and hence all the methods(functions) should be static
{
// Use warrior 1 and warrior 2
public static void StartFight (Warrior warrior1, Warrior warrior2)// ability to start a fight. Start Fight between the two warriors. (that is PlayerA and PlayerB)
{
// loop giving each warrior a chance to attack and block until one die. (that is until health of any of the warrior becomes zero.
while(true)
{
If (GetAttackResult (warriror1, warrior2) == “Game 0ver”)
{
Debug. Log (“Game Over”); // if the result is equal to game over (that is any of the warrior is reached to health value 0.) break the loop
break;
}
If (GetAttackResult (warriror2, warrior1) == “Game 0ver”)
{
Debug. Log (“Game Over”) ;// if the result is equal to game over (that is any of the warrior is reached to health value 0.) break the loop
break;
}
}
}
public static String GetAttackResult (Warrior warriorA, Warrior warriorB)// Another function GetResultAttack will tell the winner
{
double warriorA_AttckAmt = warriorA.Attack();
double warriorB_AttckAmt = warriorB.Block();
// Calculate one warriors attack and the others block.
// Subtract block from attack.
double warriorB_DmgAmt=warriorA_AttckAmt -WarriorB_BlkAmt;
if (warriorB_DmgAmt > 0)
{
//If there was damage, subtract value from the health. warriorB. Health = warriorB. Health – warriorB_DmgAmt;
}
else
{
warriorB_DmgAmt = 0;
}
//Print info about who attacks and how much value.
Debug. Log (“{0} Attacks {1} and Deals {2} Damage”, warriorA. Name, warriorB. Name, warriorB_DmgAmt);
//provide output on the change in health.
Debug. Log (“{0} has {1} Health \n”, warriorB. Name, warriorB. Health);
//Check if any warriors health fall below 0.
If (warriorB. Health<= 0)
{
// If so print the winner and send a response that will end the //loop.
Debug. Log (“{0} has died and {1} is victorious \n”, warriorB. Name, warriorB. Name);
return (“Game Over”);
}
else
{
return (“Fight Again”)
}
}
}
}
The above game will return a result as below:
PlayerA Attacks PlayerB and Deals 65 Damage // The PlayerA and PlayerB is given a random value of maximum health. In each iteration, each of the players (Warriors) is given a chance to attack and block (that is a random value of attack which will be reduced from the total health of the players). After several iterations, one of the players will reach the maximum health value equal to or less than 0. So the player who reached the maximum health to zero will die and lose the game while the other one will be declared as victorious. Each time the game is played, any one of the player can be victorious (that is, it is not the same player who will be victorious always, but it depends on the random values that are generated on each iteration of the game).
PlayerB has 79 Health
PlayerA Attacks PlayerB and Deals 18 Damage
PlayerB has 61 Health
PlayerA Attacks PlayerB and Deals 26 Damage
PlayerB has 53 Health
PlayerA Attacks PlayerB and Deals 68 Damage
PlayerB has -7 Health
PlayerA has Died and PlayerB is victorious
Game Over
The above program has three classes named ‘Game’, ‘Battle’, and ‘Warrior’. The ‘Battle’ class inherits from the unity function called ‘Debug. Log’. All these classes are written inside the namespace ‘EampleGame’. Inside the ‘Start’ function of the ‘Game’ class, we have defined two objects of the ‘Warrior’ class. And inside the ‘Warrior’ class, we have a random number generator that will generate a random number for ‘Attack’ and ‘Block’. The objects, ‘PlayerA’ and ‘PlayerB’ of the ‘Warrior’ class are defined with a maximum health value. Inside the ‘while’ loop, each object calls the ‘Attack’ and ‘Block’ functions one after the other. The value ‘Attack’ will be reduced from the health of each object when the ‘Attack’ method is called. The total damage amount is given by the ‘attackAmount’ reduced by the ‘blockAmount’; and once an object reaches zero, the game declares who the winner is and who the loser is.
This program clearly mentions the use of class and objects, and how the objects interact in game programming. Thus, the use of class and objects ease coding and let the program run in an efficient way by using less memory because the class by itself will not occupy any space in the memory and will create only the required objects. The features of OOPs are used well in the program to make the objects work in a better way.
Thus, the use of OOPs in game programming makes coding very much easier than earlier.