Need help with my Enemy AI

Hello, i’ve been having trouble for the past 3 hours with my enemy ai script, which I got from another source and then modified it to my own needs, and i’ve been having immense issues with it.

https://gyazo.com/e7ef4c94b56870e1936aaabefb2b4f22

It is laggy, I cannot find a way to make the enemy attack at all as the runservice.heartbeat keeps interrupting it, I cannot stop it from working for it to attack, I cannot make something only run once for some reason and there are just so many issues that i’ve been thinking of giving up on this project.

I’ve tried to:

  • Use debounce to stop the runservice loop when the enemy attacks
  • Use debounce on the attacking script (the EnemyAI.FastZombie one)
  • Run things only once to stop it from lagging (failed, I couldn’t get it to work somehow, I even searched dev forum for like 20 minutes to try and find a solution, but somehow my loop just ignored my solutions and kept running)
  • Stopped attempting OOP for this (it is my first time that I decided to use it) to clear up confusion
  • Much more
    Here is the (module) script:
local Players = game:GetService('Players')
local RunService = game:GetService('RunService')
local PathfindingService = game:GetService('PathfindingService')
local ReplicatedStorage = game:GetService('ReplicatedStorage')
local RaycastHitbox = require(ReplicatedStorage.RaycastHitboxV4)
local maxDistance = math.huge
local Anims = script.Parent.Anims

local EnemyAI = {}
--EnemyAI.__index = EnemyAI

function EnemyAI.new(Enemy,WalkAnim)
	--	local EnemyProperties = setmetatable({},EnemyAI)
	local Attacking = false
	local hum = Enemy.Humanoid
	local humrp = Enemy.HumanoidRootPart
	local WalkAnim = hum:LoadAnimation(WalkAnim)

--[[	EnemyProperties.Model = Enemy
	EnemyProperties.Humanoid = hum
	EnemyProperties.humrp = humrp
	EnemyProperties.Speed = Speed
	EnemyProperties.Damage = Damage
	]]
	--Pathfinding
	Enemy.PrimaryPart:SetNetworkOwner(nil)
	local destination = workspace.CentreOfFactory

	local function findTarget()
		local players = Players:GetPlayers()
		local MaxDistance = 200
		local nearestTarget

		for i,v in pairs(players) do
			if v.Character then
				if v.Character.Humanoid.Health > 0 then
					local target = v.Character
					local distance = (humrp.Position - target.HumanoidRootPart.Position).Magnitude

					if distance < MaxDistance then
						nearestTarget = target
						MaxDistance = distance
					end
				end
			end
		end

		return nearestTarget
	end

	local function agro(target)
		Attacking = true
		delay(2,function() Attacking = false end)
		local distance = (humrp.Position - target.HumanoidRootPart.Position).Magnitude
		local path = PathfindingService:CreatePath()
		path:ComputeAsync(humrp.Position, target.HumanoidRootPart.Position)
		if path.Status == Enum.PathStatus.Success then
			local waypoints = path:GetWaypoints()

			for _,waypoint in pairs(waypoints) do

				if waypoint.Action == Enum.PathWaypointAction.Jump then 
					hum:ChangeState(Enum.HumanoidStateType.Jumping)
				end

				if distance > 8 then
					hum:MoveTo(waypoint.Position)
				else
					WalkAnim:Stop()
					EnemyAI[string.gsub(Enemy.Name,' ','')](humrp,hum,Enemy,Attacking)
				end
			end
		else
			warn('Pathfinding failed.')
		end
	end


	local function walkTo(destination)
		local path = PathfindingService:CreatePath()
		path:ComputeAsync(humrp.Position, destination)
		if (humrp.Position - destination).Magnitude > 4 and WalkAnim.IsPlaying == false then
			WalkAnim:Play()
		elseif (humrp.Position - destination).Magnitude < 4 then
			WalkAnim:Stop()
		end

		if path.Status == Enum.PathStatus.Success then
			local waypoints = path:GetWaypoints()

			for _,waypoint in pairs(waypoints) do
				local target = findTarget()

				if waypoint.Action == Enum.PathWaypointAction.Jump then 
					hum:ChangeState(Enum.HumanoidStateType.Jumping)
				end
				if target then
					local distance = (humrp.Position - target.HumanoidRootPart.Position).Magnitude
					if distance <= 200 then
						agro(target)
						break
					end
				end

				hum:MoveTo(waypoint.Position)

				hum.MoveToFinished:Wait()
			end
		else
			warn('Pathfinding failed.')
		end
	end
	RunService.Heartbeat:Connect(function()
		if Attacking == false then
			walkTo(destination.Position)
		end
	end)
