Enemy Npc starts to tweak out when following player

Hey, I’m having some trouble with my enemy AI. For starters, the patrolling is completely fine and it’s bug free. The problem is whenever the enemy starts to pursue the player it starts to (I know this is a bad way to describe it but…) tweak out.

Tweaking out consists of:

  • Random turning when following player.
  • Attempting to head back a couple studs.
  • Whenever the enemy is within 3 studs and it stops, and then the player starts to move again, the animation studders at the beginning.
  • Making the game laggy when following the player.

I’ve tried EVERYTHING to fix these problems but have had no luck so far.

Video of the problem:

Code:

local PathfindingService = game:GetService("PathfindingService")
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")

local humanoid = script.Parent:WaitForChild("Humanoid")
local humanoidRoot = script.Parent:WaitForChild("HumanoidRootPart")

script.Parent.PrimaryPart:SetNetworkOwner(nil)

local CurrentlyMoving = false
local CloseToPlayer = false

local Config = script.Parent:WaitForChild("Config")
local PatrolBool = Config:WaitForChild("Patrols")

local PatrolPoints = script.Parent.Parent:WaitForChild("PatrolPoints")
local targetPart


-- assigns random part to partol to
local function AssignRandomPatrol()
	local NumberOfParts = PatrolPoints:GetChildren() -- Get all parts in PatrolPoints

	if #NumberOfParts == 0 then
		warn("No patrol points found!")
		return
	end

	local RandomPartIndex = math.random(1, #NumberOfParts) -- Get a random index
	targetPart = NumberOfParts[RandomPartIndex] -- Assign the actual part, not the index

	print("New target patrol point:", targetPart.Name) -- Debugging message
end

AssignRandomPatrol() -- Assigned random part



-- Move function
local function moveToTarget(target)
	local path = PathfindingService:CreatePath({
		AgentRadius = 2,
		AgentHeight = 5,
		AgentCanJump = true,
		AgentJumpHeight = 10,
		AgentWalkableFloorAngle = 60
	})

	path:ComputeAsync(humanoidRoot.Position, target.Position)

	if path.Status == Enum.PathStatus.Success then
		
		if CloseToPlayer == true then
			return
		end
		
		CurrentlyMoving = true
		
		local waypoints = path:GetWaypoints()
		local lastWaypoint = waypoints[#waypoints] -- Get the last waypoint

		-- Move NPC along each waypoint
		for _, waypoint in ipairs(waypoints) do
			if waypoint.Action == Enum.PathWaypointAction.Jump then
				humanoid.Jump = true
			end

			humanoid:MoveTo(waypoint.Position)

			-- Wait for the NPC to reach the waypoint
			humanoid.MoveToFinished:Wait()
		end

		-- Check if the NPC is close enough to the final destination
		local reachedGoal = (humanoidRoot.Position - target.Position).Magnitude < 5
		if reachedGoal then
			print("NPC has reached the target!")
			AssignRandomPatrol()

			CurrentlyMoving = false
		end
	else
		print("No valid path found!")
	end
end




-- Function to get the closest player to the part
local function getClosestPlayer()
	local closestPlayer = nil
	local shortestDistance = math.huge

	for _, player in pairs(Players:GetPlayers()) do
		if player.Character and player.Character:FindFirstChild("HumanoidRootPart") then
			local character = player.Character
			local humanoidRootPart = character:FindFirstChild("HumanoidRootPart")

			if humanoidRootPart then
				local distance = (script.Parent:WaitForChild("HumanoidRootPart").Position - humanoidRootPart.Position).Magnitude
				
				if distance < shortestDistance then
					closestPlayer = player
					shortestDistance = distance
				end
				
				
				if distance < 3 then
					wait(0.1)
					CloseToPlayer = true
					humanoid.WalkSpeed = 0
				else
					wait(0.1)
					CloseToPlayer = false
					humanoid.WalkSpeed = 14
				end
				
				
				
				
				if distance < 30 and distance > 3 then -- The sight range of the humanoid (in this case its 30 studs)
					wait(0.1)
					targetPart = humanoidRootPart
					moveToTarget(targetPart)
				end
				

			end
		end
	end

	return closestPlayer, shortestDistance
end


-- Run the update every frame using Heartbeat
RunService.Heartbeat:Connect(function()
	local closestPlayer, distance = getClosestPlayer()
	if closestPlayer then
		print(closestPlayer.Name .. " is the closest player, distance: " .. distance)
	else
		print("No players detected.")
	end
end)


-- Start NPC patrol loop (without using Heartbeat)
task.spawn(function()
	while true do
		if not CurrentlyMoving and PatrolBool.Value == true then
			AssignRandomPatrol()
			moveToTarget(targetPart)
		end
		task.wait(1) -- Prevent rapid execution
	end
end)


1 Like

Have you tried using black magic to fix the problem?

Oh no! I didn’t think of that. Thank you!

lmao what.

thisisfor30characters

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.