Trouble with forcefield giver/PVP zone (using magnitude)

I made a “PVP zone” using magnitude. Basically when in a certain radius of an invisible block it gives you a forcefield and if when you leave that radius it takes it away. The trouble is that I have two of these detection blocks and when you’re inside of one detection block and not the other, then you will still have a forcefield and it keeps getting taken away and given back over and over.

Script in block:

inside = false

game.Players.PlayerAdded:Connect(function(player)
	player.CharacterAdded:Connect(function(character)
		while wait() do
			for i,v in pairs(game.Players:GetChildren()) do
				if (character.HumanoidRootPart.Position - script.Parent.Position).magnitude <= 40 and inside == false then
					inside = true
					if character:FindFirstChild("ForceField") ~= nil then
						character:FindFirstChild("ForceField"):Destroy()
						end
					else
						inside = false
					if character:FindFirstChild("ForceField") ~= nil then
					else
						game.ReplicatedStorage.ForceField:Clone().Parent = character
					end
				end
			end
		end
	end)
end)

2 Likes

I wouldn’t really put a loop inside your CharacterAdded Event, especially if you’re putting another nested loop in there as well as it can affect a ton of unnecessary performance

I’d also consider making any variables you reference local, that way it’s easier to organize & navigate through

What you could possibly do is put a table of Players that are either far, or close to the Zones you’re wanting to put them on:

local Plrs = game:GetService("Players")
local Part = script.Parent
local Forcefield = game.ReplicatedStorage:WaitForChild("ForceField")

local CurrentCharacters = {}
local Rate = 1
local TargetDistance = 40

Plrs.PlayerAdded:Connect(function(Plr)
    Plr.CharacterAppearanceLoaded:Connect(function(Chr)
        table.insert(CurrentCharacters, Chr)
    end)
end)

while true do
    for _, Char in pairs(CurrentCharacters) do
        local Inside = false
        local Magnitude = (Char.Torso.Position - Part.Position).Magnitude
        local FFCheck = Char:FindFirstChild("ForceField")

        if Magnitude <= TargetDistance then --Check if the Player is close to the Part's Magnitude
            if FFCheck == nil then
                local FFClone = Forcefield:Clone()
                FFClone.Parent = Char
            end
        else
            if FFCheck ~= nil then
                FFCheck:Destroy()
            end
        end
    end
    task.wait(Rate)
end

Also 1 more thing I might suggest: If the ForceField inside ReplicatedStorage is an actual ForceField Class Object, I recommend using Instance.new("ForceField") to handle that instead of just having to clone the same item & parent it to the Character

1 Like

I tried putting this script in studio and for some reason it just doesn’t work, no error or anything.

Also thanks for the tip, I’ve changed some other scripts to create a forcefield instead of clone one. :+1:

1 Like

Where is the Part located exactly? I think I may have an alternative solution if it’s located in a specific spot

Either way, you could also try to use the GetPartBoundsInBox() or GetTouchingParts() functions since Magnitude is only reliant on a spherical type shape (Unless if you change the part to a sphere)

It depends on your use choice though, but say we wanna use the first function so we’d need to requite 3 parameters here:

  • 1 - The Part’s Originated CFrame
  • 2 - How far we want the Box to go via a Vector3 Size
  • 3 - An OverlapParams Data Type which can specify specific parts you want it to touch/not touch
local Plrs = game:GetService("Players")
local Part = script.Parent

local Protected = {}
local Rate = 1
local TargetSize = 80

local PartParams = OverlapParams.new()
PartParams.FilterType = Enum.RaycastFilterType.Blacklist
PartParams.FilterDescendantsInstances = {Part}

while true do
	local Parts = workspace:GetPartBoundsInBox(Part.CFrame, Vector3.new(TargetSize, TargetSize, TargetSize), PartParams)
	
	table.clear(Protected)

	for _, NearbyPart in pairs(Parts) do
		local HRP = NearbyPart.Name == "HumanoidRootPart"
		
		if HRP then			
			local Char = NearbyPart.Parent
			local FFCheck = Char:FindFirstChildOfClass("ForceField")
			
			table.insert(Protected, Char)

			if FFCheck == nil then
				
				local FFClone = Instance.new("ForceField")
				FFClone.Parent = Char
			end
	
		end
	end
			
	for _, Plr in pairs(Plrs:GetPlayers()) do
		local Char = Plr.Character

		if Char then
			local FFCheck = Char:FindFirstChildOfClass("ForceField")

			if FFCheck and not table.find(Protected, Char) then
				FFCheck:Destroy()
			end
		end
	end
	
	
	task.wait(Rate)
end

There’s a lot of that you may see here, but let’s go ahead and break it down:

Lines 1-6:

local Plrs = game:GetService("Players")
local Part = script.Parent

local Protected = {}
local Rate = 1
local TargetSize = 80

These are just our variables for easier definition & obtaining, nothing much here

Lines 8-10:

