Classes in C# using Unity
What are classes, custom classes and class inheritance? In this article, I will dive into those questions and more. Class is in session!
A class is basically a script, and if you are familiar with Mono Behavior in Unity, then you are already familiar with class inheritance. Mono behavior is what Unity provides so that users can drag and drop scripts on to game objects, which is important for object oriented programming. Attaching a script to a game object for functionality like movement, UI, shooting, etc., is possible because the attached script is inheriting mono behavior, from Unity.
Custom Classes
A custom class is a developer defined class, which is can be used for creating modularity, and storing relevant information for other classes inheriting from the custom class. For this first example, let’s start with a custom class called Weapon Stats, which will be used as a base class to define what shared variables are needed to create the most basic of weapons. Before diving into doing this via a new C# script, let’s add this as a custom class in the Player class, where the player will have direct access to the weapon stats.
Here at the top of the Player class, a new public custom class is declared for Weapon Stats. You can see at the bottom of the image where the player scripting begins. This custom class will be the foundation for any info needed that is related to stats for various weapons. All weapons will have a name, a fire rate and an ammo count, so those are all stored in variables.
In the player class, variables for new weapons can be created by declaring them as Weapons Stats, which uses the custom Weapons Stats class above the player class. Here I introduce two variables for new weapons. One being a plasma rifle, and the other being a rocket launcher.
Now in void start, the rocket launcher can be created by declaring it as a new Weapon Stats. After that, the rocket launcher can be referenced, then dot notation can be used to assign values to the variables in the custom class, such as name, fire rate and ammo count. Now the rocket launcher has been instantiated, and all of it’s information has been initialized.
The problem with this method, is that you would have to do this for every weapon you want to create. If you have 50 weapons in your game, this could get very long winded and tedious. To really utilize the power of custom classes, it’s recommended to initialize objects using a constructor.
Constructors
Constructors are an amazing way to initialize objects from the custom class. A constructor uses a public declaration as well as the same class name of the custom class that it is declared in. Then the variables can be initialized in the constructor with whatever values you want. The main problem here, is that this will hard code this information into the custom Weapons Stats class, which is best to avoid.
A better solution is to add parameters to the constructor method, requiring the user to enter the values upon weapon creation. Now rather than hard coding the information, the user will have the opportunity to pass in the values they want when creating a new weapon. For clarity, this.name, is referencing the name variable in the custom class, and then assigning it the value of the name being passed in through the constructor method parameters.
Now the rocket launcher declaration in the player class is upset, because it is looking for those parameters to be passed in here. Let’s take care of that.
Now in void start in the player class, the user can pass in the values they want in the method parameters when creating a new weapon. What if you wanted to customize these values in the inspector? Well, let’s take a look at Serialized Custom Classes.
Serialized Custom Classes
Let’s take a look at creating an item database. First thing we need is a custom class that does not inherit mono behavior, called Item. Then we can declare the basic variables that all items will share. In this case there are variables for ID, name, description and an icon.
The next step is to create an empty game object called Item Database, as well as a corresponding C# script with the same name. Drag the Item Database script which inherits mono behavior, on to the Item Database object to assign it.
Item variables can be declared by referencing the Item class, and then the items can be created and initialized in void start, just like with the previous weapon class. Once again, this could become tedious if you have a lot of items to create. Let’s streamline this by adding a constructor in the custom Item class.
A custom class can have multiple constructors. The first empty constructor method will enable the user to create a blank item, and then initialize it in void start on the Item Database script. The second constructor uses method parameters that need to be met in order to create an item.
Returning to the Item Database class, the dagger is initialized using the constructor that requires parameters, while the sword uses the empty constructor and then defines the values after.
An even cleaner way to create items, is through a return method with parameters. The var data type in the return method will detect that you are trying to create a new Item. Alternatively you could also type Item item. The shield item is created by declaring it’s variable, assigning it’s value to be that of the Create Item return method, and then passing in the needed parameters. Now that we have looked at three different ways to create items, let’s serialize this data to be viewable in the inspector.
Even though the dagger and sword items are public, they won’t appear in the inspector without making them visible through the base Item class. This can be done by putting [System.Serializable] at the top of the Item class.
Now the public Items in the Item Database will be viewable in the inspector. The shield is still private, so that will need an additional Serialized Field attribute.
Now the created items are viewable in the inspector.
When the play button is pressed, the information is assigned via the constructors and initialization.
Being that the base Item class is now serialized to the inspector, you can bypass hard coding in all of your items and instead give full control to a designer via the inspector. All you need is an array or list of items on the database, using the custom Item class. The foreach loop is going to tell me the name of each item currently in the array, but is not needed to make this work.
In the inspector, the amount of items in the array can be assigned (3). Then the array will populate that many elements for a designer to fill in the blank information.
Challenge: Customer Database
I have been tasked with adding customers to a database. Let’s use what we have learned so far, and create a custom class to represent the most basic attributes of a customer.
Two scripts are created, Customer and Customer Database. Customer will be the custom class, while Customer Database will be attached to an empty game object, while inheriting Mono Behavior.
Starting at the top, this custom class is serializable to show in the inspector. This public class does not inherit Mono Behavior, and has a Customer Attributes header. Those attributes include a customer ID, first and last names, age, gender and occupation. At the bottom is a constructor method that takes parameters for creating a customer.
The Customer Database class showcases an array of customers, as well as three more customers which are hard coded into the system. There is a Create Customer return method that takes parameters. These customers are initialized in void start by utilizing the return method.
Class Inheritance
We previously setup an Item class that has the base traits that all items share (ID, name, description and icon). Now other items that need more information can be created in their respective classes, while inheriting the base traits from the Item class. A new class called Weapon is created to handle the creation of weapon items. Another new class called Potion is created for managing various potions.
The main thing to note here, is that neither one of these scripts inherit from Mono Behavior, but instead inherit from Item. This way both the Weapon and Potion classes will have the same base information provided with Item (ID, name, description, icon). The Weapon class has more variables to represent things related to weapons, that aren’t needed for all items (attack power and heft weight). The Example Method shows that in the Debug message, the itemName variable can be accessed from the Item class.
The Potion class only has a string variable to tell us what the effect of the potion will be. This would be a good place to also include an int value related to health restoration.
In the Item Database class, two public variables are declared for the new classes. There is a Saber Weapon and a Potion of Health. You could also have an array or list of Weapons and Potions just like with the Items example.
Because the Weapon and Potion classes are using the System.Serializable attribute, the new Saber and Health potion can be seen and have their values adjusted in the inspector.
Bank System: Inheritance Example
Here is a custom Bank class that can be inherited by a Bank Manager script. All banks share the basic attributes of a branch name, address and how much cash they have in their vault. Three methods can be called to check the balance of an account, withdraw money, or make a deposit. The Debug message will say the name of the branch calling the method.
The Bank Manager class inherits from Mono Behavior so it can be attached to bank objects in the scene. A Bank variable will give each bank item all of the details and methods from the Bank class, and is housed under a branch details variable name.
The Federal Credit Union class inherits the base attributes from Bank, but also adds a value for lending, as well as a method to ping when your loan is approved.
A Federal Credit Union Manager class is created to be placed on the bank game object, and uses the FCU class, which inherits from Bank, for all of it’s details.
Cube primitives are created in the Hierarchy to represent some different known banking institutions. Bank of America, Wells Fargo and Bank of Hawaii all get the Bank Manager script attached, while Hawaii Federal Credit Union gets the FCU Manager script. These game objects have colliders on them, so all of this banking info could be accessed by the player on collision, as they enter the bank.
Each bank can have their unique information assigned in the inspector.
The FCU has the additional value for Available Money to Lend.
Protected Access Modifier
While a private declaration makes a variable or method only accessible from within it’s own class, and a public one gives access to any script, what does the protected access modifier do? Protected will keep things private, except for the classes that are inheriting from it. Making these variables and methods protected in the Bank class, means that only the Bank Managers inheriting from Bank, can access the data. Even though the System.Serializable attribute is present at the top of the Bank class, you will still need to serialize the individual fields to populate them in the inspector.
Virtual Methods and Overrides
There might be times that you need to override the data in one script with another. Let’s take a look at how to do that with a Pet system. This Pet class will be the base class for all pets, inheriting from Mono Behavior so that it can be placed on Pet objects in the scene. There is a protected name, so that only inheriting classes can access it, as well as a protected virtual method. Virtual methods can be used to store data that can be overwritten when accessed from another script. In void start a Speak method is called, which calls the virtual method and sends a Debug message to the console of “Speak”.
Two cube objects are created for a Dog and a Duck.
Each animal has it’s own script attached.
The Duck and Dog classes inherit from Pet, and have an override method. Because the virtual method is protected, the override methods also need to match for this to work. Because Pet is the base class, if you call base.Speak(), the message to the console will print “Speak”. If each animal sends a message in the override method speaking it’s own language, the printed message will be overwritten per animal to reflect their native tongues.
When the program runs, each Pet sends a debug message that overrides the original message in the Pet class!
Structs and Memory Management: Value Types vs Reference Types
To get started with this exercise, an empty game object is created with a matching Struct Example class.
A Struct is a lot like a class, only with some limitations and a very slight performance bump. Unlike a class, a Struct can not use class inheritance. The number of fields you use should be less than 4, and the data should be immutable (unable to be changed). A Struct is a Value Type, which sits on the Stack, while a Class is a Reference Type, that sits on the Heap. A Value Type (bool, float, byte, char, double, int, long, struct) directly stores it’s own data. A Reference Type (string, array, class, delegate) points to a memory address on your hard drive where the data is actually stored, rather than storing the data directly. Let’s do an exercise to see how they function differently.
Above the Struct Example class, there is a Struct(Item2) and a Class(Item3) with seemingly identical functionality. Both have a constructor method that takes parameters for creating items.
At the top of the Struct Example class, a small shield is created using the Item2 struct, while the war axe is created using the Item3 class. One notable difference here, is that the small shield doesn’t need a new declaration to be created, because it already has it’s own information as a value type. The war axe on the other hand does need a new declaration, because it is using a class (reference type). In void start, the small shield can be accessed, and the base attributes can be assigned. To test this out, there are two methods being called to change the name of the item. One method for the struct and one for the class. There are debug messages sending the name of the item to the console, before and after the Change Value methods are called.
The war axe is the class based reference type. Here you can see that the name of the item is war axe, but after the method the name becomes Barbarian Axe. The data was successfully changed. The struct based value type however prints out small shield as the name before and after the Change Value method is called. The data was not successfully changed. In order to send a debug message with the changed value name, I had to do it from within the Change Value method itself. So rather than changing the actual value of the shield, it made a copy of it instead.
I hope you enjoyed this lengthy dive into classes and class inheritance. Thanks for reading along and happy coding!
8 responses to “Classes and class inheritance in Unity C#”
Its not my first time to pay a visit this web site, i am browsing this site dailly
and obtain pleasant facts from here all the time.
Thank you! Please keep returning for new content!
Wow! At last I got a blog from where I be able to really take useful
facts regarding my study and knowledge.
Thank you! If there are any topics you would like covered, please let us know. There may already be an existing Blog on the subject, and if not one could be made.
Love the Detail in these tutorials
Thanks Carrot!
I am not sure where you are getting your
information, but great topic. I needs to spend
some time learning more or understanding more. Thanks for fantastic
info I was looking for this information for my mission.
Everything is very open with a clear explanation of the
challenges. It was truly informative. Your website
is useful. Many thanks for sharing!