Heya there.
I’m currently rewriting an OOP code for a fangame I’m working on called “Noobs vs Zombies: Relish Reborn”. I’ve did notice some issues with the code however and I’m wondering how I can fix them. The issues I’ve seen so far are:
- NPC’s not switching targets whenever they’re near an enemy.
- NPC’s getting ‘stuck’ whenever an enemy dies where they’ll walk back and fourth
- The code is poorly formatted.
The code is still in WIP, though criticism should be helpful to make it more readable in the future.
--[[SERVICES]]--
local Players = game:GetService("Players")
local PathfindingService = game:GetService("PathfindingService")
--[[NPC MODEL FOLDERS]]--
local NPCFolder = game:GetService("ServerScriptService").NPCS
local NoobsFolder = NPCFolder.Noobs
local ZombiesFolder = NPCFolder.Zombies
--[[MODULE]]--
local BaseNPCModule = {}
BaseNPCModule.__index = BaseNPCModule
function BaseNPCModule:CreateNPC(UnitName, UnitTeam, Weapon, Special)
--//Checking to see if the unit exists.
--//Also checking what team the unit is on as well
if UnitTeam == "Noobs" then
for _, FindUnit in pairs(NoobsFolder:GetChildren()) do
if FindUnit:IsA("Model") and FindUnit.Name == UnitName then
local self = setmetatable({}, BaseNPCModule)
--//Creating the unit.
self.Unit = FindUnit:Clone()
self.Humanoid = self.Unit:FindFirstChildWhichIsA("Humanoid")
self.Unit.Parent = game.Workspace
self.UnitTeam = game:GetService("CollectionService"):AddTag(self.Unit, UnitTeam)
self.Is_Special = Special
--//Spawning it to the map.
local Noob_Spawns = workspace:FindFirstChild("NoobSpawns")
local SelectedSpawns = Noob_Spawns:GetChildren()
local Pos = math.random(1,#SelectedSpawns)
local SpawnPosition = SelectedSpawns[Pos]
self.Unit.PrimaryPart.CFrame = SpawnPosition.CFrame + Vector3.new(0, 3, 0)
--//Giving it functionality
if not self.Is_Special then
--//Assuming it's just a basic NPC (E.g. Noob/Zombie Sworder)
--//Otherwise, the NPC is going to inheritate only the FindNearestTargets and Pathfind functions.
task.spawn(function()
while task.wait(0.05) do
task.spawn(function()
local Target = self:FindNearestTargets()
if Target then
self:Pathfind(Target)
end
end)
end
end)
end
return self
end
end
elseif UnitTeam == "Zombies" then
for _, FindUnit in pairs(ZombiesFolder:GetChildren()) do
if FindUnit:IsA("Model") and FindUnit.Name == UnitName then
local self = setmetatable({}, BaseNPCModule)
--//Creating the unit.
self.Unit = FindUnit:Clone()
self.Humanoid = self.Unit:FindFirstChildWhichIsA("Humanoid")
self.Unit.Parent = game.Workspace
self.UnitTeam = game:GetService("CollectionService"):AddTag(self.Unit, UnitTeam)
self.Is_Special = Special
--//Spawning it to the map.
local Zombie_Spawns = workspace:FindFirstChild("ZombieSpawns")
local SelectedSpawns = Zombie_Spawns:GetChildren()
local Pos = math.random(1,#SelectedSpawns)
local SpawnPosition = SelectedSpawns[Pos]
self.Unit.PrimaryPart.CFrame = SpawnPosition.CFrame + Vector3.new(0, 3, 0)
--//Giving it functionality
if not self.Is_Special then
--//Assuming it's just a basic NPC (E.g. Noob/Zombie Sworder)
--//Otherwise, the NPC is going to inheritate only the FindNearestTargets and Pathfind functions.
task.spawn(function()
while task.wait(0.05) do
task.spawn(function()
local Target = self:FindNearestTargets()
if Target then
self:Pathfind(Target)
end
end)
end
end)
end
return self
end
end
elseif UnitTeam == "Neutral" then
--//TBA
end
end
function BaseNPCModule:FindNearestTargets()
local Targets = {}
local Nearest = math.huge
local Target = nil
for _, PossibleTargets in ipairs(workspace:GetChildren()) do
if PossibleTargets:IsA("Model") and PossibleTargets:FindFirstChildWhichIsA("Humanoid") then
local Humanoid = PossibleTargets:FindFirstChildWhichIsA("Humanoid")
if Humanoid.Health <= 1 then
--//They're dead.
return
end
--//If they aren't dead, check the distance.
local Distance = (PossibleTargets.PrimaryPart.Position - self.Unit.PrimaryPart.Position).Magnitude
if Distance <= Nearest then
if PossibleTargets:HasTag("Zombies") and self.Unit:HasTag("Noobs") then
table.insert(Targets,{
Magnitude = Nearest,
FoundTarget = PossibleTargets
})
elseif PossibleTargets:HasTag("Noobs") and self.Unit:HasTag("Zombies") then
table.insert(Targets,{
Magnitude = Nearest,
FoundTarget = PossibleTargets
})
end
end
end
end
for _, Entry in pairs(Targets) do
local magnitude = Entry.Magnitude
local target = Entry.FoundTarget
if magnitude <= Nearest then
Nearest = magnitude
Target = target
end
end
return Target
end
function BaseNPCModule:Pathfind(Target)
local NewPath = PathfindingService:CreatePath({
AgentRadius = 3,
AgentHeight = 5,
AgentCanJump = true,
})
NewPath:ComputeAsync(self.Unit.PrimaryPart.Position, Target.PrimaryPart.Position)
if NewPath.Status == Enum.PathStatus.Success then
local Waypoints = NewPath:GetWaypoints()
for i, Waypoint in ipairs(Waypoints) do
if i <= 2 then continue end
if Waypoints[4] then
self.Humanoid:MoveTo(Waypoints[4].Position)
local TimeOut = self.Humanoid.MoveToFinished:Wait()
if not TimeOut then
warn("NPC got stuck... Recalculating!")
BaseNPCModule:Pathfind(Target)
end
end
end
end
end
return BaseNPCModule