So, I’m trying to make a game that is based off of PenguinDoc9999’s Survive in Area 51, and a big part of that is having many enemies for players to kill. Issue is, as the title says, the zombies (only enemy for now) are buggy. Sometimes you can even get them to follow you without attacking, for some reason. Admittedly, the code is based off of a tutorial video’s zombie script.
The zombies run off of a centralized ModuleScript in ServerScriptService, and are all R6 to reduce part-count lag.
Here’s a testing place:
First Person Area 51 testing.rbxl (1.1 MB)
Here’s the ModuleScript, for people who can’t/don’t want to go in the place file:
local AI = {}
local ammo = game:GetService("ServerStorage"):WaitForChild("Ammo"):GetChildren()
local ammoNum = #game:GetService("ServerStorage"):WaitForChild("Ammo"):GetChildren()
function AI.Register(entity)
--print(entity.Name)
--print("registered")
--body parts
local hitDB = false
local isMoving = false
local willRandomMove = false
local myHuman = entity:WaitForChild("Humanoid")
local myAnimator = myHuman:WaitForChild("Animator")
local myRoot = entity:WaitForChild("HumanoidRootPart")
local myHip = entity:WaitForChild("Hip")
--animations
local grab = entity:WaitForChild("Grab")
local grabAnim = myAnimator:LoadAnimation(grab)
grabAnim.Priority = Enum.AnimationPriority.Action
--sounds
local grabSound = myRoot:WaitForChild("Attack")
local hitSound = entity:WaitForChild("Right Arm").ZombHit
local function checkSight(target)
local ray = Ray.new(myRoot.Position, (target.Position - myRoot.Position).Unit * 40)
local hit, pos = workspace:FindPartOnRayWithIgnoreList(ray, {script.Parent})
if hit then
if hit:isDescendantOf(target.Parent) and math.abs(hit.Position.Y - myRoot.Position.Y) < 10 then
return true
end
end
return false
end
local function findTarget()
local dist = 60
local target = nil
local potTargets = {}
local seeTargets = {}
for i, v in ipairs(workspace:GetChildren()) do
local human = v:FindFirstChild("Humanoid")
local torso = v:FindFirstChild("HumanoidRootPart")
if human and torso then
if (myRoot.Position - torso.Position).magnitude < dist and human.Health > 0 then
table.insert(potTargets, torso)
end
end
end
if #potTargets > 0 then
for i, v in ipairs(potTargets) do
if checkSight(v) then
table.insert(seeTargets, v)
elseif #seeTargets < 1 and (myRoot.Position - v.Position).magnitude < dist then
target = v
dist = (myRoot.Position - v.Position).magnitude
end
end
end
if #seeTargets > 0 then
dist = 60
for i, v in ipairs(seeTargets) do
if (myRoot.Position - v.Position).magnitude < dist then
target = v
dist = (myRoot.Position - v.Position).magnitude
end
end
end
-- Check if target is still visible
if target and not checkSight(target) then
--print("oh, guess not")
target = nil
end
return target
end
local function attack(target)
coroutine.resume(coroutine.create(function()
if (myRoot.Position - target.Position).Magnitude < 3 then
if hitDB == false then
hitDB = true
grabSound:Play()
grabAnim:Play()
grabAnim.Stopped:Wait()
if target.Parent ~= nil then
if (myRoot.Position - target.Position).Magnitude < 3 then
hitSound:Play()
target.Parent.Humanoid:TakeDamage(10)
end
end
task.wait(0.3)
hitDB = false
end
end
end))
end
local function findPath(target)
local path = game:GetService("PathfindingService"):CreatePath()
path:ComputeAsync(myRoot.Position, target.Position)
local waypoints = path:GetWaypoints()
if path.Status == Enum.PathStatus.Success then
for _, waypoint in ipairs(waypoints) do
if waypoint.Action == Enum.PathWaypointAction.Jump then
myHuman.Jump = true
end
myHuman:MoveTo(waypoint.Position)
local timeout = myHuman.MoveToFinished:Wait(1)
if not timeout then
--print("Stuck :(")
myHuman.Jump = true
findPath(target)
break
end
if checkSight(target) then
repeat
myHuman:MoveTo(target.Position)
--attack(target)
--print((myRoot.Position - target.Position).Magnitude)
if (myRoot.Position - target.Position).Magnitude < 3 and hitDB == false then
print("hit")
hitDB = true
grabSound:Play()
grabAnim:Play()
local damageEntity = task.delay(grabAnim.Length, function()
if target.Parent ~= nil then
if (myRoot.Position - target.Position).Magnitude < 3 then
hitSound:Play()
target.Parent.Humanoid:TakeDamage(10)
task.wait(0.3)
hitDB = false
end
end
end)
end
task.wait(0.1)
if target == nil then
break
elseif target.Parent == nil then
break
end
until checkSight(target) == false or myHuman.Health < 1 or target.Parent.Humanoid.Health < 1
break
end
if (myRoot.Position-waypoints[1].Position).magnitude > 20 then
findPath(target)
break
end
end
end
end
local function walkRand()
local xRand = math.random(-10, 10)
local zRand = math.random(-10, 10)
local goal = myRoot.Position + Vector3.new(xRand, 0, zRand)
local path = game:GetService("PathfindingService"):CreatePath()
path:ComputeAsync(myRoot.Position, goal)
local waypoints = path:GetWaypoints()
if path.Status == Enum.PathStatus.Success then
for _, waypoint in ipairs(waypoints) do
if waypoint.Action == Enum.PathWaypointAction.Jump then
myHuman.Jump = true
end
myHuman:MoveTo(waypoint.Position)
local timeout = myHuman.MoveToFinished:Wait(.5)
if not timeout then
--print("Stuck :(")
myHuman.Jump = true
walkRand()
end
end
else
--print("Path failed :(")
task.wait(.5)
walkRand()
end
end
myHuman.Died:Connect(function()
local drop = ammo[math.random(ammoNum)]:Clone()
drop.Position = myRoot.Position
drop.Parent = game.Workspace
game:GetService("Debris"):AddItem(drop,10)
game:GetService("Debris"):AddItem(entity,1)
end)
myHip.Touched:Connect(function(obj)
if not obj:FindFirstAncestorWhichIsA("Model"):FindFirstChild("Humanoid") and obj:FindFirstAncestorWhichIsA("Model").Parent:FindFirstChild("Humanoid") then
myHuman.Jump= true
end
end)
coroutine.wrap(function()
while true do
task.wait(math.random(1, 7))
willRandomMove = true
end
end)()
while task.wait() do
if myHuman.Health < 1 then
break
end
local target = findTarget()
if target then
--print("OWO what's this?")
myHuman.WalkSpeed = 16
findPath(target)
else
myHuman.WalkSpeed = 8
if willRandomMove == true and isMoving == false then
isMoving = true
willRandomMove = false
task.spawn(walkRand)
isMoving = false
end
end
end
end
return AI
If you have any idea of what the culprit could be, or if my npc code is just plain bad, please let me know. Thanks!