Displaying a Gui when a player is touching a part

I have a platform that when the user stands on it, a Gui should be displayed. This is not as easy as it seems like it should be, because there is no TouchStarted event, only a Touched event and a TouchEnded event. And the Touched event gets fired continuously as a user walks around on the part, rather than only the first time they touch it.

I have tried to do something like this:

script.Parent.Touched:Connect(function(Object)
	local Player = Players:GetPlayerFromCharacter(Object.Parent)
	if not Player then
		return
	end
	
	-- My own internal data structure that keeps pack of per-player session data.
	local SessionData = GameInstanceData[Player.UserId].SessionData
	
	if SessionData.PartGuiReason == Object then
		print("Not showing PlayerGui because one is already displayed.")
		return
	end
	
	SessionData.PartGuiReason = Player
	
	local MyGui = game:GetService("ServerStorage"):WaitForChild("MyGui")
	SessionData.PartGui = MyGui()
	SessionData.PartGui.Parent = Player.PlayerGui

	print("PlayerGui added for player %s" .. Player.Name)
end)


script.Parent.TouchEnded:Connect(function(Object)
	local Player = Players:GetPlayerFromCharacter(Object.Parent)
	if not Player then
		return
	end
	
	local SessionData = GameInstanceData[Player.UserId].SessionData

	if SessionData.PartGuiReason ~= Player then
		print("Not clearing PlayerGui because the TouchEnded event part did not match the PartGuiReason")
		return
	end
	
	SessionData.PartGuiReason = nil
	-- Unparent it to get it out of the player's Workspace.
	SessionData.PartGui.Parent = nil
	SessionData.PartGui = nil
	print("PlayerGui removed for player %s" .. Player.Name)
end)

But this doesn’t seem to work either, because I get tons of TouchEnded events as the right and left legs leave the ground during the walk animation.

I feel like I’m overcomplicating this though, and there should be like 3-5 lines of code I can write that just solve the problem trivially.

Am I missing something here?

5 Likes

I can’t seem to think of another way to do so using touched events, but you might try using region3. Put a region above the platform and detect when the user is in it and when the user leaves it. Region3 | Documentation - Roblox Creator Hub

You can try using debounce thing. Here’s how it works:

local debounce = false
script.Parent.Touched:Connect(function()
if debounce == false then
debounce = true --This will prevent function from firing multiple times
--Rest of your code here
end
end)
script.Parent.TouchEnded:Connect(function()
--Your code here
wait(1) --To prevent code from firing until player actually leaves the platform
debounce = false --Our code can run again
end)

I would have suggested that although another issue is that everytime a leg is lifted while walking on the platform the script sees that as a TouchEnded event which would negate the entire debounce system.

Yea, I’ve tried all of this. Another thing I’ve tried is making an invisible cylinder (or box) with CanCollide = false and putting a weld constraint on it so that it moves with my character, and then checking if the platform is touching this cylinder, rather than any part of the humanoid.

But even when I’m standing right smack in the middle of the platform, it keeps firing Touched followed immediately by TouchEnded, which doesn’t make any sense. It seems like Touch events are kind of just buggy :frowning:

Try doing this locally and raycast to the ground to see if the player is standing on the shop ground.
If they are then show the gui, if not then close it. You could also try detecting touched by placing an invisible region on the shop instead

Alright. Big-brain time.

local debounce = false
local debounce2 = false
script.Parent.Touched:Connect(function()
if debounce == false then
debounce = true --This will prevent function from firing multiple times
--Rest of your code here
end
end)
script.Parent.TouchEnded:Connect(function()
if debounce2 == false then
debounce2 = true
--Your code here
wait(1) --To prevent code from firing until player actually leaves the platform
debounce = false --Our code can run again
debounce2 = false
end
end)

I’m not sure if this works, but it should.

This seems promising. How to compute the ray normal to the bottom face of the HumanoidRootPart?

1 Like

You really don’t need to. Use <0, -dist, 0>

Why not just calculate the distance (for a radius) between the Player’s HumanoidRootPart and the shop thing itself. If the player is within the distance then the UI is open, the moment they aren’t you can simply ask for it to close. Using a debounce you can then prevent it from reopening. If you are worried about processing time then start capturing the moment the player touches a region. The moment they leave said region by a safe distance then end the capture and close the UI.

That way you can just use the x, y, z components of the position to validate whether a player has entered the desired region.

you can use this script:
(put this inside a part)

script.Parent.Touched:Connect(function(hit)
local player = game.Players:GetPlayerFromCharacter(hit.Parent)
if hit.Parent:FindFirstChild("Humanoid") ~= nil then
script.Parent.ShowUI:fireClient(player)
end
end)

For show ui:
(Put this on The UI and make sure that the ui is inside starterGui)

game.Workspace.Part.ShowUI.OnClientEvent:Connect(function()
script.Parent.Visible = true
end)
2 Likes

I wouldn’t recommend using region3, but I would however, recommend using .Magnitude.

So, what you will do is constantly check if the HumanoidRootPart is within a certain radius of something using Runservice. And once it’s in a certain magnitude, open the GUI

If you are worried that detecting the magnitude 40 - 60 times per second is too much(which will happen), you can use this method:

You can try detecting only if the right or left foot of the player is touching the part, so all you have to do is edit in this line to both your events:

Change:

if not Player then

to:

if not Player or Object.Name not == "RightFoot" then

If your stilling having a problem just loop creating a ray starting from you character and straight down and this will detect if you are standing on the platform and make sure when you find the part on the ray you use an ignore list of your character so the ray goes trough your character.
YourRayHere = Ray.new(YOURCHATACTERSHUMANOIDROOTPART, Vector3.new(0,-10,0)*100)

2 Likes

Hi Guys, I know this thread is over a year old but I was also having the same issue - I wanted to touch a part and have a message pop-up to the player for a short period of time and then vanish.

My solution eventually came down to something quite small and very usable and I want to share it here as I am sure this topic will come up many times in the future:

On the part being touched, I placed this (server) script:
function onTouched(part)
local h = part.Parent:findFirstChild(“Humanoid”)
if (h~=nil) then
local thisplr = game.Players:findFirstChild(h.Parent.Name)

	if part.Parent:FindFirstChild("TheMagicItem") then
		script.Parent:Destroy() -- destory or otherwise interact with the part
		print('Found Tool magic magic item') -- and place whatever you want here for testing purposes
	else
		print("player touched the part but needs a magic item to interact it")
		local GuiStal = game:GetService("ServerStorage").Guis

		script.Parent.Touched:connect(function(Part)
			local Player = game.Players:GetPlayerFromCharacter(Part.Parent)
			if Player and not Player.PlayerGui:FindFirstChild("Guis") then -- copies the GUI ONCE ONLY
				print("Adding the GUI to the player")
				GuiStal:Clone().Parent = Player.PlayerGui -- GUI pops up with message	
				wait(8)
				Player.PlayerGui:FindFirstChild("Guis"):Destroy() -- remove the GUI after 8 seconds
			end
		end)
	end
	
end

end

script.Parent.Touched:connect(onTouched)

Inside ServerStorage I created a folder called Guis and within that folder I added a screen gui which had a simple text label containing the message. When the part is touched, the GUI is copied from server storage and appears on the player’s screen. The after 8 seconds disappears as it is removed from the playergui.

This might not be the best implementation but it is small and works well.