end

local runOnce = false

function EnemyAI.FastZombie(humrp,hum,Enemy)
	local AnimId = 'rbxassetid://6932293833'
	local Damage = 15
	local enemyHitbox = RaycastHitbox.new(Enemy.RightHand)
		enemyHitbox:SetPoints(Enemy.RightHand,{Vector3.new(0,0,0),Vector3.new(0,0,2)})
	if not Anims:FindFirstChild(AnimId) then
		local animationInstance = Instance.new('Animation')
		animationInstance.Parent = Anims
		animationInstance.AnimationId = AnimId
		animationInstance.Name = AnimId
	end
	local AttackAnim = hum:LoadAnimation(Anims[AnimId])
	AttackAnim:Play()
	enemyHitbox:HitStart()

	enemyHitbox.OnHit:Connect(function(hit,humanoid)
		if humanoid.Parent.Name ~= Enemy.Name then
			humanoid:TakeDamage(Damage)
		end
	end)
	AttackAnim.Stopped:Wait()
	enemyHitbox:HitStop()
end

return EnemyAI

Here is the place download:
need help on ai.rbxl (106.5 KB)
Please help, I’m desperate for any tips.

EDIT: Here is the server script:

local EnemyUtils = require(script.EnemyAI)
EnemyUtils.new(workspace["Fast Zombie"],script.Anims.walk.WalkAnim,10,20)```
2 Likes

Try removing wait statements and see if that helps.

It did somewhat help but now the ai just doesn’t attack and there are still a lot of issues

the only thing that has been solved was some of the lag, but I very much assume that the lag is from RaycastHitbox getting called a million times a second along with some of its functions that are only supposed to run once.

edit: at this point I am going to create a new ai

Ok so I made a new ai completely on my own and it has less problems (kinda), but now it is incredibly imprecise and it still can’t catch up to the player.

local Players = game:GetService('Players')
local RunService = game:GetService('RunService')
local PathfindingService = game:GetService('PathfindingService')
local ReplicatedStorage = game:GetService('ReplicatedStorage')
local RaycastHitbox = require(ReplicatedStorage.RaycastHitboxV4)
local maxDistance = math.huge
local Anims = script.Parent.Anims

local EnemyAI = {}
--EnemyAI.__index = EnemyAI

function EnemyAI.new(Enemy,WalkAnim)
	--	local EnemyProperties = setmetatable({},EnemyAI)
	local Attacking = false
	local hum = Enemy.Humanoid
	local humrp = Enemy.HumanoidRootPart
	local WalkAnim = hum:LoadAnimation(WalkAnim)

--[[	EnemyProperties.Model = Enemy
	EnemyProperties.Humanoid = hum
	EnemyProperties.humrp = humrp
	EnemyProperties.Speed = Speed
	EnemyProperties.Damage = Damage
	]]
	--Pathfinding
	Enemy.PrimaryPart:SetNetworkOwner(nil)
	local CoreOfFactory = workspace.CentreOfFactory

	local function findTarget()
		local players = Players:GetPlayers()
		local MaxDistance = 200
		local nearestTarget

		for i,v in pairs(players) do
			if v.Character then
				if v.Character.Humanoid.Health > 0 then
					local target = v.Character
					local distance = (humrp.Position - target.HumanoidRootPart.Position).Magnitude

					if distance < MaxDistance then
						nearestTarget = target
						MaxDistance = distance
					end
				end
			end
		end

		return nearestTarget
	end
	
	local function agro()
		repeat task.wait(0.05)
			local target = findTarget()
		local distance = (humrp.Position - target.HumanoidRootPart.Position).Magnitude
		local path = PathfindingService:CreatePath()
		local success, errorMessage = pcall(function()
			path:ComputeAsync(humrp.Position, target.HumanoidRootPart.Position + target.HumanoidRootPart.Velocity.Unit * 3)
		end)
		if success and path.Status == Enum.PathStatus.Success then
			local waypoints = path:GetWaypoints()
			for _,waypoint in pairs(waypoints) do
				
				if waypoint.Action == Enum.PathWaypointAction.Jump then
					hum:ChangeState(Enum.HumanoidStateType.Jumping)
				end
				
				if distance <= 5 then
					EnemyAI[string.gsub(Enemy.Name,' ','')](humrp,hum,Enemy)
				else
					hum:MoveTo(waypoint.Position)
				end
			end
			end
			until (humrp.Position - target.HumanoidRootPart.Position).Magnitude > maxDistance
	end
	
	local function walkToCore()
		local path = PathfindingService:CreatePath()
		
		local success, errorMessage = pcall(function()
			path:ComputeAsync(humrp.Position, CoreOfFactory.Position)
		end)
		WalkAnim:Play()
		if success and path.Status == Enum.PathStatus.Success then
			local waypoints = path:GetWaypoints()
			for _,waypoint in pairs(waypoints) do
				
				if waypoint.Action == Enum.PathWaypointAction.Jump then
					hum:ChangeState(Enum.HumanoidStateType.Jumping)
				end
				
				if findTarget() and (humrp.Position - findTarget().HumanoidRootPart.Position).Magnitude < maxDistance then
					agro()
				end
				
				hum:MoveTo(waypoint.Position)
				hum.MoveToFinished:Wait()
			end
		end
		WalkAnim:Stop()
	end
	
	walkToCore()
end

local RunOnce = false
function EnemyAI.FastZombie(humrp,hum,Enemy)
	local AnimId = 'rbxassetid://6932293833'
	local Damage = 15
	local enemyHitbox
	if RunOnce == false then
	enemyHitbox = RaycastHitbox.new(Enemy.RightHand)
		enemyHitbox:SetPoints(Enemy.RightHand,{Vector3.new(0,0,0),Vector3.new(0,0,2)})
		end
	if not Anims:FindFirstChild(AnimId) then
		local animationInstance = Instance.new('Animation')
		animationInstance.Parent = Anims
		animationInstance.AnimationId = AnimId
		animationInstance.Name = AnimId
	end
	local AttackAnim = hum:LoadAnimation(Anims[AnimId])
	
	AttackAnim:Play()
	enemyHitbox:HitStart()

	enemyHitbox.OnHit:Connect(function(hit,humanoid)
		if humanoid.Parent.Name ~= Enemy.Name then
			humanoid:TakeDamage(Damage)
		end
	end)
	coroutine.wrap(function()
	AttackAnim.Stopped:Wait()
		enemyHitbox:HitStop()
		end)()
end

return EnemyAI

The problem is that you are using the pathfinding service to calculate the path to players that will be moving constantly. Pathfinding has its own delay and is not 100% precise. You need to either send the zombie straight toward the player or create a system that tries to walk the zombie past immediate obstacles.

1 Like

That did seem to fix it, but now I have entirely seperate, smaller problems.
New script:

https://gyazo.com/3998b7aa29c0c579f65a6c7aa742f10b
Issue: when the player stands still the ai doesn’t go to them, if I put this in a print, it outputs nan,nan,nan, and only works when the player is moving.
hum:MoveTo(target.HumanoidRootPart.Position + target.HumanoidRootPart.Velocity.Unit * 7)
Also, do you have any tips on making this smoother and having more accurate hit detection?
https://gyazo.com/c1bd32672a36206f1628f5f41cc76da1

hum:MoveTo(target.PrimaryPart.Position)

If you want it to walk to the point in front of the target:

local targetVel = target.PrimaryPart.Velocity
local leadBy = 7
local lead
if targetVel.Magnitude < 0.1 then
   lead = Vector3.new(0, 0, 0)
else
   lead = targetVel.Unit * leadBy
end
hum:MoveTo(target.PrimaryPart.Position + lead)
2 Likes

Thanks for all the help man (char)