Help With OOP Enemy System

I’ve recently come back from a break I took from studio and I am now learning Object Oriented Programming. It’s a little confusing and I tried using it to make an enemy system and I’m kind of confused.

I have a main module called Enemy, a descendant module called Zombie, and a normal script called Main. I was wondering if someone could guide me on how to improve and organize the code.

Enemy main module:

local Enemy = {}

Enemy.__index = Enemy --Sets index to itself

function Enemy.new(hp, spd, dmg) --Function to create a new Enemy and variables


	return setmetatable({
		Health = hp,
		Speed = hp,
		Damage = dmg,},
        Enemy) 
	end

return Enemy

Zombie module:

local Enemy = require(script.Parent.Enemy) --requires other module

local Zombie = {}

function Zombie.new(model) --Allows the creation of a zombie and model
	return setmetatable({Enemy.new( --Sets variables of hp, speed, and dmg
		100,
		12,
		20)},
		Zombie)
end
return Zombie

Finally, the normal script

local Zombie = require(script.Parent.Zombie) --Is their a way to require all the modules so I can add more enemies like a skeleton for example?

local ZombModel = game.ReplicatedStorage.EnemyModels.ZombieModel

Zombie.new(ZombModel)

ZombModel.PrimaryPart.CFrame = CFrame.new(430, 4, -8) --Theirs probably a better way to do this like adding a new function or var to enemy.  Please let me know how if possible

ZombModel.Parent = workspace
2 Likes

Well to improve the code you can definitely utilize the fact that it is Object Oriented more. One way you can do this is I see in your normal script you create a new zombie object with “Zombie.new(ZombModel)” but you dont actually set that as a variable which means you created the object but you cant ever use it in the future, So you could instead do "local Zombie = Zombie.new(ZombModel), this will allow you to edit properties of the zombie object in the future, for instance you have a line of code that changes the “ZombModel”'s primaryParts CFrame, instead of this you could reference the zombie object variable I talked about (local Zombie = Zombie.new(ZombModel)) and do Zombie.Model.PrimaryPart.CFrame = blabla. to do this you would have to do add a value called Model to the Zombie.new() function in the Zombie module by doing
return setmetatable({Enemy.new(
100,
12,
20)
Model = model}, – added value to the object
Zombie)
Now this made it so you are actually accessing the zombie object and using the OOP to your advantage, otherwise you have created the object for nothing.
Sorry if this was confusing I would assume it is because I can be bad at explaining things, feel free to ask any questions.

1 Like

Okay, I added this to the zombie module:

function Zombie.new(model)
	return setmetatable({Enemy:new(
		100,
		12,
		20,
		model --This is what I added
		)},
		Zombie)
end
return Zombie

But I don’t know how to access the model from the regular script.
I tried this and it didn’t work

local ZombieModule = require(script.Parent.Zombie)

local ZombModel = game.ReplicatedStorage.EnemyModels.ZombieModel

local Zombie = ZombieModule.new(ZombModel)

Zombie.Model.PrimaryPart.CFrame(0,0,0)

you have to do Model = model like this
return setmetatable({Enemy:new(
100,
12,
20,
Model = model --**change— instead of just model
)},
Zombie)

I added to the Enemy module

function Enemy:new(hp, spd, dmg, mod)  -- and this


	return setmetatable({
		Health = hp,
		Speed = hp,
		Damage = dmg,
		model = mod --I added this
		,}, 
		
	    self)
	end

still doesn’t work and I think the issue is the regular script because it doesnt know what Zombie.Model is because it isn’t defined but I don’t know how to access the model variable from the module script and use it in the regular script.

Here’s a new example:

local enemyClass = {}
enemyClass.__index = enemyClass

function enemyClass.new(hp, spd, dmg, model)
local this = setmetatable({}, enemyClass)

this.Health = hp
this.Speed = spd
this.Damage = dmg
this.Model = model

return this
end

--//Some other script

local myEnemy = enemyClass.new(10, 10, 10, workspace.someModel)

Then call the .new method to create new enemy objects passing in whichever arguments you want :slight_smile:

1 Like

I do see you are trying to do this in an unusual way, I also notice that the enemyModule does virtually nothing and you are returning the return value of the setmetatable function, I am pretty sure this will return void(nothing) so instead do this in the zombie module:

function Zombie.new(model, hp, spd, dmg)

 local newZomb = {}
 setmetatable(newZomb, Zombie)
 newZomb.Model = model
 newZomb.Health = hp
 newZomb.Speed = spd
 newZomb.Damage = dmg
 return newZomb

end

return Zombie

and when doing Zombie.new() in the main script do Zombie.new(ZombModel, hp, speed, damage) – replace hp speed and damage with their proper values

Okay thanks for the advice. I removed the zombie module and this is the new Enemy module:

local Enemy = {}

Enemy.__index = Enemy

function Enemy:new(hp, spd, dmg, mod)
	local EnemyTable = setmetatable({}, Enemy)
	EnemyTable.Health = hp
	EnemyTable.Speed = spd
	EnemyTable.Damage = dmg
	EnemyTable.Model = mod
	
	return EnemyTable
	end

return Enemy

and here is the normal script:

local Enemy = require(script.Parent.Enemy)

local ZombModel = game.ReplicatedStorage.EnemyModels.ZombieModel

local Zombie = Enemy.new(100, 13, 20, ZombModel)

My only other question is should I move the model by just doing
ZombModel.PrimaryPart.CFrame = CFrame.new()
or is their something else I should do?