OOP enemy system cannot spawn more then one enemy at a time

I am creating an enemy system using OOP however I have no idea how I would excute it’s code more then one at a time this may sound confusing so I’ll explain

Constructer Function and pathfinding for enemy:

local Zombie = {}
Zombie.__index = Zombie

function Zombie.new(position)
	print(position)

	local NewZombie = setmetatable({}, Zombie)
	
	NewZombie.Model = game.ReplicatedStorage.ZombieModels.ZombieModel:Clone()
	NewZombie.Model.Parent = game.Workspace
	NewZombie.Model.HumanoidRootPart.Anchored = false
	NewZombie.Model.HumanoidRootPart.CanCollide = true
	NewZombie.Model:SetPrimaryPartCFrame(CFrame.new(7.65, 6.75, 24.55))
	
	NewZombie.health = 100
	NewZombie.speed = 8
	NewZombie.damage = 10
	NewZombie.state = "Idle"
	
	return NewZombie
end

function Zombie:Update()
	print(self.Model.Parent)
	local PathfindingService = game:GetService("PathfindingService")
		--What to do depending on the Zombies state
		
		if self.state == "Idle" then
			
			local ReconPoints = game.Workspace.ReconPoints:GetChildren()
			local MaxDistance = 200
			local ReconTable = {}

			--Get all ReconPoints near it and choose one to randomly go to 
			for _, ReconPoint in pairs(ReconPoints) do
				local DistanceFromReconPoint = (ReconPoint.Position - self.Model.HumanoidRootPart.Position).Magnitude
				if DistanceFromReconPoint < MaxDistance then
					table.insert(ReconTable, ReconPoint)
				end
			end
			
			print(ReconTable)

			local ReconPointPickedNum = math.random(1, #ReconTable)
			local ReconPointPicked = ReconTable[ReconPointPickedNum]

			local path = PathfindingService:CreatePath()

		path:ComputeAsync(self.Model.HumanoidRootPart.Position, ReconPointPicked.Position)
		--If path is possible then go to each waypoint only start gong to the next one when your 5 studs away from current one
			if path.Status == Enum.PathStatus.Success then
				
				for _, waypoint in ipairs(path:GetWaypoints()) do					
					
					
					self.Model.Humanoid:MoveTo(waypoint.Position)
					if waypoint.Action == Enum.PathWaypointAction.Jump then
						self.Model.Humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
					end
					local goal = false

					while goal == false do
						task.wait(0.1)

						local GoalDistance = (self.Model.HumanoidRootPart.Position - waypoint.Position).Magnitude

						if GoalDistance < 5 then
                       
							goal = true
						end
					end
				end
			else
				
			end



		elseif self.state == "Chasing" then
			-- Ignore this for now, still being worked on
		elseif self.state == "Attacking" then
			-- Ignore this for now, still being worked on
		end
		
end

return Zombie

Now the thing is that this code works fine however If I spawn in an enemy useing, for example a script like this

local Zombie = require(game.ReplicatedStorage.ZombieSystem.Zombie)
local ZombiePosition = game.Workspace.ZombieSpawner.Position


local NewZombie = Zombie:new(ZombiePosition)

NewZombie:Update()

then using this script it could only manage one enemy at a time

I’ve tried other things like making it so whenever a new zombie is created it automatically starts using it’s update function but that does not work as the if I trying running its constructer it has to wait untill the previously created zombie is done running its code to spawn in another.

If anyone could give me an idea on how to fix this it would be appreciated!

Why do you have your constructor function as .new but your calling it with :new?

image

image

that’s a mistake on my end however it does not really change anything

I would suggest using tags and the collection service. So that you could just tag a zombie and then the constructor and update functions would be called automatically.

CollectionService in a nutshell - Resources / Community Tutorials - Developer Forum | Roblox

I can provide some boilerplate if you’d like.

If its waiting for somethind its due to a yield, the main culprit is the while loop and task.wait(), you can use task.spawn to run this update function in a different thread in order to not wait for it.

You can use a for loop and store the object in a dictionary if you want other scripts to acces and manage multiple enemies {[model] = newObject.new()}

please do provide some I would appreciate it.

This is the boilerplate I use when I create a class that I want to use tags with:

local CollectionService = game:GetService("CollectionService")

local Base = {}
Base.__index = Base
Base.instances = {}
Base.TAG_NAME = "Base"

function Base.New(part: Part)
	local self = {}
	setmetatable(self, Base)

	self.part = part
	self.part.Destroying:connect(function() self.Old(self.part) end)

	Base.instances[part] = self
	return self;
end

function Base.Old(inst: Instance)
	if Base.instances[inst] then 
		--destroy Base

		Base.instances[inst] = nil
	end
end

for _, inst in pairs(CollectionService:GetTagged(Base.TAG_NAME)) do
	Base.New(inst)
end

CollectionService:GetInstanceAddedSignal(Base.TAG_NAME):Connect(function(inst) Base.New(inst) end)
CollectionService:GetInstanceRemovedSignal(Base.TAG_NAME):Connect(function(inst) Base.Old(inst) end)

You can change .New to .new to stay in form with Roblox and change .Old to something more descriptive but the core functionality of OOP and tagging is here.

In the case for your update function, you could add a connection to RunService.Stepped to call it every frame.

Thx Man. I’ll mark your comment as solution for now

No problem. Let me know if you have trouble setting it up.