Pressing Escape Makes All Hidden GUIs Visible

I have a full screen shop GUI in my game and when the user presses ESC, the GUI becomes visible underneath the ROBLOX GUI.

The shop dialog is Visible = false and I use this property to toggle whether it is modal or not.

It’s quite ugly.

1 Like

Wait… what? The shop dialog is set to Visible=false, but then the menu opening makes it visible again?

Yes.

After more investigation I think that there is a race condition somewhere.

The core scripts should not be touching the .Visible property of my GUIs. If they want to hide all other GUIs they should do so with a lower level call.

My code uses the Visible property to figure out when to open/close the dialog based on input from the controller. It doesn’t expect anyone else to come along and start messing with it.

I wonder if there is a bad interaction with Filtering Enabled. That is also a possibility.

What signals are you listening to to open and close your guis? Can we see any code? I’ve never heard of this happening to anyone else, it sounds like a problem on your end.

YES YES I’ve seen this happen before!!
Sometimes when you press ESC, the input event gets fired with other values than expected.
I think I fixed it by checking to see if my event was given the correct keycode.

You can see how this code depends on someone else not setting the Visible property of GUIs that they do not own.

function OpenDialog()
	print("Open")
	if (shopDialog.Visible == false) then
		
		shopDialog.Visible = true
		
		FreezeCharacter(true)
		
		shopBtn.ImageRectOffset = btnRect.Pressed
		
		DoLayout()
		
		GUI.SelectedObject = script.Parent.ShopDialog.Frame.BuyButton
		
		

		CAS:BindAction("CloseShop", CloseDialog, false, Enum.KeyCode.ButtonB)
		CAS:UnbindAction("OpenShop")
	end
end
function CloseDialog()
	print("Close")
	if (shopDialog.Visible == true) then
		shopDialog.Visible = false
		
		FreezeCharacter(false)
		
		if (UIS.GamepadEnabled == true) then
			shopBtn.ImageRectOffset = btnRect.Normal
		else
			shopBtn.ImageRectOffset = btnRect.Hover
		end
		
		CAS:BindAction("OpenShop", OpenDialog, false, Enum.KeyCode.ButtonY)
		CAS:UnbindAction("CloseShop")
	end
end


shopBtn.MouseEnter:connect(
	function()
		if (shopDialog.Visible == true) then
			shopBtn.ImageRectOffset = btnRect.Pressed
		else
			shopBtn.ImageRectOffset = btnRect.Hover
		end
	end
)

shopBtn.MouseLeave:connect(
	function()
		if (shopDialog.Visible == true) then
			shopBtn.ImageRectOffset = btnRect.Pressed
		else
			shopBtn.ImageRectOffset = btnRect.Normal
		end
	end
)

shopBtn.MouseButton1Click:connect(
	function()
		if (shopDialog.Visible == false) then
			OpenDialog()
		else
			CloseDialog()
		end
		
	end
)



CAS:BindAction("OpenShop", OpenDialog, false, Enum.KeyCode.ButtonY)

I also saw this happen today on the mobile version of Work at a Pizza Place.

Can you reproduce it reliably?

If so, try changing the keycode in those calls to CAS.BindAction, or disable them completely, and see if you can still reproduce it. Maybe the menu / escape key is for some reason triggering events from CAS that it should not.

BindAction will fire a cancel event to actions in the event something is bound over them (this is useful if your movement action is being bound over, so you can stop the character moving, etc.). What is happening here is when you open the menu we stop movement controls on the controller so you aren’t moving the character while navigating the menu (via BindCoreAction).

Functions bound by BindAction can take 3 arguments (the name of action, the state of input that fired the action, and the actual input object that fired it). In the case of opening the menu, you would get the name of the action you assigned and Enum.UserInputState.Cancel.

This tutorial goes into more depth: http://wiki.roblox.com/index.php?title=ContextActionService_tutorial

Is it guaranteed that the player scripts that ROBLOX injects will bind first?

Is it guaranteed even if I have StarterGui.ResetPlayerGuiOnSpawn = false?

A useful debug view would be to see all of the stack of all actions currently bound and which scripts registered them.

Yes, they use a higher priority bind that are always processed first, which means you will always get a cancel event from this.

Yeah, this does sound useful, I’ll add it to the list…

Is this something that only ROBLOX can do, or can I do it too? How do I do this? I had this exact question a week ago - http://devforum.roblox.com/t/how-do-i-cancel-all-input-events-going-into-the-character/24851

I have read your post several times and I still don’t understand how to keep this race condition from happening.

Are you saying there is a cancel event that my code needs to subscribe to so that I can remove my GUI in the case that ROBLOX wants to pop up their GUI?

I’m pretty sure this is not a race condition. Essentially when the ROBLOX menu opens it is calling OpenDialog, because it is bound by ContextActionService and the ROBLOX menu is binding another function to the same user input. When this happens, we send a cancel event to the previous top of the stack, so it can do clean up if necessary.

The problem is OpenDialog does no checking of the args that are sent to it. If you update the function to do something like

function OpenDialog(actionName, userInputState, inputObject)
   if userInputState == Enum.UserInputState.Cancel then return end

Your problem should go away.

although probably a better solution would be

function OpenDialog(actionName, userInputState, inputObject)
   if userInputState ~= Enum.UserInputState.Begin then return end 

mostly to make sure your code only happens once on button press, otherwise it technically could happen on up and down of the button, although this is not the case right now because it is unbound at the end of this function.

I will give it a try and report back, thanks!