Why Inheritance Is Actually Simple (Once You See This)
You've already used inheritance. Probably today. Nobody told you it had a name. Add a Button to a Godot scene, and it arrives with five ancestors: BaseButton, Control, CanvasItem, Node, Object. That chain is inheritance, and it's been doing free work for you the whole time you've been building. In this one, we start with that chain, then build our own from scratch in C#: an Enemy base class with health, damage, and death, and a Slime that inherits all of it without writing a single line of its own. Then overriding, so your Slime can die differently from everything else. And then the bug that got me when I was starting my career: a boss that gets angry, reacts dramatically, and refuses to lose a single HP. Because overriding REPLACES the parent's method instead of adding to it ā and base. is the one thing standing between you and an immortal boss fight. ā± CHAPTERS 0:00 You've already used inheritance 0:45 Why does that chain even exist? 1:50 Writing the Enemy base class 2:45 Making a child class (Slime) 4:18 How other languages spell it (GDScript, Java) 4:35 Overriding: same name, new behavior 5:57 The boss that won't die 7:12 The fix: calling base. 7:56 The is-a test 8:45 Recap + what's next Subscribe for more! -------------------------------------------------------------------------------------------------------------- SOCIALS -------------------------------------------------------------------------------------------------------------- Watch the chaos live on Twitch: https://twitch.tv/professorleo Join the lab's Discord Server: https://discord.gg/Gp87EyBDvD Clips Channel: https://www.youtube.com/@LeoOffHours
Transcript
If you use a game engine, you've probably heard and used something called inheritance, even though you didn't know its name. Today we are going to learn what it is. Say for example, we have this empty scene right here. If I go in here and say, let's just say add a button, look at what composes the button itself. We have the main class, the button, we then have base button, control, canvas item, and then node. That chain is called inheritance. It's running your engine, it's running your error handling, it's been doing the free work for you all this time. Let's actually work with it.
Before we start actually working with inheritance, let's ask a most useful question. Why is that chain there at all? Why isn't button just button? Because a button isn't the only clickable thing. There's checkbox, texture button, link button, and many more. They all need "I can be clicked and I fire a signal" when someone clicks me. Writing that 4 separate times would be miserable and you'd fix bugs in it 4 separate times. So, the engine devs wrote it once in base button and every clickable thing takes it from there. The press signal you connect all the time, not defined in button. It's defined up in base button and button just gets it. That's the whole concept. The class you write it in is a parent. The
classes taking from it are children. And children get everything. Every variable, every function automatically. Now, let's stop looking at other people's work and build one. Every enemy in your game has health. It takes damage and dies. Same logic for all of them. So one parent. For that we are going to define a class called enemy. Okay. Inside of enemy we are going to define three things. A constructor, which will take health. A take damage method. And a die method. Alright.
So, in here we have all three things I said we would define. We have a constructor, which initializes our health as a private variable. The take damage, which calculates the damage you're going to take. And then if the health drops below zero, you basically die. And you emit a simple line in the log saying enemy has been defeated. Now let's write something that will derive from this. A children. In this case, a slime. So in the same class for now, let's just say public class Slime.
Slime. And this is going to derive from enemy. In C#. To declare inheritance, you use a colon. So in this case, slime colon enemy. Slime derives from enemy. Okay. In this case we need to declare that the constructor for slime calls upon base. Base. Base. Is basically saying the base class from which we derive. And yeah. That's basically our slime class. Nothing has been changed. So yeah. Now we can just go into the program. And I'm going to say.
To create a slime. Slime. So we are initializing slime with 10 HP. And I'm going to tell it to take 30 damage. Which is going to be way above what it has for health. And now if I run this. Enemy has been defeated. Because the slime dropped below zero HP. So. In this case I wrote nothing instead of slime. It inherited everything from its base class. This is essentially the same thing that your engine is pulling with button in the case of base button. Except now.
It's made by us. Also to be noted that there is different punctuation and ways to inherit depending on your language. C sharp as I said uses a colon. Java and GDScript use a line saying extends. And then the class name. Alright. I know what you're going to say. If every enemy is the same. Why have two classes or more? That's where the other part of inheritance comes in handy. Overriding. So if I go into the enemy class. And say for example. That the die method is a virtual. Now I can override this method in the slime class.
So I can just go in here. And say override. And it also suggests die. And from here I can just remove this slime. And say. Like so. Now instead of saying enemy has been defeated. It will say slime has been defeated. If I just run this. There we have it. Slime has been defeated. This is a simple principle of overriding the parent's methods. You can also do this in most components in your game engine. It's the same name. But slime has a different version. Everything else.
The health. The take damage. Everything still comes from the parent. Untouched. And that's the shape of the whole thing. Shared behavior. Up top in the parent. Differences down in the children. That's what the button chain is doing. I can be clicked up top. I have a check mark down the bottom. Now the part that will bite you. Because it bit me a few times when I was starting. Imagine you have a boss fight. You have to create a boss enemy class. You want the boss to take damage. But also trigger a dramatic angry reaction. So obviously you're gonna do something like this. So essentially you overwrite the take damage void. And you execute the new logic you want. To add that dramatic flare for your boss. Now
let me rewrite the program a bit. So it can be demonstrated a bit better. Okay. In this case I rewrote the program. To show the boss HP. And your actions to take. Where flee is to basically. Get out of the program. So now if I run this. You now see boss HP is 100. Choose to attack. The boss is angry now. But the HP didn't go down. And why is that? Well if we go back to the enemy class. We can see that while overwriting the take damage. We also are not calling the part about calculating the damage. And taking it from the health. For that we need to call base .takeDamage. And passing the amount. Like this. Now it will take damage. So yeah. That's one of the things you need to be careful with. If you want
to still execute the base class's logic. You need to use base. And then the method. Otherwise you're just going to be replacing it. So if I now run this. And choose to attack. As you can see. It now has 90 HP. So one last thing. And this is what keeps inheritance from turning into a nightmare six months from now. If I write extends or basically implement inheritance. Say it out loud. A slime is an enemy. Is it true? Yes. Then inherit it. A button is a base button. Is it true? Yes.
Then it's an inheritance. This is called the 'is-a' test. And it's very useful in the future for inheritance. But if you're only inheriting because a parent happens to have a function you want to borrow. That's not how you do it. That's when you give your class the other thing as a variable instead. Which is a whole other video. So you were already using it. Now you know its name. You know its a parent handing stuff down. You know overriding replaces instead of adds. And you know base or super is how you get your behavior back. Go look at the inherits line on any node in your project. And I guarantee that chain is longer than you expect. Tell me the worst one you find in the comments.
If you enjoyed the class do leave a like and subscribe. It helps me know you want more videos like this. And helps keep the lab open. For now class dismissed. I'll see you next time.