Making key-bind objects, but they all activate in unison

I’m working on a Sci-Fi sort of game and this is my first big project. In this game I thought it would be a nice idea to make every intractable object be binded to a key/Button (GUI button for mobile users once I get to that point). The problem I face is all said objects activate at once, instead of whichever is closest. I have it set up so that the objects should only be activated when the player is within a minimal distance to them, however it only does it with one door and none of the others.



local player = game:GetService("Players").LocalPlayer
local character = player.Character
local RunService = game:GetService("RunService")
local Events = game:GetService("ReplicatedStorage").Events
while not player.PlayerGui:FindFirstChild("HUD") do
	wait()
end
local HUD = player.PlayerGui.HUD

local Object_radius = 10
local ObjectInfo = {
	ObjectNear = false,
	ObjectName = "",
	}

RunService.Stepped:Connect(function(step)
	for _,v in pairs (workspace:GetDescendants()) do
		if v:IsA("Model") and v:FindFirstChild("Config") then
			if v.Config:FindFirstChild("Interactable") and v:WaitForChild("Base") then
				if player:DistanceFromCharacter(v:GetPrimaryPartCFrame().p) < Object_radius then
					--player.PlayerGui.HUD.Interact.TextLabel.Text = v.Config.Interactable.Controls.Interact.Value
					local Cam = workspace.CurrentCamera:WorldToScreenPoint(v:GetPrimaryPartCFrame().p)
					HUD.Interact.Visible = true
					HUD.Interact.Position = UDim2.new(0,workspace.CurrentCamera:WorldToScreenPoint(v:GetPrimaryPartCFrame().p).X,0,workspace.CurrentCamera:WorldToScreenPoint(v:GetPrimaryPartCFrame().p).Y)
					if v.Config:FindFirstChild("Open") then
						if not v.Config.Open.Value then
							HUD.Interact.InteractText.Text = "Open"
						else
							HUD.Interact.InteractText.Text = "Close"
						end
					end
					if v.Config:FindFirstChild("Locked") then
						HUD.Lock.Visible = true
						HUD.Lock.Position = UDim2.new(0,Cam.X,0,Cam.Y)
						if v.Config.Locked.Value then
							HUD.Lock.LockText.Text = "Unlock"
						else
							HUD.Lock.LockText.Text = "Lock"
						end
					end
					ObjectInfo.ObjectNear = true
					ObjectInfo.ObjectName = v.Name
					--print(ObjectInfo.ObjectName)
					--print(ObjectInfo.ObjectNear)
				else
					--player.PlayerGui.HUD.Interact.TextLabel.Text = ""
					HUD.Interact.Visible = false
					HUD.Lock.Visible = false
					HUD.Lock.LockText.Text = "Lock"
					HUD.Interact.InteractText.Text = "Interact"
					ObjectInfo.ObjectNear = false
					ObjectInfo.ObjectName = ""
				end
			end
		end
	end
end)

game:GetService("UserInputService").InputBegan:Connect(function(input, gameProcessedEvent)
	if not gameProcessedEvent then
		if input.UserInputType == Enum.UserInputType.Keyboard then
			if ObjectInfo.ObjectNear then
				if input.KeyCode == Enum.KeyCode.E then
					Events.Interact:FireServer(false)
				elseif input.KeyCode == Enum.KeyCode.R then
					Events.Interact:FireServer(true)
				end
			end
		end
	end
end)

The code is a bit messy, but I plan on cleaning it up once I can fix the issue.

I’m not quite sure why or how it happens. I tried figuring it out with no avail, and I can’t find anything about it online. If anyone could help me see what the problem is, I would greatly appreciate it.

Thank you in advance.

2 Likes

I believe you would be wanting to look at the ContextActionService for this

2 Likes

I tried looking into how I could use this. It is useful for a lot of other things though so thank you very much for that, but I can’t figure out how I can use it to solve this problem. All the doors run off one script using CollectionService since I didn’t think it would be a good idea to have a copy of the same script in 100s of doors that pretty much do the same thing.

1 Like

So we are gonna restructure you code a little bit.

RunService.Stepped:Connect(function(step)

You don’t need to check this frequently, it is wasteful and will not be performative

for _,v in pairs (workspace:GetDescendants()) do

That is all fine and dandy until you starting adding a map into the game and it has to iterate through every single step. Most people use tagging through the collections service.

if player:DistanceFromCharacter(v:GetPrimaryPartCFrame().p) < Object_radius then

So you’re not considering the case if there are two intractable objects both within the Object_radius So what you want to do is keep track of which object is the closest.

So here is a template of what your code should look more like




local ObjectRadius = 10;
local ClosestObject;

local ArrayOfObjects = {
	--Objects or collections Service
}

while wait(.2) do
	ClosestObject = TableOfObjects[1];
	for i = 0,#TableOfObjects do
		local v = ArrayOfObjects[i];
		if (CheckDistanceFromPlayer(v) < ObjectRadius ) then
			if CheckDistanceFromPlayer(ClosestObject) < CheckDistanceFromPlayer(v) then
				ClosestObject = v;
			end
		end
	end

	SetUpGuiStuff();--Where Gui what not goes.
end

I’m pretty sure setting it up this way will fix your problems as well.

How should I go about collecting the interactive objects for the array?

  1. Put them together in one place for quick and easy access.
  2. Tag them with a new tag called ‘Interactive’ and add it that way.
1 Like

Either one, whichever you feel is more convenient, end result is pretty much the same. Im personally a fan of tags tho

1 Like

Ok, I’m going to experiment with tags then. I’m new to using tags though. Thank you.

1 Like