In my script, I am trying to make the NPC always detect if its in the water so the Y position is -2 and just move onto the land. The model is inside ReplicatedStorage and will be placed into workspace and sometimes some of them just dont move at all. When the NPC touches the part, it should jump onto the platform and move forward then stop. Here is a clip of it:
Here is my script:
local humanoid = script.Parent:WaitForChild("Humanoid")
local RunService = game:GetService("RunService")
local pathfindingService = game:GetService("PathfindingService")
local lowerPositionThresholdOffset = -2
local groundMaterial = Enum.Material.Grass
local moveSpeed = 5
local searchRadius = 20
local jumpVelocity = 10
local jumpMoveSpeed = 7
local jumpForwardOffset = 3
local rootPart
local initialY
local function findNearestGround()
local nearestGroundPart = nil
local nearestDistanceSqr = math.huge
local searchOrigin = rootPart.Position
local partsToCheck = workspace:FindPartsInRegion3WithWhiteList(
Region3.new(searchOrigin - Vector3.one * searchRadius, searchOrigin + Vector3.one * searchRadius),
workspace:GetChildren()
)
for _, part in ipairs(partsToCheck) do
if part.Material == groundMaterial then
local distanceSqr = (part.Position - searchOrigin).Magnitude ^ 2
if distanceSqr < nearestDistanceSqr then
nearestDistanceSqr = distanceSqr
nearestGroundPart = part
end
end
end
return nearestGroundPart
end
local function moveToGround(groundPart)
local targetPosition = groundPart.Position + Vector3.new(0, humanoid.HipHeight / 2, 0)
local path = pathfindingService:CreatePath({
AgentRadius = 2,
AgentHeight = 3,
AgentCanClimb = true,
AgentCanJump = true,
})
local success, waypoints = path:ComputeAsync(rootPart.Position, targetPosition)
if success and #waypoints > 0 then
for _, waypoint in ipairs(waypoints) do
humanoid:MoveTo(waypoint.Position)
humanoid.MoveToFinished:Wait()
end
else
humanoid:MoveTo(targetPosition)
humanoid.MoveToFinished:Wait()
end
end
local function jumpAndMove(groundPart)
humanoid.Jump = true
humanoid.WalkSpeed = jumpMoveSpeed
local forwardVector = rootPart.CFrame.LookVector * jumpForwardOffset
local sideVector = rootPart.CFrame.RightVector * (math.random() - 0.5) * 2
humanoid:MoveTo(groundPart.Position + Vector3.new(0, humanoid.HipHeight, 0) + forwardVector + sideVector)
end
if humanoid then
rootPart = script.Parent:FindFirstChild("HumanoidRootPart")
initialY = script.Parent:GetPivot().Position.Y
while true do
local currentY = rootPart.Position.Y
local nearestGround = findNearestGround()
if currentY < initialY + lowerPositionThresholdOffset and nearestGround then
moveToGround(nearestGround)
humanoid.MoveToFinished:Wait()
jumpAndMove(nearestGround)
humanoid.MoveToFinished:Wait()
end
wait(0.1)
end
end```
This does not work because setting Humanoid.Jump = true only initiates the jump but calling MoveTo immediately after interrupts it MoveTo makes the humanoid start walking which cancels the jumpbefore it actually happens so the NPC never properly leaves the ground and fails to reach elevated targets
You should wait until the humanoid is truly in the air for example by checking HumanoidStateChanged for Freefall or use BodyVelocity to control the jump manually
edit line:
local function jumpAndMove(groundPart)
humanoid.JumpPower = jumpVelocity
humanoid.UseJumpPower = true
humanoid.Jump = true
task.wait(0.1)
local forwardVector = rootPart.CFrame.LookVector * jumpForwardOffset
local sideVector = rootPart.CFrame.RightVector * (math.random() - 0.5) * 2
local moveTarget = groundPart.Position + Vector3.new(0, humanoid.HipHeight, 0) + forwardVector + sideVector
humanoid:MoveTo(moveTarget)
end
local humanoid = script.Parent:WaitForChild("Humanoid")
local RunService = game:GetService("RunService")
local pathfindingService = game:GetService("PathfindingService")
local lowerPositionThresholdOffset = -2
local groundMaterial = Enum.Material.Grass
local moveSpeed = 5
local searchRadius = 20
local jumpVelocity = 10
local jumpMoveSpeed = 7
local jumpForwardOffset = 3
local rootPart
local initialY
local function findNearestGround()
local nearestGroundPart = nil
local nearestDistanceSqr = math.huge
local searchOrigin = rootPart.Position
local partsToCheck = workspace:FindPartsInRegion3WithWhiteList(
Region3.new(searchOrigin - Vector3.one * searchRadius, searchOrigin + Vector3.one * searchRadius),
workspace:GetChildren()
)
for _, part in ipairs(partsToCheck) do
if part.Material == groundMaterial then
local distanceSqr = (part.Position - searchOrigin).Magnitude ^ 2
if distanceSqr < nearestDistanceSqr then
nearestDistanceSqr = distanceSqr
nearestGroundPart = part
end
end
end
return nearestGroundPart
end
local function moveToGround(groundPart)
local targetPosition = groundPart.Position + Vector3.new(0, humanoid.HipHeight / 2, 0)
local path = pathfindingService:CreatePath({
AgentRadius = 2,
AgentHeight = 3,
AgentCanClimb = true,
AgentCanJump = true,
})
local success, waypoints = path:ComputeAsync(rootPart.Position, targetPosition)
if success and #waypoints > 0 then
for _, waypoint in ipairs(waypoints) do
humanoid:MoveTo(waypoint.Position)
humanoid.MoveToFinished:Wait()
end
else
humanoid:MoveTo(targetPosition)
humanoid.MoveToFinished:Wait()
end
end
local function jumpAndMove(groundPart)
humanoid.JumpPower = jumpVelocity
humanoid.UseJumpPower = true
humanoid.Jump = true
task.wait(0.1)
local forwardVector = rootPart.CFrame.LookVector * jumpForwardOffset
local sideVector = rootPart.CFrame.RightVector * (math.random() - 0.5) * 2
local moveTarget = groundPart.Position + Vector3.new(0, humanoid.HipHeight, 0) + forwardVector + sideVector
humanoid:MoveTo(moveTarget)
end
if humanoid then
rootPart = script.Parent:FindFirstChild("HumanoidRootPart")
initialY = script.Parent:GetPivot().Position.Y
while true do
local currentY = rootPart.Position.Y
local nearestGround = findNearestGround()
if currentY < initialY + lowerPositionThresholdOffset and nearestGround then
moveToGround(nearestGround)
humanoid.MoveToFinished:Wait()
jumpAndMove(nearestGround)
humanoid.MoveToFinished:Wait()
end
wait(0.1)
end
end
Calling Humanoid:MoveTo() after forcing a jump (humanoid.Jump = true) is unreliable, because MoveTo internally cancels jump momentum and waits for the character to land before completing.
And you’re adding an offset to groundPart.Position, but not using a path to it. If that new position isn’t walkable or is mid-air, the NPC will stop and do nothing.
MoveToFinished waits until the humanoid finishes walking to the destination. But if the NPC jumps, this can stall indefinitely if they never “finish” walking.
local humanoid = script.Parent:WaitForChild("Humanoid")
local RunService = game:GetService("RunService")
local PathfindingService = game:GetService("PathfindingService")
local groundMaterial = Enum.Material.Grass
local lowerPositionThresholdOffset = -2
local moveSpeed = 5
local searchRadius = 20
local jumpVelocity = 50
local jumpForwardOffset = 3
local rootPart = script.Parent:WaitForChild("HumanoidRootPart")
local initialY = rootPart.Position.Y
local function findNearestGround()
local nearest = nil
local nearestDist = math.huge
local origin = rootPart.Position
for _, part in ipairs(workspace:GetDescendants()) do
if part:IsA("BasePart") and part.Material == groundMaterial then
local dist = (part.Position - origin).Magnitude
if dist < nearestDist then
nearest = part
nearestDist = dist
end
end
end
return nearest
end
local function moveToGround(groundPart)
local target = groundPart.Position + Vector3.new(0, humanoid.HipHeight, 0)
local path = PathfindingService:CreatePath({
AgentRadius = 2,
AgentHeight = 5,
AgentCanClimb = true,
AgentCanJump = true,
})
local success, _ = pcall(function()
path:ComputeAsync(rootPart.Position, target)
end)
if path.Status == Enum.PathStatus.Complete then
for _, waypoint in ipairs(path:GetWaypoints()) do
humanoid:MoveTo(waypoint.Position)
humanoid.MoveToFinished:Wait()
end
else
humanoid:MoveTo(target)
humanoid.MoveToFinished:Wait()
end
end
local function jumpToward(groundPart)
local direction = (groundPart.Position - rootPart.Position).Unit
rootPart.Velocity = Vector3.new(direction.X * jumpForwardOffset, jumpVelocity, direction.Z * jumpForwardOffset)
end
while true do
local y = rootPart.Position.Y
local ground = findNearestGround()
if y < initialY + lowerPositionThresholdOffset and ground then
moveToGround(ground)
task.wait(0.2)
jumpToward(ground)
end
task.wait(0.1)
end
I tried it and it still did not work but without any errors so I just didnt say anything about it. When the NPC touches the part, it wont jump and move to the EDGE but a little bit forward afterwards. I want this to happen so not all of the NPCs will try to go to the middle
Why not make another script/section of the script where if the NPC touches land, they jump, allowing them to get onto Land? Just check if the NPC is swimming
Yeah so I went through all the replies and tested some things. The problem is that when you do humanoid.Jump = true and then immediately call MoveTo(), it cancels the jump before the NPC even leaves the ground.
MoveTo basically starts a walk command, which overrides the jump. That’s why sometimes the NPC doesn’t actually jump or move onto the land correctly.
What fixed it for me was waiting until the NPC is actually in the air before calling MoveTo.
local function jumpAndMove(groundPart)
humanoid.JumpPower = jumpVelocity
humanoid.UseJumpPower = true
humanoid.Jump = true
local jumped = false
local conn
conn = humanoid.StateChanged:Connect(function(_, new)
if new == Enum.HumanoidStateType.Freefall then
jumped = true
conn:Disconnect()
end
end)
local timeout = 0
while not jumped and timeout < 0.5 do
task.wait(0.05)
timeout += 0.05
end
local forwardVector = rootPart.CFrame.LookVector * jumpForwardOffset
local sideOffset = rootPart.CFrame.RightVector * math.random(-2, 2)
local moveTarget = groundPart.Position + Vector3.new(0, humanoid.HipHeight, 0) + forwardVector + sideOffset
humanoid:MoveTo(moveTarget)
end
This way the NPC actually jumps first, then moves while in the air toward a slightly randomized position so they don’t all land on the same spot.