Lesson 26 - Introduction to Polymorphism

Tutorial Series: Introduction to Unity with C# Series

Previous Article  |  Next Article


Okay, one final thing I would like to cover before we move on and do some Unity- specific programming, is touch up on the third part of the OOP Holy Trinity of defining principles. In Object-Oriented Programming, there is said to be 3 main principles or pillars that make a language Object-Oriented. We've already looked at 2 of these pillars of OOP. We've looked at encapsulation. All of those containment structures, classes, object containment/composition and so on. Inheritance, which is pretty self explanatory and all we have have yet to cover is called Polymorphism. Which sounds like an incredibly scary term but isn't too difficult to understand. Polymorphism simply refers to aspects in code that can take many forms, hence 'poly' and 'morphic'. We'll look at that in this video but before we move forward I want to actually make some changes to where we left off and go back to ... Originally we're inputting a knight, so we'll change that reference back to a... taking a knight object for the Deflect() method. We'll comment out reference to the string, since we're no longing passing in just the string. We'll comment back in the previous code.

I will put a little comment here. For future reference, and back in our RPGController, we'll want to also put back the references to the knight object. So here and we also made reference here too. Alright. Combing it to make sure everything's kosher. Yep. We're all good. Now, we'll make a new method to illustrate polymorphism. Get these out of the way. Call it Polymorphism() We'll write in something for that shortly. We'll call that here. In the previous video, we saw how we made this method that takes in the Knight that deflect his weapon and so on but what if you want to use this very method; the Deflect() method here. What if you want to use it performing the same basic process but instead allowing you to input a Wizard or any other any class that you may be using in your game. You'll have a lot of duplicate code, if you make different methods, each just taking in a different object type as an input parameter. Polymorphism comes to the rescue in exactly this case. Let's start by re-working our inheritance tree, so that our classes now inherit from a common base class instead. Let's make a common base class called Combatant and we'll give that base class a Weapon.

Now, what we'll do, since that'll be inherited now, we can get rid of these Weapon fields for each deriving class, inheriting class in other words. We'll have Wizard inherit from Combatant and Knight also inheriting from Combatant and why not also make an Elf class, a new class. We can make as many as want. I'm going to stop at 3. Actually, just to make it a little bit more compact, we don't have any implementation details for these classes. So just have empty parentheses all on one line. Sorry, not parentheses, I mean squiggly brackets, containment brackets. Now, with that inheritance structure in effect, let's change the Deflect() method to take in any kind of Combatant, giving us that polymorphism that we've been talking about. Combatant. We're also going to want to change referencing Knight. Now we know it's just going to be any kind of Combatant. Here as well. Now, since a Wizard, Knight or Elf are all equally Combatants. Remember the way you think of inheritance, an Elf is an a Combatant, a Knight is a Combatant and a Wizard is a Combatant. You can input any one you want to get the same functionality without requiring any extra code. Let's just pull some of the code that we already wrote just for simplicity's sake, here. What are we going to need? We'll need that. Why not, let's just run that and see if it outputs the way we were hoping, with polymorphism now in play. Here we go. Broken Sword, so it correctly took in that knight object that is holding the Sword. Instead of just a Knight being input, now we just allow it to input a Combatant.

To prove that this is working the way we're saying it is, let's make now an Elf for example. Let's just do it here and give that Elf a weapon type. Say Arrow. Just go back to simply outputting a simple message and now if it works all correctly, we expect it to show what? "You deflected the Elf's Arrow and degraded your weapon to 70." To do this a slightly different way, let's now store our object, either Wizard, Knight or Elf into a Combatant object. Let's do this. This also has some... harkens back a bit to our look at reference types. We don't need to make a new Combatant, we simply assign to this Combatant, say Elf. And run it. There we go. Still has the same message. We input a Combatant that we know is of, specifically of type Elf and as the base type is of Combatant. That's why that works. Polymorphism let's you therefore specify the variable type as the broader base type, which then allows you to use the same variable. In this case a Combatant, even if we reference Knight, it will still work. Just to prove it you. "Deflected the Knight' Sword and degraded your weapon to 70." It allows you to use the same variable to reference objects of different types but nonetheless share the common base type, such as in this case here. This works quite easily as long as there isn't anything Elf or Knight specific that you want to reference with this variable, the Combatant variable right here. It just references the Combatant's Weapon, which is common to all theCombatants.

