I'm having trouble with the Touched event. Essentially I'm trying to make interact-able (?) shops. When the player touches an invisible part the script displays a prompt ("Press E to interact"). When the player presses E it then displays the shop GUI.
The prompt should obviously disappear once the player has pressed E and it should un-queue the E key from ContextActionService, however it appears this isn't happening every time - sometimes if you press E the prompt will stay on screen and then disappears when you press E a second time.
I added a couple of print()s with timestamps and it looks like the Touched event is being fired multiple times (particularly when walking through the invisible part) causing the PROMPT.SHOW() function to run multiple times as well.
Is there a fix for this or am I better moving away from the Touched event altogether? If so what is the best/most efficient alternative? Code is below, thanks
local INPUT = game:GetService("ContextActionService") local PLAYER = game:GetService("Players").LocalPlayer local CHARACTER = PLAYER.Character local TORSO = CHARACTER:WaitForChild("HumanoidRootPart") local ALL_SHOPS = game.Workspace:WaitForChild("Shops") local TOUCHING = nil local function OPEN_SHOP(_, STATE) if STATE == Enum.UserInputState.Begin then INPUT:UnbindAction("OpenShop") local HUMANOID = CHARACTER:WaitForChild("Humanoid") local LAST_WALKSPEED = HUMANOID.WalkSpeed HUMANOID.WalkSpeed = 0 HUMANOID:SetStateEnabled(Enum.HumanoidStateType.Jumping, false) script.Parent.Prompt.Visible = false local UI = script.Parent.Frame UI.ExitBtn.MouseButton1Down:Connect(function() UI.Visible = false if TOUCHING ~= nil then INPUT:BindAction("OpenShop", OPEN_SHOP, false, Enum.KeyCode.E) script.Parent.Prompt.Visible = true end HUMANOID.WalkSpeed = LAST_WALKSPEED HUMANOID:SetStateEnabled(Enum.HumanoidStateType.Jumping, true) end) UI.ScrollingFrame:ClearAllChildren() UI.TotalTxt.Text = "Total: £0.00" UI.ShopNameTxt.Text = TOUCHING.Parent.Name local ITEMS = require(TOUCHING.ModuleScript) for i, details in pairs(ITEMS) do local FRAME = script.ItemFrame:Clone() FRAME.Position = UDim2.new(0, 0, 0, FRAME.Size.Y.Offset * (i - 1)) FRAME.ImageLabel.Image = details[2] or FRAME.ImageLabel.Image FRAME.ItemTxt.Text = "£"..tostring(math.floor(details[3] * 100) / 100).." "..details[1] FRAME.Parent = UI.ScrollingFrame end UI.Visible = true end end local PROMPT = { SHOW = function() print("Showed prompt @ "..os.time()) INPUT:BindAction("OpenShop", OPEN_SHOP, false, Enum.KeyCode.E) script.Parent.Prompt.Visible = true end, HIDE = function() INPUT:UnbindAction("OpenShop") script.Parent.Prompt.Visible = false end, } TORSO.Touched:Connect(function(newPart) print("Touched new part: "..newPart.Name) if newPart:IsDescendantOf(ALL_SHOPS) and TOUCHING == nil then TOUCHING = newPart PROMPT.SHOW() end end) TORSO.TouchEnded:Connect(function(oldPart) if oldPart == TOUCHING then TOUCHING = nil PROMPT.HIDE() end end)
Answered on Discord, thanks to NewVoids. I added another print() to the TouchEnded event which it turns out was also firing which was setting TOUCHING to nil which is why the Touched event wasn't stopped by the if statement checking if TOUCHING == nil.
It's not a solution as such but as a workaround rather than just doingif oldPart == TOUCHING then
I instead used GetTouchingParts(), looped through all the parts touching the HumanoidRootPart, and if it included the invisible shop part I return'd to terminate the function. Entire script is the same save for this last bit:
TORSO.TouchEnded:Connect(function() for _, part in pairs(TORSO:GetTouchingParts()) do if part == TOUCHING then return end end -- From here on won't run if the torso is still touching the invisible shop part! TOUCHING = nil PROMPT.HIDE() end)