Player opening Door Outside Radius

I’ve made a door system.

  • A player must be within radius of said door and press E to open.

The problem.

  • A player can be within radius once of said door and walk away, if not in a radius of another door, they can still open the same targeted door via pressing E.

I’ve looked around on DevForums and I can’t seem to get the same question around.

  • I’ve also tried making it so if you were further than the Radius, the Closest would be nil, but that seemed to break the entire script.

I do believe this is the code block that causes this sort of issue.

	if Doors ~= nil then
		for i, v in pairs(Doors) do			
			if Closest == nil then
				if (PlayerPosition - v.InteractPart.Position).magnitude < Radius then
					Closest = v
				end
			elseif (PlayerPosition - v.InteractPart.Position).magnitude < (Closest.InteractPart.Position - PlayerPosition).magnitude and (PlayerPosition - v.InteractPart.Position).magnitude < Radius then
				Closest = v
			end
		end
	end
  • PlayerPosition is defined as: Player.Character.HumanoidRootPart.Position
  • Closest is originally defined as: nil

https://gyazo.com/a3d27ad1670671a0809e3c47bfffb696

Is this code ran in a loop? And if so, is the Closest variable defined as nil inside or outside of that loop?

This code is ran in a while true do loop and yes, the Closest variable is defined inside the loop. So is the PlayerPosition variable.

After the elseif, add an else and set Closest to nil.

That just stops the code.

	if Doors ~= nil then
		for i, v in pairs(Doors) do			
			if Closest == nil then
				if (PlayerPosition - v.InteractPart.Position).magnitude < Radius then
					Closest = v
				end
			elseif (PlayerPosition - v.InteractPart.Position).magnitude < (Closest.InteractPart.Position - PlayerPosition).magnitude and (PlayerPosition - v.InteractPart.Position).magnitude < Radius then
				Closest = v
			else 
				Closest = nil
			end
		end
	end
  • I also made it print out Closest after the suggested code, and it now just prints out nil.

Edit One: It doesn’t change a single time, not even when inside the radius of the door.

Instead, do an elseif and check if the player is outside the radius of Closest. If they are outside the radius, then set Closest to nil.

To find the closest object I usually do something like this:

local closestObject = nil
local closestDistance = nil

for _, object in pairs(objects) do
    local distance = (object.Position - playerPosition).Magnitude

    if not closestObject or distance < closestDistance  then
        closestObject = object
        closestDistance = distance
    end
end

This basically sets the distance corresponding to the current closest object and compares the rest of the objects’ distances to the player against that.

2 Likes

I’ve shortened the code to just this and it still selects the same target even when v or Closest is set to nil.

	if Doors ~= nil then
		for i, v in pairs(Doors) do
			if Closest == nil then
				if (PlayerPosition - v.InteractPart.Position).magnitude < Radius then
					Closest = v
				elseif (PlayerPosition - v.InteractPart.Position).magnitude > Radius then
					Closest = nil
					v = nil
				end
			end
		end
	end

Edit One: When the player is outside radius, v and Closest are set to nil, yet it keeps targeting same door.

Edit Two: I now have reason to think it is the second code that is run afterwards inside the while true do loop.

	if Doors ~= nil then
		for i, v in pairs(Doors) do
			if Closest == nil then
				if (PlayerPosition - v.InteractPart.Position).magnitude < Radius then
					Closest = v
				elseif (PlayerPosition - v.InteractPart.Position).magnitude > Radius then
					Closest = nil
					v = nil
				end
			end
		end
	end
	for i, v in pairs(Doors) do
		if v == Closest then
            -- Do stuff here.
		end
	end

I made it print the player’s distance from the Door.

  • I set the Radius to 10
    Once the player went outside of the radius, the script ended itself. I’ll do some more testing.

Why do you do 2 for loops when you can just do

if Closest ~= nil then
      -- do stuff here
end

Try using ProximityPrompt, their property has this property for you to set.

