How do I accurately check if a player is in a certain area?

Well, You used :Touched that relies on the user not doing anything, while getting the touching parts you basically just check if the user is there or not. not if it has moved

5 Likes

Ah, I’m sorry, I hadn’t thought about it.

Using maths:

  • For a rectangular set a region in terms of x, y, z, w, h, l such that:
    - x is the x position,
    - y is the y position,
    - z is the z position,
    - w is the width of the rectangular region,
    - h is the height of the rectangular region,
    - l is the length of the rectangular region.
  • Then compare the value of the players parts parts of the body (you could use recursion on each body part) to see if it fits with all the boxes at all the vertexs of the part. If it does then at least 0.001 studs of the players body is within said region.
  • You will have to use magnitude and radius for a circle alongside pythagoras in order to get pin point locations.

This is long though as maths in the third dimension is time consuming - doing that in my A Level project though and I’m predicted to do extremely well thanks to it as I’m not using a built in function.

1 Like

The thing here is that you aren’t really supposed to use GetTouchingParts for a region check. This is reinventing the wheel for resolving functions to check if a position is within another via mathematical equations, using Region3 or whitelist downcasting to a region identifier. The workflows are the same but Touched involves more work because it is a function tied to the physics pipeline.

Touched is not really an accurate or good way to check for presence in a region.

3 Likes

How would I check whenever a player enters the area and exits it without touch? Would I infinitely loop code that checks if the player is in the area?

1 Like

I think I’ll be using that code. I was just recently thinking about how to check if an NPC is in the area. So simple yet so brilliant. LOVE it. The exact type of code I can handle.

Something like that, yeah. You can check the coordinates of the character in 2D space (no height axis), unless you need regions across multiple floors as well which then you’ll need to do 3D space checking.

You can also alternatively set up region parts below the map and do a whitelisted downcast against those parts every frame (with STEPPED, not RenderStepped) to check what region part a player is at. You can then push changes if the region changes between the found value and the current.

An empty touched event for parts without collisions to make GetTouchingParts work is somewhat of an unsupported hack. Luckily it doesn’t cause undue physics simulation but now you’re taking up memory for a blank function which is also called every physics step when an intersection is found.

3 Likes

Why would I prefer detect player’s 2d space over 3d?

It’s use case dependent. In some cases, you may have regions in your world that are layered. For example, imagine a tower with two floors. Each of those floors you want a different region, yes? Therefore, you would need to work in 3D space, adding the Y coordinate, to determine what floor you’re on and thus determine a player region.

Now suppose you have that same tower but you have two regions: inside the tower and outside. You want the tower to be considered a region, regardless of how high the player is. We can then eliminate the need to check for any vertical axis and we can treat the entire X-Z space of the tower as a region.

The short of it is that when you have regions in different vertical heights, you want to check in 3D space. If you just have regions of a map and there doesn’t need yo be any layered regions (ground region plus some on top), we can just work in 2F space and ignore pointlessly accounting for height.

By the way, if you’re interested in region code because you want to make a sound system, SoundService.AmbientReverb has some great starter code you can reference from. It works in 3D space to define a cave ambient region, so anything above or below the cave is not part of the cave region but anything inside is.

Using the same scenario above: if we wanted to just make a whole section of the map a region regardless of where you were (even inside the cave), ignore height, just check 2D space. There are cases where you can combine them: you check 2D space X-Z for one region and Y to check for layered regions.

1 Like

I need to have some pads the player can kinda step on but if they jump or are just above them then it won’t count as stepping off the pad, and I need stuff happening precisely when the player steps onto the pad and steps off, and my scenario isn’t layered
I have a function that lets you check if a vector3 position is above a part’s position:

local function posAbovePart(pos, part)
	local siz = part.Size
	local opos = CFrame.new(pos):ToObjectSpace(part.CFrame)
	local c1 = false
	local c2 = false
	if math.abs(opos.X) < siz.X/2 and math.abs(opos.Z) < siz.Z/2  and opos.Y < 0 then
		return true
	else
		return false
	end
end

Do I just loop it constantly to check if a player is on or not? Is that a good idea performance-wise?
Is looping code constantly infinitely a good idea in general? (I’ve never been in such a scenario before)

1 Like

Ah, so you want to work with 3D space in this case then. What’s your use case: are you intending to make a sort of effect pad? Yeah, it’d be more understandable if you wanted to stick with a physics-based event then.

In terms of looping, you can cut costs immensely by only using a vector check only if a player is near a pad. That way, each iteration the engine performs an inexpensive distance check between a point and your pad’s position: if the point is near the pad, then it can perform the vector check.

The only real expense I see in your code is the creation of a new CFrame which leads me to confusion as to why you don’t do a raw comparison for a passed CFrame. You shouldn’t need to do object construction at all. That aside, nothing else is really expensive here.

Loops are fine. Just use them sparringly and rely more on events, such as from RunService. lso avoid looping where not necessary or preventing heavier operations from running if lighter ones do not pass.

1 Like

Is there a way I can get events that fire when player enters the pad or exits it?

You’ll either have to create your own events or continue using the originally marked solution, I’m afraid.

2 Likes

You said using the I don’t need to use the physics engine, that can be innacurate or ineffective or whatever. What would be a better solution in my case: Using the touch and gettouchedparts events or using the stepped event to loop the position check?

That would be fixed with a simple debounce.

1 Like

Debounce not worked, trust me. Now i would use the Zone+ Module mady by @ForeverHD, its really easy and helpfull.

1 Like

Do you know how I could go about regions based on position?

Hey thanks for your reply, does this also work with unions?

Yeah, it should work with unions (and any other part that can be collidable).

This code here will detect when a player is inside a part, and when the player leaves the part.

The code will be repeated but I suggest you modify what I made using debounce

Edit: I added in debounce for simplicity; it was a lot trickier to code than I thought.

-- local script

local Part = game.Workspace:WaitForChild("Vent1").Detect -- The part you want to detect
local tbl = {}  
local debounce = false
local debounce2 = false

game:GetService("RunService").Heartbeat:Connect(function(loop) -- Heartbeat loop
	task.wait() 

	if #tbl == 0 then -- if there isnt anything in table
		if not debounce2 then
			debounce2 = true
			print("Player Outside Part") -- run code

			debounce = false
		end
	end
	
	if #tbl ~= 0 then -- if there is a part in table
		if not debounce then
			debounce = true
			task.wait()
			print("Player Inside Part") -- run code
			debounce2 = false
		end
		
		table.remove(tbl, 1) -- removes the part from table
	end
	Part.Touched:Connect(function() end) -- creates a touch interest 
	local prt = Part:GetTouchingParts()[1] -- gets the touching parts of the specified part
	table.insert(tbl, prt) -- inserts the part into table
end)
3 Likes