Detecting Players within Area/FOV

I’m trying to make an AI pillbox system similar to that of games like Centaura or Trenches: Beta.

I have tried to use parts and detect all the players/humanoids that walk into it but it seemed very intensive and would lag a LOT when there were many entities.

What I have tried doing is detecting all players touching the part, raycast to them, and if it succeeds fire a bullet at them.

However, when I tested this with more than 4 entities, all the touching events and raycasts started to slow the game down by a lot.
I’m trying to find an alternate solution to this, as my current code already works fine.

Also, if you how games like Centaura do it, please tell me! :slight_smile:

I have a diagram below:

2 Likes

I could try to explain this but I don’t even understand how this works. But in other forum post, a guy gave a solution that probably works well:

Helping Post by sleitnick

Hope it helps!

3 Likes

Do you care if it’s a cone or does it have to have an exact horizontal and vertical angle?

I would prefer it to be a perfect angle shape, but a cone is fine too! :smiley:
Currently my part model that uses touching is a cylinder, so if you stand on top of the pillbox it manages to shoot you.

I think for my current situation I would do it like:

  1. Get all players within view area
  2. Get the closest player
  3. Raycast to that player
  4. Shoot the bullet

Also, I am a bit confused with unit vectors, could you explain it for me?
I will try and implement this code to see if it works.

Begin by calculating the direction towards the player. Once you have this, calculate the dot product of the weapon’s look-vector and that direction. The dot product of two vectors is a measure of how parallel they are. The range of that value is [-1, 1], where -1 is parallel in the opposite direction, 1 is parallel in the same direction, and 0 is perpendicular in both directions.

Since you only care for those in front of the weapon, you’ll want to look at range [0, 1]. I doubt you’ll want the weapon to have a FOV of 90° though, so [0.25, 1] may be more appropriate as it gives your weapon a FOV of 22.5°

local function inSightOf(weapon: Model, character: Model): boolean
    local weaponPivot        = weapon:GetPivot()
    local characterPivot     = character:GetPivot()
    local characterDirection = (characterPivot.Position - weaponPivot.Position).Unit

    return weaponPivot.LookVector:Dot(characterDirection) >= PARALLEL_THRESHOLD
end

Where PARALLEL_THRESHOLD is the lower bound of the previously discussed appropriate ranges, for example, 0.25

1 Like

After making my code, it didn’t go how I expected. The turret detects me as I’m in the FOV, but if the CLOSEST Humanoid is OUTSIDE of the FOV, it breaks and stops firing.

Does anybody have an idea about how to fix this?
The video below describes the behaviour:

My script is here:

local HumanoidRootPart = script.Parent

local Humanoids = workspace

local MaxRadius = 130

local function FindNearest()
	Closest = nil -- Setting to nil since there is no reason to be set to anything.
	local ClosestDistance = math.huge -- Setting it to math.huge in order to compare it.

	for _, TargetCharacter in ipairs(Humanoids:GetChildren()) do -- Looping through all children of the "Humanoids" folder. In your case, this could be different, along with all the checks.
		local TargetHumanoid = TargetCharacter:FindFirstChild("Humanoid") -- Gets the Humanoid of the model.
		local TargetHumanoidRootPart = TargetCharacter:FindFirstChild("HumanoidRootPart") -- Gets the HumanoidRootPart of the model.
		if TargetCharacter:IsA("Model") and TargetHumanoid and TargetHumanoidRootPart and TargetHumanoid.Health > 0 then -- Checking if the "TargetCharacter" is a model, has a Humanoid, has a HumanoidRootPart, and if the humanoid's healt his bigger than 0. This check could be specialized for your case.
			local Distance = (HumanoidRootPart.Position - TargetHumanoidRootPart.Position).magnitude -- Gets the distance between the character's HumanoidRootPart and the target's HumanoidRootPart.
			if Distance <= MaxRadius and Distance < ClosestDistance then -- Checking if the character is within our max radius and if the distance is smaller than the closest distance so far.
				Closest = TargetCharacter -- Setting the closest character to the current one.
				ClosestDistance = Distance -- Setting the closest character distance to the current one.
			end
		end
	end

	if not Closest then ClosestDistance = nil end -- Set the ClosestDistance to nil if there is no close player.
	return Closest, ClosestDistance -- Return Closest and ClosestDistance.
