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)