My pathfinding script can now jump, But cannot work properly. It gets stuck often

Anything in spoilers means that it was old and is not accurate now.

  1. What do you want to achieve?

I want to fix my pathfinding script so it can jump when needed

I want to fix my pathfinding script, making it smooth and usable.

  1. What is the issue?

I have a dummy that I made follow a player. If I ever go on top of something that you can jump on top of, the dummy just walks into it without jumping.

It is not very smooth and it also can get stuck and start spasming.

  1. What solutions have you tried so far?

I’ve made sure AgentCanJump = true. For some reason, the problem doesn’t occur with my version of the pathfinding (It is very choppy so I didn’t use it) but it occurs with code I took from the Roblox dev articles and modified.

I have tried reducing how many times update is fired by putting it into a while wait() loop. didn’t improve anything (Honestly it seemed worse?). I have set the network owner to the server (AKA. nil).

EDIT: I have rewritten the script and it somewhat works. The dummy will jump but now it cannot successfully follow me as if it is having a seizure.

OLD

local function GrabTarget()
  local playertable = game.Players:GetChildren()
  local target
  local targetmag
  for i, player in pairs(playertable) do
    local char = player.Character or player.CharacterAdded:Wait()
    local rootpart = char.HumanoidRootPart
    local mag = math.round((rootpart.Position - script.Parent.HumanoidRootPart.Position).Magnitude*10^(3))/10^(3)
    if target == nil or mag < targetmag then
      target = player
      targetmag = mag
    end
  end
  return target
end

local PathfindingService = game:GetService("PathfindingService")

-- Variables for the zombie, its humanoid, and destination
local zombie = script.Parent
local humanoid = zombie.Humanoid
local destination = GrabTarget()
local lastknowntarget
local lastknownpos
local runservice = game:GetService("RunService")
zombie.PrimaryPart:SetNetworkOwner(nil)
local pathparams = {}
pathparams.AgentCanJump = true
pathparams.AgentHeight = 6
pathparams.AgentRadius = 2

-- Create the path object
local path = PathfindingService:CreatePath(pathparams)

-- Variables to store waypoints table and zombie's current waypoint
local waypoints
local currentWaypointIndex

local function followPath()
  if destination and destination.Character then
    -- Compute and check the path
    path:ComputeAsync(zombie.HumanoidRootPart.Position, destination.Character.HumanoidRootPart.Position)
    -- Empty waypoints table after each new path computation
    waypoints = {}

    if path.Status == Enum.PathStatus.Success then
      -- Get the path waypoints and start zombie walking
      waypoints = path:GetWaypoints()
      -- Move to first waypoint
      local index = 0
      for i,v in pairs(waypoints) do
        local closest = nil
        index = index + 1
        if closest == nil or (zombie.HumanoidRootPart.Position - v.Position).Magnitude < closest then
          closest = (zombie.HumanoidRootPart.Position - v.Position).Magnitude
          currentWaypointIndex = index
        end
      end
      index = 0
      if waypoints[currentWaypointIndex].Action == Enum.PathWaypointAction.Jump then
        humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
        print("trying to jump")
      else
        humanoid:MoveTo(waypoints[currentWaypointIndex].Position)
      end
    else
      print("path not available")
      followPath()
    end
  end
end

local function onWaypointReached(reached)
  if reached and currentWaypointIndex < #waypoints then
    currentWaypointIndex = currentWaypointIndex + 1
    local before = currentWaypointIndex
    if waypoints[currentWaypointIndex].Action == Enum.PathWaypointAction.Jump then
      print("trying to jump")
      humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
    else
      humanoid:MoveTo(waypoints[currentWaypointIndex].Position)
      wait(4)
      if before == currentWaypointIndex then
        followPath()
      end
    end
  end
end

local function onPathBlocked(blockedWaypointIndex)
  -- Check if the obstacle is further down the path
  if blockedWaypointIndex > currentWaypointIndex then
    print("Recalculating")
    -- Call function to re-compute the path
    destination = GrabTarget()
    followPath()
  end
end

