What do I put instead of nil?

Hello, I have been trying to make an NPC follow a player without it getting stuck in walls, and I found a script for it!

But I don’t know what to put in the nil section, here is the script.

local service = game:GetService(“PathfindingService”)
local humanoid = script.Parent
local torso = script.Parent.Parent.Torso
local finishPosition = nil --Change this to the nearest players torso

local path = service:ComputeRawPathAsync(torso.Position, finishPosition, 512)
local points = path:GetPointCoordinates()
for p = 1, #points do
repeat
humanoid:MoveTo(points[p])
humanoid.MoveToFinished:wait()
until (points[p] - torso.Position).Magnitude <= 4
end

1 Like

Greetings! I understand and I will be able to help you right away. Before I help you can I know if the script is LOCAL or GLOBAL?

Its Global but the game has a max for 1 player anyway

Great! So put this in the ‘nil’ section…

local finishPosition = game.Players:GetChildren()[1].Character.Torso

But remember, this will make the system pick 1 person in the player’s list.*
If this won’t work, please notify me back. c:

If you want to get the position of the closest player you will just need to loop through all players.

-- get the torso position of the nearest player to 'pos'
local function getNearestPlayer(pos)
	local closest
	for _, player in ipairs(game.Players:GetPlayers()) do
		local t = player.Character and player.Character:FindFirstChild("Torso")
		if (t ~= nil) then
			if (closest == nil or (t.Position - pos).Magnitude < (closest - pos).Magnitude) then
				closest = t.Position
			end
		end
	end
	return closest
end

Then in your code set finishPosition to this:

local finishPosition = getNearestPlayer(torso.Position)

You can change this local finishPosition = nil to this local finishPosition = Vector3.new()

You have to wait for character and humanoid root part to load. This script is written for game with only one player, like you expect. Call chasePlayer() function to follow the player.

local Players = game:GetService("Players")
local PathfindingService = game:GetService("PathfindingService")

local humanoid = script.Parent
local hrp = script.Parent.Parent:FindFirstChild("HumanoidRootPart")

local playerRootPart

local function chasePlayer()
	if (not playerRootPart) then return; end
	local path = PathfindingService:ComputeRawPathAsync(hrp.Position, playerRootPart.Position, 512)
	local points = path:GetPointCoordinates()

	for p = 1, #points do
		repeat
			humanoid:MoveTo(points[p])
			humanoid.MoveToFinished:Wait()
		until (points[p] - hrp.Position).Magnitude <= 4
	end
end

Players.PlayerAdded:Connect(function(player)
	if (not player.Character) then player.CharacterAdded:Wait() end
	playerRootPart = player.Character:WaitForChild("HumanoidRootPart")	
end)

You can use this to safely get the first player:

local Players = game:GetService("Players")

local player = Players:GetPlayers()[next(Players:GetPlayers())]

EDIT
Alright, now we are checking for HumanoidRootPart. Try now.

´´´
repeat wait() until #game.Players:GetChildren() >= 1
local finishPosition = game.Players:GetPlayers[1].Character.Torso
´´´
This is my way, others may use just :WaitForChild() but I prefer this one just more.
Also, ‘Birdelther’ has a pretty good code, try his too. It is not limited.

I may have found a small problem, the script was going for a Torso but the charecter doesn’t have one… oops

image

Yeah, it doesn’t work, no error also, im think im going to leave it for now, thanks for helping tho!

@XShadowTheGamerX sorry for late reply. The reason why it’s not working is that it is actually working. :smile: Right after the player joins, PathfindingServe does recognize position, calculates the path and in fact moves the player. The problem is that there is no loop. Now, calculating path constantly for every move is obviously not a good idea, because the movement isn’t only stuttery, but script is performance heavy. What can we do? We can calculate the path less frequently. Correct, although NPC won’t follow player real-time, but instead take rapidly corrected paths. It’s going to look like NPC is a little lost. Ideally, we would use :MoveTo() function. It provides smooth movement, paths are direct. Back to the original issue. :MoveTo() is great, but doesn’t avoid obstacles. Ideally, we would somehow detect blockades (a good idea is raycasting), and only then trigger pathfinding.
Originally, I wanted to use

humanoid.MoveDirection:Dot(humanoid.MoveDirection) > 0
-- or (the above line seems faster and less demanding)
humanoid.MoveDirection.Magnitude > 0

to check whether NPC is moving or not, but apparently it’s dot product of it’s movement commonly falls to zero (which means it stops often for very short amounts of time). Calls for pathfinding are then frequent, and movement is stuttery again.

Because I am a bit in a hurry, I quickly created a coroutine, checking humanoid’s movement status every 3 to 4 seconds. This doesn’t seem too performance demanding, however, there certainly is a better solution, which also involves slightly different, smarter, but more complex script.

This is really not my proudest script, but it’s a solid solution.

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local PathfindingService = game:GetService("PathfindingService")

local humanoid = script.Parent
local hrp = script.Parent.Parent:FindFirstChild("HumanoidRootPart")

local find_path = false
local playerRootPart

local function checkForMovement()
	while (true) do
		if (humanoid.MoveDirection:Dot(humanoid.MoveDirection) == 0) then
			wait(.5)
			if (humanoid.MoveDirection:Dot(humanoid.MoveDirection) == 0) then
				find_path = true
			end
		end
		wait(3)
	end
end

local function chasePlayer(player)
	if (not playerRootPart) then return; end
	coroutine.wrap(checkForMovement)()
	while (true) do
		if (not find_path) then
			humanoid:MoveTo(playerRootPart.Position)
		else
			local path = PathfindingService:ComputeRawPathAsync(hrp.Position, playerRootPart.Position, 512)
			local points = path:GetPointCoordinates()

			for p = 3, #points do
				humanoid:MoveTo(points[p])
				humanoid.MoveToFinished:Wait()
			end
			find_path = false
		end
		RunService.Heartbeat:Wait()
	end
end

Players.PlayerAdded:Connect(function(player)
	if (not player.Character) then player.CharacterAdded:Wait() end
	playerRootPart = player.Character:WaitForChild("HumanoidRootPart")
	chasePlayer(player)	
end)

EDIT (2021-03-06)

@XShadowTheGamerX the script currently belongs inside humanoid of your NPC (like it was origianlly in your script). Should you decide to move it elsewhere, you have to change the paths as well.

1 Like

I am sorry for all the time I wasted, I just came back to the forums, but the script you sent me gave me this error. image

Or you could just use math.min with vector magnitudes