Help with inheritance in OOP

After learning OOP in Python I came back to Roblox and was able to finally understand how it works however I am still a bit confused mainly with inheritance.

So I currently have this

NPC = {}
NPC.__index = NPC

function NPC.new(Name,Gender,Age,HP,MP)
	local NewNPC = {}
	setmetatable(NewNPC, NPC)

	NewNPC.Name = Name
	NewNPC.Gender = Gender
	NewNPC.Age = Age
	NewNPC.HP = HP
	NewNPC.MP = MP
	
	function NPC:Info()
		local Pronoun = ""
		if self.Gender == "Male" then
			Pronoun = "he"
		elseif self.Gender == "Female" then
			Pronoun = "she"
		end
		print("This is "..self.Name.." "..Pronoun.." is "..self.Age)
	end
	
	function NPC:ChooseClass(Class)
		print(self.Class)
		self.Class = Class
		print(self.Class)
	end

	return NewNPC
end

return NPC

And then I made a second module called Warrior

NPC = require(game.ReplicatedStorage.NPC)

Warrior = {}
Warrior.__index = Warrior
setmetatable(Warrior, NPC)

function Warrior.new(Name,Gender,Age,HP,MP,PhysicalDEF) --Warrios can have defense against physical atacks
	local NewWarrior = NPC.new(Name,Gender,Age,HP,MP)
	setmetatable(NewWarrior, Warrior)
	
	NewWarrior.PhysicalDEF = PhysicalDEF
	
	function Warrior:Strike()
		print("STRIKE")
	end
	
	return NewWarrior
end

return Warrior

And lastly I have my main script

local NPC = require(game.ReplicatedStorage.NPC)
local Warrior = require(game.ReplicatedStorage.Warrior)

--Name, Gender, Age, HP, MP
Person1 = NPC.new("Dom","Male","27",80,120)
Person1:Print()

Now say I wanted this NPC to be a warrior and it to be able to use the strike function how can I do that? Would I need to do Person1 = Warrior.new() instead?

1 Like
  1. You have the class methods inside the new constructor so it’s making new functions everytime you call it, the point of the metatable is so that you can have the functions for a specific class and be able to call them with the Object you create with new (without making a million functions)

  2. You made the warrior class but never called it

  3. In Warrior.new you use NPC.new, which is fine it will just create another “NPC” object, but you don’t have to use NPC.new because you set the metatable to NPC anyway.

I am very new to OOP, so this is just a guess.
Maybe you need to set the metatable after defining the Strike() method?

A proper setup would look like

NPC = {}
NPC.__index = NPC

function NPC:Info() -- method to use on NPC objects
	local Pronoun = ""
	if self.Gender == "Male" then
		Pronoun = "he"
	elseif self.Gender == "Female" then
		Pronoun = "she"
	end
	print("This is "..self.Name.." "..Pronoun.." is "..self.Age)
end


function NPC:ChooseClass(Class) -- another method to use on NPC objects
	print(self.Class)
	self.Class = Class
	print(self.Class)
end

function NPC.new(Name,Gender,Age,HP,MP) -- "NPC" constructor (aka, creates the "object")
	local NewNPC = {}
	setmetatable(NewNPC, NPC) -- doesn't matter when you set the metatable as long as it's in the constructor

	NewNPC.Name = Name
	NewNPC.Gender = Gender
	NewNPC.Age = Age
	NewNPC.HP = HP
	NewNPC.MP = MP
	
	return NewNPC
end

return NPC

Warrior

NPC = require(game.ReplicatedStorage.NPC)

Warrior = {}
Warrior.__index = Warrior
setmetatable(Warrior, NPC)

function Warrior:Strike()
	print("STRIKE")
end

function Warrior.new(Name,Gender,Age,HP,MP,PhysicalDEF) --Warriors can have defense against physical atacks
	local NewWarrior = NPC.new(Name,Gender,Age,HP,MP) -- don't have to make an NPC object but can if you want to (could speed up the process so you don't need to write code twice)
	setmetatable(NewWarrior, Warrior) -- since Warrior is set to NPC, NewWarrior will have the methods of NPC and of Warrior
	
	NewWarrior.PhysicalDEF = PhysicalDEF
	
	return NewWarrior
end

return Warrior
1 Like

Ooooh ok I think I got it now or at least I just realized instead of doing

Person1 = NPC.new("Dom","Male","27",80,120)

I can do

Person1 = Warrior.new("Dom","Male","27",80,120,50)

And then I can do

Person1:Info()
Person1:Strike()

yes that should work

The only question I have left is when would I need to use OOP?

I’m no master at OOP yet, but so far I’ve seen OOP the most useful for making Libraries among other things.

For example have you ever used Instance:FindFirstChild() or Instance:Destroy()

You can use these methods on Roblox’s “Instances”, which are made using OOP

One of the main things about OOP is that once it’s made the code is abstracted from you and you don’t have to think about it, the same way you don’t have to think about the code that runs when you do Instance:FindFirstChild()

Why are you creating new functions when you’re creating a new class?

You should know that all class shares the same function, you don’t have to create the new functions every time you create a new class.

I believe you know the concept of a super class. In Lua, this is no different. To make a class inherit another, you simpy super() in Lua style. (class.new())
Here’s an example:

local super = {}
super.__index = super

function super.new()
	local self = setmetatable({
		test = 1
	}, super)
	
	return self
end

local child = {}
child.__index = child

function child.new()
	local self = setmetatable(super.new(), child)
	self.childProperty = 10
	
	return self
end

local superObj = super.new()
local childObj = child.new()

print(superObj.test .. ": super.test")
print(childObj.test .. ": child.test")
print(tostring(superObj.childProperty) .. ": super.childProperty")
print(childObj.childProperty .. ": child.childProperty")

image
(I’ve just put this explanation as your question has been answered only by giving the ready script)
Edit: Declare methods outside the constructor as they’ll eat up memory!

3 Likes