I have just tried that and even without 2 for loops it still opens the door same way, but I guess thanks for the shortening the code part.

I don’t want to create a ProximityPrompt for this, or any sort of BillboardGui. I’m using a SurfaceGui on a part for this.

Why do you set the door to nil if the Player is outside of the radius?

Because when the player is out of the radius, the door, or v is set to nil to avoid being interacted with again. Which obviously doesn’t work.

this is the thing I use for this, idk if itll work with your system though

function getClosest(player)
	local ch = player.Character
	repeat wait() until ch ~= nil
	local root = ch.HumanoidRootPart
	local tbl = {}
	local lowest = 1000000
	local instance
	local typ
	for i,v in pairs(game:GetService("Workspace"):FindFirstChild("INTERACTABLES"):GetChildren()) do
		local real_name = string.split(v.Name, "_")[1]
		if real_name == "obj" then
			local z = v.Position
			local pos = root.Position
			tbl[(pos-z).Magnitude] = {["Instance"] = v, ["Type"] = "obj"}
		elseif real_name == "npc" then
			local z = v.HumanoidRootPart.Position
			local pos = root.Position
			tbl[(pos-z).Magnitude] = {["Instance"] = v, ["Type"] = "npc"}
		elseif real_name == "lock" then
			local z = v.Position
			local pos = root.Position
			tbl[(pos-z).Magnitude] = {["Instance"] = v, ["Type"] = "lock"}
		elseif real_name == "door" then
			local z = v.Position
			local pos = root.Position
			tbl[(pos-z).Magnitude] = {["Instance"] = v, ["Type"] = "door"}
		end
		
		
	end
	local data = game:GetService("HttpService"):JSONDecode(player:FindFirstChild("Data").Value)
	for i,v in pairs(tbl) do
		if i < lowest then
			lowest = i
			instance = v.Instance
			typ = v.Type
		end
	end
	return {lowest, instance, typ, player, data}
end
game.ReplicatedStorage.interact.OnServerEvent:Connect(function(player)
	local data = getClosest(player)
	if data[1] <= 25 then
		if data[3] == "obj" then
			print("Player is interacting with an object.")
			iapi:UpdateInventory(player,data[2].Name)
		data[2]:Destroy()
		elseif data[3] == "npc" and data[1] <= 10 then
			print("Player is interacting with an NPC.")
		elseif data[3] == "lock" and data[1] <= 15 then
			print("Player is attempting to unlock something. Importing lock libraries from object.")
			local dapi = require(game.ServerStorage.Modules.DoorHandler)
			dapi:openLock({["Player"] = player,["Instance"] = data[2], ["Data"] = data})
		elseif data[3] == "door" and data[1] <= 15 then
			local dapi = require(game.ServerStorage.Modules.DoorHandler)
			dapi:openDoor({["Player"] = player, ["Instance"] = data[2], ["Data"] = data})
		end
		
	end
end)

-- client
local cas = game:GetService("ContextActionService")
cas:BindAction("interact", function(n,is) if is == Enum.UserInputState.Begin then game.ReplicatedStorage.interact:FireServer() end end, false, Enum.KeyCode.E)


1 Like

I’ll definitely have a look at it and will get back to you.

I’ve looked at this, and I like the way you put your interactables in a table, unfortunately that just isn’t going to work with me. My snippet of code is entirely in a LocalScript.

Honestly I have been trying to find a solution for 21 whole days and I’ve found it. The problem was not identifying which door was closest to you, it was rather the user input still being connected despite out of range.

First it started with this following code snip.

UserInputService.InputBegan:Connect(function(input)
    if input.KeyCode == Key then
        --< Code blah blah blah
    end
end)

This was obviously a mistake as I didn’t disconnect the input once the key was pressed.

local keyconnect
keyconnect = UserInputService.InputBegan:Connect(function(input)
    keyconnect:Disconnect()
    --< Code blah blah blah
end)

This is the solution I have determined for this. Thank you for the few people that have tried to help me.