How Should I do This?

I’m trying to write a script for an enemy to come after a player, and this is my code so far:

local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local enemy = script.Parent
local enemyRootPart = enemy:FindFirstChild("HumanoidRootPart")

local dmg = 15
local aggroDist = 30
local resetDist = 50

Players.PlayerAdded:Connect(function(plr)
	local plrChar = plr:FindFirstChild("Character")
	local plrRootPart = plrChar:FindFirstChild("HumanoidRootPart")
	
	if (enemyRootPart.position - plrRootPart.position).magnitude >= aggroDist then
		print("Player within range")
	end
end)

But it gives me an error on line 12 saying “attempt to index nil.” What am I doing wrong?

It looks like you are trying to access the Character child of plr , but this child may not exist. This is why you are getting the “attempt to index nil” error.

Or, just slap a childadded function in there and it should fix it.

  • Use player.Character instead of player:FindFirstChild("Character")
  • Use .Position instead of .position
  • Use .Magnitude instead of .magnitude
  • Use <= instead of >= in your if statement.
2 Likes

I have no idea how to use ChildAdded.
How do I do that?

local RunService = game:GetService(“RunService”)
local Players = game:GetService(“Players”)
local enemy = script.Parent
local enemyRootPart = enemy:FindFirstChild(“HumanoidRootPart”)

local dmg = 15
local aggroDist = 30
local resetDist = 50

Players.PlayerAdded:Connect(function(plr)
plr.CharacterAdded:connect(function(plrChar)
local plrRootPart = plrChar:FindFirstChild(“HumanoidRootPart”)

if (enemyRootPart.position - plrRootPart.position).magnitude >= aggroDist then
print(“Player within range”)
end
end)
end)

now there’s an error at line 14. It says it indexed nil with “Position?”

so you’re handling player joining and enemy from a script that inside a model?

yes. The script is inside the enemy’s humanoid

If this script is in some type of NPC, it won’t work. PlayerAdded won’t be reliable because it only fires for new players. I strongly suggest moving the script and reprogramming it in a way to handle all enemies at once.

how would I find the NPC, though?

There’s a few ways.

  • Every type of NPC in their own folder
  • Every NPC in one folder and their type is assigned by name, attributes, tags, or children.
  • Purely using if statements (worst way)

Then you can detect who is an enemy easily.

ok, I’ll try re-writing it. Thanks!

1 Like

Easiest option would be to put them into a folder and use pairs loop to loop through them all

2 Likes

Although this is one way, it wouldn’t make sense to have a PlayerAdded Connection, it isn’t really ideal for what you are trying to do, use a for loop to Iterate over the current existing Players:

for _,player in game.Players:GetPlayers() do -- Iterates through Players
    if player.Character then -- If the Players Character Exists
        local target = player.Character -- target
        local Mag = (enemyRoot.Position - PlrRoot.Position).Magnitude -- Gets Distance

        if Mag > resetDist then return end -- ends function if Distance is greater
        if Mag < aggrodist then -- If Target is in Range
            print"Plr in Range"
        end
       
    end
end


local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local enemy = script.Parent
local enemyRootPart = enemy:WaitForChild("HumanoidRootPart") -- Just in case it has not loaded in

local dmg = 15
local aggroDist = 30
local resetDist = 50

Players.PlayerAdded:Connect(function(plr)
	local plrChar = plr.Character or plr.CharacterAdded:Wait() -- finds Character or Waits for character to be added.
	local plrRootPart = plrChar:WaitForChild("HumanoidRootPart") -- Wait For Child
	
	if (enemyRootPart.position - plrRootPart.position).Magnitude <= aggroDist then -- Capitalize M, < less than (if aggroDist is bigger)
		print("Player within range")
	end
end)

This would half work (unless you think the op is going to add something in the Mag > restDistance if statement.) If you think about the code, one person could be 1cm away from the monster while the other could be a million lightyears away. If the code detects that other person first, its going to stop the code before it could even check who else is nearby.

Another thing is that player:DistanceFromCharacter(position: Vector3) exist!

That wont happen as there is a Distance to check

I tested the code that uses it, and it works how i want it to, the NPC focuses on the Person its targetting until someone closer is there

Plus, you can do this:

local MaxDistance
for _,player in game.Players:GetPlayers() do -- Iterates through Players
    if player.Character then -- If the Players Character Exists
        local target = player.Character -- target
        local Mag = (enemyRoot.Position - PlrRoot.Position).Magnitude -- Gets Distance

        if Mag > resetDist then return end -- ends function if Distance is greater
        if Mag < aggrodist then -- If Target is in Range
            MaxDistance = Mag -- Sets Distance
        end
       
    end
end

You could, but a lot of people would avoid Player:GetDistanceFromCharacter