local function UpdatePath()
  if destination and destination.Character then
    local HRP = destination.Character.HumanoidRootPart
    local newtarget = GrabTarget()
    local currentpos = Vector3.new(math.round(HRP.Position.X*10^(2)/10^(2)),math.round(HRP.Position.Y*10^(2)/10^(2)),math.round(HRP.Position.Z*10^(2)/10^(2)))
    destination = newtarget
    if destination ~= lastknowntarget or lastknownpos ~= currentpos then
      lastknowntarget = destination
      lastknownpos = currentpos
      followPath()
      print("Following")
    end
  else
    destination = GrabTarget()
  end
end

-- Connect 'Blocked' event to the 'onPathBlocked' function
path.Blocked:Connect(onPathBlocked)

-- Connect 'MoveToFinished' event to the 'onWaypointReached' function
humanoid.MoveToFinished:Connect(onWaypointReached)

runservice.Heartbeat:Connect(UpdatePath)

followPath(destination)

NEW

local pathfindingservice = game:GetService("PathfindingService")
local runservice = game:GetService("RunService")
local zombie = script.Parent
local human = zombie.Humanoid
local path = pathfindingservice:CreatePath()
local currentwaypoint
local waypoints
local lastknownpos
local lastknowntarget

zombie.PrimaryPart:SetNetworkOwner(nil)

local function GrabTarget()
	local players = game.Players:GetChildren()
	local closest
	local target
	for i,v in pairs(players) do
		local char = v.Character or v.CharacterAdded:Wait()
		local HRP = char.HumanoidRootPart
		local distance = (HRP.Position - zombie.HumanoidRootPart.Position).Magnitude
		distance = math.round(distance*10^(3))/10^(3)
		if not closest or distance < closest then
			target = char
			closest = distance
		end
	end
	closest = nil
	return target
end

local function InitiateTarget(target)
	path:ComputeAsync(zombie.HumanoidRootPart.Position,target.HumanoidRootPart.Position)
	waypoints = {}
	local index = 0
	local closest
	if path.Status == Enum.PathStatus.Success then
		currentwaypoint = 1
		waypoints = path:GetWaypoints()
		for i,v in ipairs(waypoints) do
			index = index + 1
			local dist = (zombie.HumanoidRootPart.Position - v.Position).Magnitude
			if not closest or dist < closest then
				closest = dist
				currentwaypoint = index
			end
		end
		closest = nil
		if waypoints[currentwaypoint].Action == Enum.PathWaypointAction.Jump then
			print("Jumping")
			human:ChangeState(Enum.HumanoidStateType.Jumping)
		else
			print("Walking")
			human:MoveTo(waypoints[currentwaypoint].Position)
		end
	else
		InitiateTarget(GrabTarget())
	end
end

local function Update()
	local target = GrabTarget()
	if target then
		local targetpos = target.HumanoidRootPart.Position
		local finalpos = Vector3.new(math.round(targetpos.X*10^(3))/10^(3),math.round(targetpos.Y*10^(3))/10^(3),math.round(targetpos.Z*10^(3))/10^(3))
		if target ~= lastknowntarget or finalpos ~= lastknownpos then
			print("Recalculating")
			lastknownpos = finalpos
			lastknowntarget = target
			InitiateTarget(target)
		end
	end
end

local function FollowThrough(reached)
	if reached and currentwaypoint < #waypoints then
		currentwaypoint = currentwaypoint + 1
		if waypoints[currentwaypoint].Action == Enum.PathWaypointAction.Jump then
			print("Jumping")
			human:ChangeState(Enum.HumanoidStateType.Jumping)
		else
			human:MoveTo(waypoints[currentwaypoint].Position)
		end
	elseif not reached then
		Update()
	end
end

human.MoveToFinished:Connect(FollowThrough)

runservice.Heartbeat:Connect(Update)

EXAMPLE

https://streamable.com/vfx65m

So, if you work back from the print what is the value of the waypoints[currentWaypointIndex].Action?

It seems that the pathfinding Service thinks that it should walk.

Following indicates that it recomputed the path, yet the pathfinding service decides to walk.

OK and what is the value of closest. I am asking as I am wondering if the distance is too far for a jump to be determined.

Alright. I rewritten the code and now my dummy can jump. I need to implement some optimizations and fixes to make it smoother and not get stuck. Any ideas?

If there are no errors then I suggest you raise a new topic in Code Review as that is where ‘tuning’ is discussed.