Raycasting to each NPC's HumanoidRootPart inside a folder

I know how to raycast, I know how to loop through folders and tables, I know how to get descendants etc…

My question is, I got the HRP of each NPC, but how would I raycast independently to each HRP?

I’ll answer any questions you have, I know I was a bit vague.

1 Like

You already answered your question if you know how to raycast, loop thru folders, and get descendants.

The direction of the raycast is NPCRootPartPosition - OriginPosition

I know how to calculate the direction.

My question is how to raycast to the other NPCs? It seems like my code won’t switch to the other NPC. I checked everything and it does indeed know that there is more than one entity in the folder, yet it only ever loops through one.

Are you returning a value too early?

Nope, I’m only ever just raycasting and checking if the NPC is visible. The only time I do something with a value is when I’m setting it at the end (it’s for something else, and that works perfectly.)

Is it possible to provide code? It’s difficult to understand what’s going on without knowing what was written.

If I was to raycast to each HumanoidRootPart inside of a folder, I would do something along the lines of this:

local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local playerHRP = character:WaitForChild("HumanoidRootPart")
local folder: Folder = workspace.NPCs
local NPCs: {Model} = folder:GetChildren()

local function getVisibleHRPs(): ({Model})
	local visible: {Model} = {}
	local currPos: Vector3 = playerHRP.Position
	local params: RaycastParams = RaycastParams.new()
	params.FilterDescendantsInstances = {character}

	-- loops through every NPC model
	for _, NPC in ipairs(NPCs) do
		local npcHRP: BasePart = NPC:FindFirstChild("HumanoidRootPart")
		if not npcHRP then
			continue
		end

		local direction: Vector3 = currPos - npcHRP.Position
		local result: RaycastResult = workspace:Raycast(currPos, direction, params)

		-- adds them into the table if visible
		if result and result.Instance == npcHRP then
			table.insert(visible, NPC)
		end
	end

	-- returns them
	return visible
end
1 Like

Huh, I’ve done things much differently… Never knew that what I was trying to do was this advanced… Could you please (if it’s not a problem for you) explain what some things do in your script and how to use it? Mainly stuff like
visible >>: {Model} = {}<<

Most things I understand, if you don’t feel like explaining it, I’ll just mark you response as the answer, otherwise I’ll mark your explanation as the answer.

It was not my intention to be spoonfed code, it just appears that I took on something way out of my skill range…

Anything after the : is just declaring a type. It isn’t necessary, but just something that helps with things like autocomplete and readability.

If you used this script instead, the same thing would work.

local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local playerHRP = character:WaitForChild("HumanoidRootPart")
local folder = workspace.NPCs
local NPCs = folder:GetChildren()

local function getVisibleHRPs()
	local visible = {}
	local currPos = playerHRP.Position
	local params = RaycastParams.new()
	params.FilterDescendantsInstances = {character}

	-- loops through every NPC model
	for _, NPC in ipairs(NPCs) do
		local npcHRP = NPC:FindFirstChild("HumanoidRootPart")
		if not npcHRP then
			continue
		end

		local direction = currPos - npcHRP.Position
		local result = workspace:Raycast(currPos, direction, params)

		-- adds them into the table if visible
		if result and result.Instance == npcHRP then
			table.insert(visible, NPC)
		end
	end

	-- returns them
	return visible
end

What the idea behind this script is that it:

  1. Defines all necessary variables, such as where the NPC folder is, the player’s character, etc.
  2. Loops through every NPC model
  3. Gets the direction and raycasts from the player to the NPC
  4. If the raycast hits the NPC HRP, it adds it into the visible table
  5. Returns all visible NPCs

I also realized that I made a small mistake with the code before. It now checks if the instance that is hit is the NPC itself.

If there’s any other questions on what this script does, feel free to ask!

1 Like

Probably me being stupid.

How do I… Use the function…

I’m used to having to insert arguments into functions like these and now I’m just totally lost…

I declared a variable before the function which defines what to loop through. If you’d like more customization through parameters (which is better than what I did before), you can use this script:

local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local folder = workspace.NPCs

local function getVisibleHRPs(playerHRP, NPCFolder)
	local visible = {}
	local currPos = playerHRP.Position
	local params = RaycastParams.new()
	params.FilterDescendantsInstances = {character}

	-- loops through every NPC model
	for _, NPC in ipairs(NPCFolder) do
		local npcHRP = NPC:FindFirstChild("HumanoidRootPart")
		if not npcHRP then
			continue
		end

		local direction = currPos - npcHRP.Position
		local result = workspace:Raycast(currPos, direction, params)

		-- adds them into the table if visible
		if result and result.Instance == npcHRP then
			table.insert(visible, NPC)
		end
	end

	-- returns them
	return visible
end

Don’t be afraid of asking questions! I’m not the type of person to yell at someone else, and many people here on the DevForum aren’t, either.

1 Like

It does help, however I should’ve phrased myself a bit better.

Since the 2nd version, whatever I’ve done with the function always returned nothing. I’m asking how to use it because my silly little brain can’t handle a function it seems…

Also thank you a lot! You’re really helpful, I just hope I’m not taking up a lot of your time

Okay, made a few more changes to the script. It sounds like the raycast is hitting the other parts of the NPC instead of the humanoid root part, so I made it so that the script detects if whatever is hit belongs to the NPC.

local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local folder = workspace.NPCs

local function getVisibleHRPs(playerHRP, NPCFolder)
	local visible = {}
	local currPos = playerHRP.Position
	local params = RaycastParams.new()
	params.FilterDescendantsInstances = {character}

	-- loops through every NPC model
	for _, NPC in ipairs(NPCFolder) do
		local npcHRP = NPC:FindFirstChild("HumanoidRootPart")
		if not npcHRP then
			continue
		end

		local direction = currPos - npcHRP.Position
		local result = workspace:Raycast(currPos, direction, params)

		-- adds them into the table if visible
		if result and result.Instance.Parent == NPC then
			table.insert(visible, NPC)
		end
	end

	-- returns them
	return visible
end

If this script doesn’t work, is it possible to provide a screenshot of the NPCs that you’re working with?

game.rbxl (63.7 KB)

Here is the place file, should help. I think it’s an issue with me, am I printing wrong? (deleted everything I added so you can navigate it easier)
NPCs are in workspace;
Script is in StarterCharacter.

1 Like

Found the problem. Turns out I did the math wrong and had the subtraction reversed.

game-fixed.rbxl (64.1 KB)

I also added a quality of life function which will allow you to check if the raycast works as intended.

1 Like