We don't have any specific code, specific to Wizards, Knights and Elves in our Wizard, Knight and Elf classes right now. If we did, you wouldn't be able to reference it because we're treating it just as it's base type, as a Combatant. We know that, that allows it to have a Weapon and so far nothing else that's common to each type. I have a bit of analogy for this. It's kind of like if you were to give out payment to somebody. You don't ask if this somebody is a female or a male. It doesn't matter whether it's a female or male because the process of paying people is the same. In other words, if you made a method for this, say void Pay(), it could take in the common base type of type Person, which is all you're concerned with when you're making payments. You could input either a male or female into this payment method, because it both equally inherit from type Person. Forget about the differences. They're not relevant here. If you treat them both as a Person, you pay them the exact same way. See what I did there? If you were to try to reference the Person's say chromozone properties, which are field-specific to their malesness or femaleness, that would be irrelevant in the context of the Pay() method that is treating them as the broader base type of type Person. Now, if it's something like a MakeBaby() method, maybe you need to treat those two objects as more specific, as a male or female but certainly not when making a payment to those objects. Polymorphism makes sense I hope.

Let's take this even step further. Is there even a broader base type that Combatant inherits from? Well, it doesn't appear to be. We don't have the colon after Combatant. How's this for craziness? Since classes in C# all are objects, they implicitly inherit from the broadest object type which is just object. You can even do this right now. You can make this of type object and then in RPGClasses here, let's just make a method. We're not really going to use it but just for illustrative purposes, it takes an object. Call it anObject, local to the method. Now, because it's an object we don't really have a lot available to us as an object in this method. We'll come back to that in a second. For now, let's not really actually do anything, let's just say do something that damages the object. Pretend that it's got some code there that is actually is useful and damages the object and... Let's actually fix this. Let's go back to Combatant. Here. Let's go object anObject = knight, or elf, whatever. Give equal opportunity to the elves out there. Now that we have a method that takes in an object, I'm not going to write all the code here but just to illustrate, let's take our wizard and part of his weapon, now he has a DamageSomething() method that takes an object. Let's input that object. This doesn't seem immediately useful, so let's actually ... Let's say we have 2 objects that don't inherit from a common base type.

Let's say you have something like this. Make another class that's not common to the Combatant class but is an object nonetheless. Let's say public class NPC and another one public class Companion that inherits from NPC. These are all objects, NPCs, Companions, Combatants, Wizards, Knights, Elves but they don't share common inheritance, other the most basic common base type of type object. Now, going back here, we can input into this method. Well, let's just get that out the way. We already have an elf. Let's reference it there. An elf at the base is an object, so that's perfectly legal and we can also do something like this. Imagine the scenario where this might actually be useful. spiritFamiliar = new Companion() Then again, we'll call that DamageSomething() method and this time we'll damage the spiritFamiliar. Which works because we're just treating it as an object when it gets input into the DamageSomething() method. There's no real output here, so I'll just build it to show you that there is nothing wrong with this code. Again, this allowed to work this method here, taking the object, either a Combatant or an NPC, as long as it's referencing something that's common to the base type here because we're treating this just as an object.

Now, that shows you the flexibility you get with polymorphism. Bear in mind as I said earlier, when you use this kind of polymorphism and treat an object just as it's base type, in this case it's of type object, which is the broadest type around. You can't do much with just objects. You won't be able to access the particular fields and methods that relate to the particular type. Unfortunately, if the Companion class has a Damage() method, it won't be accessible when you treat it just as a type object. In these cases, you'll have to downcast to the more specific type of Companion or Combatant or a more specific type of that - a Wizard, Knight or Elf for example - with a process called unboxing. Now, unboxing just checks to see if the objects specific type is of a certain type, say a Companion and cast to that type. You can then treat it as that specific type, with it's own specific Damage() method. You can achieve this casting, with a series of if statements where it checks if it is a Combatant, cast to Combatant and do this. If it is a Wizard, cast Wizard and do this. Otherwise, if it's an NPC or Companion, do that. Something like that would be how you'd solve that problem. It still have the benefit of having this generalized method that you only write once. It can take in as an input a variety of a more broader base types. The broadest again being of type object.

