Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
0

My npc's pathfinding is super laggy and the npc stops every once and a while, does anyone know why?

Asked by 4 years ago

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

1 answer

Log in to vote
0
Answered by 4 years ago

Network Ownership

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.

Example: Projectiles

Imagine a player is shooting an object at another player.

Problem

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.

Solution

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.

Your script

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
0
i did some tests with this and from what i saw all the issues are fixed, thanks! CelticFury600 4 — 4y
0
No problem! CaIcuIati0n 246 — 4y
Ad

Answer this question