Check if player left Region3

Simply how do I check if I play left a Region3 area, and destroy the force-field.

I can probably do the second part, but I wouldn’t mind the answer

robloxapp-20200719-0120075.wmv (4.2 MB)
Sorry For Download

while true do
	wait(0.1)
	local partsInRegion = workspace:FindPartsInRegion3(region, part, 1000)
	for i, partspawn in pairs(partsInRegion) do
		if partspawn.Parent:FindFirstChild("Humanoid") ~= nil then
			print("Player found in region SafeZone: ".. partspawn.Parent.Name)
			local char = partspawn.Parent
			if ff == false then --check for forcefield
				Instance.new("ForceField",char)
				ff = true
			end
			if ff == true and  --idk what goes here, to determine that the player left
			--destroy force field, and set ff to false
		end
		end		
end
	end

Any help or advice helps, I’m getting better at scripting but I’m still really new.

It looks like you’re doing this locally per player as you have a debounce ‘ff’ that’s not defined? If so, you’ll need to do this on the server as you’re instantiating a GameObject that won’t be replicated from client->server.

Using the variables in your code, a server implementation of this would be:

-- globals
local DEBUG = true --> Change to 'false' to stop displaying the Region3 as a red part

-- main
local region     = Region3.new(Vector3.new(0, 0, 0), Vector3.new(20, 20, 20))
local characters = { } --> Whitelist table for all characters currently in game
local inside     = { } --> Used to check whether a character is currently inside the region

local function characterAdded(character)
	local humanoid = character:WaitForChild 'Humanoid'
	
	--> Add the player's character to our active character list
	characters[#characters + 1] = character
	
	humanoid.Died:connect(function ()
		--> They're dead, so let's remove them from our active character list
		for i, char in next, characters do
			if char == character then
				table.remove(characters, i)
				break
			end
		end
	end)
end

local function playerAdded(player)
	player.CharacterAdded:connect(characterAdded) do
		--> If the player already has a character let's call the characterAdded function
		if player.Character then
			characterAdded(player.Character)
		end
	end
end

game.Players.PlayerAdded:connect(playerAdded) do
	--> If there are already players in the game that the event missed, let's call the playerAdded function
	for _, player in next, game.Players:GetPlayers() do
		playerAdded(player)
	end
end

game.Players.PlayerRemoving:connect(function (player)
	--> They're leaving so let's clean up our list
	local character = player.Character
	if character then
		for i, char in next, characters do
			if char == character then
				table.remove(characters, i)
				break
			end
		end
	end
end)

spawn(function ()
	local check = 0 --> Interatively incremented so we can tell whether they're currently in the 
	while true do
		check = check >= 1000 and 0 or check + 1 --> You could just bitshift this, but to keep memory low, let's reset the integer every once in a while
		
		--> First let's find all the characters that are currently within our region...
		local parts = game.Workspace:FindPartsInRegion3WithWhiteList(region, characters, 1000)
		for _, part in next, parts do
			if part.Parent then
				local humanoid = part.Parent:FindFirstChildOfClass 'Humanoid'
				if humanoid then
					local character = humanoid.Parent
					local player    = game.Players:GetPlayerFromCharacter(character)
					if player then
						print(player, 'is inside')
						
						--> They're inside, so let's give them the updated check integer
						inside[player.Name] = check
						
						--> Now let's give them a forcefield if they haven't already got one
						if not character:FindFirstChildOfClass 'ForceField' then
							Instance.new('ForceField', character)
						end
					end
				end
			end
		end
		
		--> Now let's look at our active list to make sure that they all have the most recent frame's check
		--> if not, we'll remove them from the list, and remove their forcefield
		for name, sum in next, inside do
			if sum and sum ~= check then
				local player = game.Players:FindFirstChild(name)
				if player then
					local character = player.Character
					if character then
						local ff = character:FindFirstChildOfClass 'ForceField'
						if ff then
							print(player, 'is outside')
							ff:Destroy()
							inside[name] = nil
						end
					end
				end
			end
		end
		wait(0.1)
	end
end)

-- Ignore this, it's just for debug purposes for spawning the red box if 'DEBUG' variable is set to true
do
	if DEBUG then
		local box = Instance.new 'Part'
		box.Anchored     = true
		box.CanCollide   = false
		box.Color 		   = Color3.fromRGB(200, 50, 50)
		box.Name 			   = 'DEBUG_REGION_BOX'
		box.Size 			   = region.Size
		box.CFrame       = region.CFrame
		box.Transparency = 0.7
		box.Parent 		   = game.Workspace
		
		for _, surface in next, {'TopSurface', 'BottomSurface'} do
			box[surface] = 0
		end
	end
end

Hopefully my minimal commenting will help you understand the above. If not, feel free to ask any questions. The idea is to keep track of which players are active in game, so you don’t have to call :FindPartsInRegion3 on every part in that region, and instead only look for character parts. Then once you know that, you just need to keep track of who was found to be in that region in that frame and add/remove the forcefield accordingly.

6 Likes

Instead of saying Instance.new(“forcefield,char”) do:

local ForceField = Instance.new(“ForceField”)
ForceField.Name = “ForceField”
ForceField.Parent = char

2 Likes

This shouldn’t make any difference.

Although, if you’re referring to the performence implications of using Instance.new(‘ForceField’, char) I agree that that method does usually incur a penalty. However, since we’re not setting any properties before it’s instantiated in workspace, it has very little effect on performance:

local reps = 1000 --[?] How many iterations

--> Iteratively test and get avg
local d1 = 0
for i = 1, reps do
	local start = tick()
	
	local ff = Instance.new('ForceField', game.Workspace)
	
	local finished = tick()
	d1 = d1 + (finished - start)
end
d1 = d1 / reps

--> Now let's test the other
local d2 = 0
for i = 1, reps do
	local start = tick()
	
	local ff = Instance.new('ForceField')
	ff.Parent = game.Workspace
	
	local finished = tick()
	d2 = d2 + (finished - start)
end
d2 = d2 / reps

--> Now display the results
local t1 = ('%f'):format(d1)
print(
	('Time with Instance.new(\'obj\', parent) -> %s s [%s]'):format(t1, d1)
)

local t2 = ('%f'):format(d2)
print(
	('Time with Instance.new(\'obj\').Parent = game.Workspace -> %s s, [%s]'):format(t2, d2)
)

local diff = ('%f'):format(math.max(d1, d2) == d1 and d1 - d2 or d2 - d1)
print(
	(math.min(d1, d2) == d1 and 'Instance.new(\'obj\', parent)' or 'Instance.new(\'obj\').Parent = game.Workspace') .. ' is faster by ' .. diff .. 's'
)

--[[
My results:

  Time with Instance.new('obj', parent) -> 0.000007 s [6.9293975830078e-06]
  Time with Instance.new('obj').Parent = game.Workspace -> 0.000008 s, [7.8639984130859e-06]
  Instance.new('obj', parent) is faster by 0.000001s

]]
2 Likes

I was digging for bronze, but I found gold… it works perfectly after a bit of modification

3 Likes