As discussed in the previous tutorial, we are given some tools by the Lua language natively to make our lives easier. These are variables, functions, if-statement, loops, etc. For simple scripting, these were good enough. However, as it is the case with any other tools, given the right tools, people start building things that are even more complex. And to combat ever-growing complexity in our code, people have developed different styles of coding. There are a number of approaches such as Procedural, Imperative, Functional, Object-Oriented, etc. Each approach has its pros and cons and should be used according to your needs. I generally use Object Oriented Programming style for most of my code base.
It is a really difficult concept to grasp. It took me many months to truly understand what OOP is about and how to properly utilize them, if not years. Do not be discouraged if you don’t understand this concept. It is probably because I fail to explain it properly. There are many other resources out there on the Internet. I highly recommend learning the OOP if you want to be a programmer as it has dominated the software engineering scene in the past 30-40 years and made a profound impact in our daily lives. It’s that good.
Object Oriented Programming in General (not related to Lua)
By now, you should be familiar with functions. One of the main reasons why you use functions is to break down some repetitive code, encapsulate it into a function so many other parts of your code base can “call” the function to utilize the same code. If done well your code looks very neat and readable.
local myPosition = GetPrimaryPosition(character)
local cameraAimedPosition = GetCameraAimedPosition()
local hitPart = RayCast(myPosition, cameraAimedPosition)
We have 3 functions here that are self explanatory as to what these lines are doing just by reading the variable names and the function names. All of those functions are fully reusable by any other code in your game. Likewise, it is absolutely necessary to break down your code into reusable parts if your game logic gets beyond some level of complexity. So let me ask you this question. How do you organize your functions in Roblox? In most cases you would have them sit in individual Scripts, Local Scripts, and Module Scripts.
Object Oriented Programming is nothing but a way to organize not just functions but variables as well. In a nutshell, with OOP, you get to choose any number of variables and functions, mark them to be part of one cohesive bundle and re-use them over and over just like how you would have done with a single function above. Because you are bundling up a multiple co-related variables and functions together, you are able to handle much more complex things that happens in your game with this bundle as opposed to a single function which maybe too difficult to do and end up writing spaghetti code.
Class
So we now know in OOP, you are supposed to bundle up a bunch of variables and functions and mark it as “these belong with each other as they are related”. How do you do this? This is done by defining a Class. You have been doing something similar. When you define a function
function SomeFunction()
first line that does something
second line that does something
third line that does something
...
last line that does something
end
You are essentially bundling up all of those lines together as a set of related commands that exist to do one specific task by putting them into one function. When defining a class, you do exactly this with bunch of variables and functions and put them together in one place. I will write how it would look like without using Lua, just to show the idea.
class Warrior
-- variables
local Health -- number value
local Armor -- number value
local Damage -- number value
-- functions
function Constructor(health, armor, damage) end
function TakeDamage(rawDamage) end
function BuffArmor(buffAmount) end
function GetHealed(healAmount) end
end
Here we have defined a Warrior class with 3 variables and 3 functions. (I know the above code looks very similar to Lua but I did that just to make it look familiar to everyone from Roblox) Above function is not completed at all because none of the functions do anything. They are just empty. Let’s go ahead and fill in the gaps for the functions TakeDamage, BuffArmor, and GetHealed. Remember though, since we are working on a “Warrior” class we have to consider what it means to be a warrior when those functions are called.
class Warrior
-- variables
local Health -- number value
local Armor -- number value
local Damage -- number value
-- functions
function Constructor(initialHealth, initialArmor)
Health = initialHealth
Armor = initialArmor
end
function TakeDamage(rawDamage)
local actualDamage = rawDamage - (Armor + 10)
Health = Health - actualDamage
end
function BuffArmor(buffAmount)
Armor = Armor + buffAmount
end
function GetHealed(healAmount)
Health = Health + healAmount
end
end
I feel I need to say this again. Above code will not work in Roblox. That is not Lua code and I am writing that just to explain the idea behind what a Class is in OOP. Let’s go through the functions one by one.
- When a Constructor function is called and given 2 number values as parameters, we are simply assigning them to our local variables that is part of our class.
- When TakeDamage function is called and given the rawDamage amount as a parameter, we calculate the actual damage taken after armor value is applied. Because this is a Warrior class we give it a + 10 armor bonus. We apply the damage and update the local health variable.
- When BuffArmor function is called and given the buffAmount as a parameter, we add it to the local armor value.
- When GetHealed function is called and given the healAmount as a parameter, we add it to the local health value.
So our class is ready. I want to use this class to create bunch of enemy NPCs and keep track of their current health and armor. This is where creating objects come into play.
Object
If “class” is just a way to bundle related variables and functions together and mark them as such, “objects” are the actual instantiations of that “class”. Let’s go ahead and create 2 objects from the class we created above. (Again not using Lua, I will show how I do all this in Lua langauge in the next tutorial)
class Warrior
-- whatever we have up there
...
end
local warriorNpc1 = Warrior.New(100, 10)
local warriorNpc2 = Warrior.New(1000, 100)
Right away we see something strange. What is this “New” function when it doesn’t exist anywhere in our Warrior class? When the New function is called in OOP, it calls the Constructor function for you inside the class definition. When an object is created using the New function we say that an object is instantiated based off of its class. So we have instantiated two objects with different initial health and armor values. Not only that, each of these instantiated objects also have functions defined in your class that can be called. Now when I do this
warriorNpc1.TakeDamage(30)
print(warriorNpc1.Health)
What do you think will print? 90 is the right answer. Let’s write some more code after those lines.
warriorNpc1.TakeDamage(30)
print(warriorNpc1.Health)
warriorNpc2.TakeDamage(200)
print(warriorNpc2.Health)
print(warriorNpc1.Health)
When you execute these 5 lines of code, you will see that it will first print 90, then 910 and finally 90 again. What this tells us is that when you let warriorNpc2 take some damage, warriorNpc1 is completely unaffected. Why would this be the case? This is because at the time of object instantiation each of these warrior npcs were created with their own set of variables and functions that we defined in our class “Warrior”. In other words, the defined class is the blue print and the objects are the products of that blue print which was created based on it. This is exactly how I keep track of individual enemy NPCs in my game. They are all an instantiation based off of a class and their own health, movement speed, damage, armor, and many other things are kept track individually. If you create 1000 objects of the Warrior class because you want to have 1000 enemy warriors in your game, you can see how OOP shines here as an excellent way to self manage health and armor values of these NPCs.
It may be frustrating to realize none of the above code can actually be experimented with in Lua. But I wanted to get across the theory / idea behind the Class and Objects in OOP before getting into using it in Lua. I will use the upcoming tutorials to actually write a part of my current game using OOP in Roblox.
Conclusion
OOP is just one way of organizing / modularizing your complex code. By defining a class in your code, it allows you to mark a collection of variables and functions as “one related thing” in your game. By instantiating objects based off of the class, you are initializing initial values for the object variables at the time of its birth. You are also able to call any of the functions defined in the class by calling it from the object, as these get attached to the objects at the time of its birth as well.
Contact
You can find out more about my discord and any other contact information about me on my twitch channel.