Now, this all might be a little bit confusing at this point. It probably should be. If you're not confused at this point, you've confronted these topics before, I'm quite sure. It's far field from where we're at right now. I admit that. However, I just want to mention it, so you don't scratch your head wondering about it too much, when we first make use of it in our project. I want to cover polymorphism before we start stepping away from pure C# and OOP. I know we'll come back around to this topic in later lessons, so don't sweat it if you don't get it all right now. But thanks for following along. And I'll see you in the next lesson.

Related Articles in this Tutorial:

Lesson 1 - Who This Course is For

Lesson 2 - What to Expect from this Course

Lesson 3 - Installation and Getting Started

Lesson 4 - Starting the First Project

Lesson 5 - Prototype Workflow

Lesson 6 - Basic Code Review

Lesson 7 - Game Loop Primer

Lesson 8 - Prototyping Continued

Lesson 9 - C# Fundamentals and Hello World

Lesson 10 - Variables and Operations

Lesson 11 - Variables and Operations Continued

Lesson 12 - Floats, Bools and Casting

Lesson 13 - If Statement Conditionals

Lesson 14 - If Statements Continued

Lesson 15 - Complex Evaluations and States

Lesson 16 - Code Syntax vs. Style

Lesson 17 - Variable Scope

Lesson 18 - Object-Oriented Programming Intro

Lesson 19 - OOP, Access Modifiers, Instantiation

Lesson 20 - Object Containment and Method Returns

Lesson 21 - "Has-A" Object Containment

Lesson 22 - "Is-A" Inheritance Containment

Lesson 23 - Static Fields and Methods

Lesson 24 - Method Inputs and Returns

Lesson 25 - Reference vs. Value Types

Lesson 26 - Introduction to Polymorphism

Lesson 27 - Navigating the Unity API

Lesson 28 - Applying What You've Learned and Refactoring

Lesson 29 - Constructors, Local Variables in the Update Method

Lesson 30 - Collecting Collectibles, Items and Powerups

Lesson 31 - Spawning and Managing Prefab Powerups

Lesson 32 - Implementing Powerup State Logic

Lesson 33 - Displaying Text, OnGUI, Method Overloading

Lesson 34 - Referencing Instantiated GameObjects, Parenting

Lesson 35 - Understanding the Lerp Method

Lesson 36 - Creating Pseudo Animations in Code

Lesson 37 - Understanding Generic Classes and Methods

Lesson 38 - Animations Using SpriteSheets and Animator

Lesson 39 - Working with Arrays and Loops

Lesson 40 - Debugging Unity Projects with Visual Studio

Lesson 41 - Camera Movement and LateUpdate

Lesson 42 - Playing Audio Clips

Lesson 43 - Routing Audio, Mixers and Effects

Lesson 44 - Adding Scoring Mechanics and Enhancements

Lesson 45 - Scene Loading and Game Over Manager

Lesson 46 - Understanding Properties

Lesson 47 - Controller Mapping and Input Manager

Lesson 48 - Understanding Enums

Lesson 49 - Dealing with Null References

Lesson 50 - Handling Variable Framerates with time.DeltaTime

Lesson 51 - Preparing the Project for Final Build

Lesson 52 - Final Build and Project Settings

Lesson 53 - Introduction to the Unity Physics Engine

Lesson 54 - Understanding FixedUpdate vs. Update

Lesson 55 - Movement Using Physics

Lesson 56 - Attack Script and Collision Events with OnCollisionEnter2D

Lesson 57 - Projectiles and Stomping Attack

Lesson 58 - Parallax Background and Scrolling Camera

Lesson 59 - Infinitely Tiling Background Sprites

Lesson 60 - OOP Enemy Classes

Lesson 61 - OOP Enemy Classes Continued

Lesson 62 - Trigger Colliders and Causing Damage

Lesson 63 - Multi-Dimensional Arrays and Procedural Platforms

Lesson 64 - Finishing Touches

Lesson 65 - Series Wrap


Please login or register to add a comment