local PartParams = OverlapParams.new()
PartParams.FilterType = Enum.RaycastFilterType.Blacklist
PartParams.FilterDescendantsInstances = {Part}

Now these are what’s known as a new type, the OverlapParams which can create a specific boundary provided via the Origin’s CFrame, and overall Size you want it to tend

  • FilterType defines the type you want the Params to be: Blacklist will ignore any Parts within the boundary, and Whitelist only detects Parts within that boundary

  • FilterDescendantsInstances checks for what parts you want to Find/Ignore provided by the FilterType

Now moving on, inside of our loop here:

Lines 13-33

    local Parts = workspace:GetPartBoundsInBox(Part.CFrame, Vector3.new(TargetSize, TargetSize, TargetSize), PartParams)
	
	table.clear(Protected)

	for _, NearbyPart in pairs(Parts) do
		local HRP = NearbyPart.Name == "HumanoidRootPart"
		
		if HRP then			
			local Char = NearbyPart.Parent
			local FFCheck = Char:FindFirstChildOfClass("ForceField")
			
			table.insert(Protected, Char)

			if FFCheck == nil then

				local FFClone = Instance.new("ForceField")
				FFClone.Parent = Char
			end
	
		end
	end

A whole lot, but if we simplify everything it should be taken quite easily here :thinking:

Parts is what we use the GetPartBoundsInBox() function for, and referencing its parameters return back a Table of all the parts back within that Part Boundary

We also wanna go ahead and clear our Protected table using table.clear so that we can refresh which Character Models are exactly safe and not

Next, we use a pairs loop to go through that said Parts table and check for a specific Part Name, preferably the HumanoidRootPart and if we’re able to find one, we can create variables for both the Character, and a check for a ForceField inside that said Character! :slightly_smiling_face:

We also want to insert a Character inside our Protected function, so we use table.insert for that :stuck_out_tongue:

Then if our FFCheck variable returns back nil for the ForceField, then we can create one for our safe Character here

Lines 35-45

	for _, Plr in pairs(Plrs:GetPlayers()) do
		local Char = Plr.Character

		if Char then
			local FFCheck = Char:FindFirstChildOfClass("ForceField")

			if FFCheck and not table.find(Protected, Char) then
				FFCheck:Destroy()
			end
		end
	end

Now, a little additional stuff I added here is just simply to check for all Players using the GetPlayers() function, and we want to reference the Character yet again so that we can both check for the ForceField if any, and if they’re protected or not using the table.find() function

If both of our conditional statements are met, then we can remove that Forcefield as they’re not close in the safe zone :thinking: And hopefully, you’ll end up with something like this as the result!

Hopefully this helps you a bit on how these unique functions work, cause they’re really handy in situations like these

Here’s also the place file in case you wanna try it out for yourself:

SafeZonePlace.rbxl (33.3 KB)

1 Like

This doesn’t work with multiple detectors.

This also doesn’t work with multiple detection zones. Should I try making the table it’s own module that both of the scripts can access?

I mean you could do that, cause there are a bunch of ways you could go about it really more or less

My idea is that if you’re wanting to have more than 1 safe zone, I suggest putting all the parts inside a Folder, and then calling GetChildren() to loop through every single one of them so that you can check what’s valid, and encase it in separate coroutines as well

Also put your Script somewhere secure, like inside ServerScriptService so you fully know where it is

local Plrs = game:GetService("Players")
local PartFolder = workspace.SafeZones

local Protected = {}
local Rate = 1
local TargetSize = 80

local PartParams = OverlapParams.new()
PartParams.FilterType = Enum.RaycastFilterType.Blacklist
PartParams.FilterDescendantsInstances = {PartFolder}

while true do
	table.clear(Protected)
	
	coroutine.resume(coroutine.create(function()
		for _, SafeZone in pairs(PartFolder:GetChildren()) do
			local Parts = workspace:GetPartBoundsInBox(SafeZone.CFrame, Vector3.new(TargetSize, TargetSize, TargetSize), PartParams)


			for _, NearbyPart in pairs(Parts) do
				local HRP = NearbyPart.Name == "HumanoidRootPart"

				if HRP then			
					local Char = NearbyPart.Parent
					local FFCheck = Char:FindFirstChildOfClass("ForceField")

					table.insert(Protected, Char)

					if FFCheck == nil then
						local FFClone = Instance.new("ForceField")
						FFClone.Parent = Char
					end

				end
			end

			for _, Plr in pairs(Plrs:GetPlayers()) do
				local Char = Plr.Character

				if Char then
					local FFCheck = Char:FindFirstChildOfClass("ForceField")

					if FFCheck and not table.find(Protected, Char) then
						FFCheck:Destroy()
					end
				end
			end

		end
	end))
	
	task.wait(Rate)

end

I haven’t 100% tested this out, but I’m pretty sure this will work just fine

What do you mean by “multiple detectors”?

The script I provided could be copied into two zones & will work just fine.

Sorry for the late reply, but whenever I put two of these zones one of them doesn’t work.