I went along with a tutorial for a pathfinding npc on youtube and changed it around so it fits my npc, and the pathfinding is super laggy and I don't know why.
local myHuman = script.Parent:WaitForChild("Humanoid") local myRoot = script.Parent:WaitForChild("HumanoidRootPart") local head = script.Parent:WaitForChild("Head") local lowerTorso = script.Parent:WaitForChild("LowerTorso") local grab = script.Parent:WaitForChild("grabanim") local grabAnim = script.Parent.Humanoid:LoadAnimation(grab) grabAnim.Priority = Enum.AnimationPriority.Action local clone = script.Parent:Clone() function walkRandomly() local xRand = math.random(-50,50) local zRand = math.random(-50,50) 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(0.1) if not timeOut then print("Got stuck") myHuman.Jump = true walkRandomly() end end else print("Path failed") wait(0.1) walkRandomly() end end 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(0.1) if not timeOut then myHuman.Jump = true print("Path too long!") findPath(target) break end if checkSight(target) then repeat print("Moving directly to the target") myHuman:MoveTo(target.Position) attack(target) 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 print("Target has moved, generating new path") findPath(target) break end end end end function checkSight(target) local ray = Ray.new(myRoot.Position, (target.Position - myRoot.Position).Unit * 40) local hit,position = workspace:FindPartOnRayWithIgnoreList(ray, {script.Parent}) if hit then if hit:IsDescendantOf(target.Parent) and math.abs(hit.Position.Y - myRoot.Position.Y) < 3 then print("I can see the target") return true end end return false end function findTarget() local dist = 200 local target = nil local potentialTargets = {} local seeTargets = {} for i,v in ipairs(workspace:GetChildren()) do local human = v:FindFirstChild("Humanoid") local torso = v:FindFirstChild("Torso") or v:FindFirstChild("HumanoidRootPart") if human and torso and v.Name ~= script.Parent.Name then if (myRoot.Position - torso.Position).magnitude < dist and human.Health > 0 then table.insert(potentialTargets,torso) end end end if #potentialTargets > 0 then for i,v in ipairs(potentialTargets) do if checkSight(v) then table.insert(seeTargets, v) elseif #seeTargets == 0 and (myRoot.Position - v.Position).magnitude < dist then target = v dist = (myRoot.Position - v.Position).magnitude end end end if #seeTargets > 0 then dist = 200 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 if target then end return target end function attack(target) if (myRoot.Position - target.Position).magnitude < 5 then grabAnim:Play() if target.Parent ~= nil then target.Parent.Humanoid:TakeDamage(20) end wait(.5) end end function died() wait(5) clone.Parent = workspace game:GetService("Debris"):AddItem(script.Parent,0.1) end myHuman.Died:Connect(died) lowerTorso.Touched:Connect(function(obj) if not obj.Parent:FindFirstChild("Humanoid") then myHuman.Jump = true end end) function main() local target = findTarget() if target then myHuman.WalkSpeed = 18 findPath(target) else myHuman.WalkSpeed = 8 walkRandomly() end end while wait(0.1) do if myHuman.Health < 1 then break end main() end
In a Roblox game, all non-anchored parts are “physically simulated” — they can fall, bounce, float in water, etc. All of these things are handled automatically by Roblox so you don’t have to write your own physics engine.
When a Roblox game runs, several computers/devices are involved. To divide the work of calculating physics, Roblox automatically assigns parts to either the server or to a client. In general, if a part is near an in-game character, its physics will be calculated by that player’s device; otherwise it will be calculated by the server. In either case, the server or client that calculates a part’s physics is called its owner.
Since physics updates must be sent over the network, there’s a small delay between when the owner makes the physical changes and when the other devices see those changes. Normally this delay isn’t too noticeable, but issues may occur when part ownership changes.
Imagine a player is shooting an object at another player.
As the object travels through the air, it will get far from the shooter and ownership will switch to the server. Once the object gets close enough to the target player, it will switch ownership to the player. Even with a good network connection, there may be a tiny delay and visible “hop” when the object’s owner switches.
Manually set ownership of the projectile to the server using SetNetworkOwner(). This ensures that the owner does not change, preventing visible “hops.”
SetNetworkOwner() and its partner function GetNetworkOwner() must be set on the server side. Clients are not allowed to change ownership settings in a LocalScript.
In your script, the NPC networks owner is changing from client to server, which will make it stutter a few times, delaying it's time to get to the destination.
We can use :SetNetworkOwner
, to prevent it from stuttering.
Put this at the top of your script
if script.Parent.HumanoidRootPart:CanSetNetworkOwnership() then script.Parent.HumanoidRootPart:SetNetworkOwner(nil) -- setting the owner to the server end