end


-- Field of view (in degrees):
local FOV = 70

function LookCheck()
	if not Closest then return end
	-- Define stuff:
	local part = workspace.TurretEmplacement["MG-08"].AimPart
	local point = Closest.Torso
	local fovCheck = math.rad(FOV) / 2

	-- Find angle between two normalized vectors:
	local function AngleBetween(vectorA, vectorB)
		return math.acos(math.clamp(vectorA:Dot(vectorB), -1, 1))
	end

	-- Get the two lookvectors:
	local lookForward = part.CFrame.LookVector
	local lookToPoint = (point.Position - part.Position).Unit

	-- Calculate angle between our two lookvectors:
	local angle = AngleBetween(lookForward, lookToPoint)

	-- Check if the angle is within range:
	if math.abs(angle) <= fovCheck then
		print("Detected"..Closest.Name)
		return Closest
	end
end

while wait(0.35) do
	FindNearest()
	Target = LookCheck()
	if Target then
		script.Shoot:Fire(Target)
	end
end

Thanks in advance!

Two super quick things, and some terminology before going into it; right at the start of your script you have a variable called “Humanoids”. This points to a folder containing what we will refer to as “Candidates” (A thing we’re searching for when we’re looking through the folder).

-! In line 11, you’re using the :GetChildren() command for Humanoids. Theres nothing wrong with this itself, but the reason this will take so long is because you’re searching through every object directly parented to the workspace. Every part making up your game-map, every model, every player, everything is all considered a potential candidate.
-? Why is this bad? It’s going to create lag. You’re just looking for things with Humanoids in, and you don’t need to consider anything else as a candidate. Save yourself some time and instead think about using the CollectionService to find all the humanoids. Check it out here; CollectionService | Documentation - Roblox Creator Hub You’ll first have to find all the humanoids in the game like you’re doing now, but once you do you won’t have to search through every model again! Not an immediate issue, but the bigger your game gets, the more important it’ll be.

-! You’re only ever considering the Closest humanoid, not the closest in-range. Looking at the current loop;
→ 1. Find the closest humanoid, set a global value to say what humanoid that is.
→ 2. Check to see if that single humanoid is within the correct angle.
→ 3. If that humanoid is in angle, fire. If that humanoid is not in the correct angle, do nothing.

-? Why is this bad? It means that if there is something in the angle, but even just a little bit further away, it’ll never be considered as a potential candidate! Instead, you’ve got to make some changes in order to find a valid target.

Consider instead a loop like this;
→ 1. Find all Humanoids, place them in a list, closest to furthest. (It might be worth setting a maximum distance for this)
→ 2. Iterate through that list until you find the closest humanoid that is within your angle
→ 3. If you have a humanoid in range, Shoot.

Boom. Now you’ll have a loop that will always fire at whatever is closest and in front of your turret!

Thanks for your detailed response!
After tinkering with my code, I just couldn’t make a version that would work.

It would be great if you could make an example for me!

Also, the Humanoids is pointing to the ENTIRE workspace, as I intend to sell this on the Toolbox, I don’t think using the Collection service will be necessary.

Thank you for replying! :slight_smile:

Did you use getpartsinparts to detect the players that went in the radius? (On the previously mentioned attempt to detect players by detecting them when they walk in the radius)

I don’t quite get what you mean, is it in my detection script or something else?

Nope, I didn’t use GetPartsInParts. I just compared the distances between the Humanoids and get the one which is the closest.

I ended up not managing to make this work how I wanted, but the behavior here should help some people who want to make something similar.

Thanks for making me almost believe I could code!