Exiting a Screen Works Inconsistently

Hello developers. I was wanting to create a simple exit screen function for a ScreenGui in my game. So far, you press a proximity prompt to make the screen pop up and parent to the PlayerGui, where a local script can then run and set up a .Activated event for the button you click to destroy the ScreenGui. The problem is, this works the first time around, but when you click the proximity prompt and try to exit out of the screen again, it doesn’t. I’m not sure if the problem lies in the local script, the server script, or both. I’ll also include a snapshot of the explorer panel. It seems like such a simple problem, but because of how it works so inconsistently, this is what’s throwing me off.

Server script:

local ProximityPromptService = game:GetService("ProximityPromptService")
local CollectionService = game:GetService("CollectionService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local StartGameFolder = ReplicatedStorage:WaitForChild("StartGameFolder")

local StartGameGui = StartGameFolder.StartGameGui
local StartGameEvent = StartGameFolder.StartGameEvent

local Modes = {
	Classic = {"Diagonal"}
}

for _, prompt in CollectionService:GetTagged("startgame") do
	prompt.ObjectText = "Start game"
end

local function startGame(prompt, player)
	if CollectionService:HasTag(prompt, "startgame") then
		local StartGuiClone = StartGameGui:Clone()
		
		StartGuiClone.Parent = player.PlayerGui
		StartGuiClone:FindFirstChild("StartGui", true).Enabled = true
		
		StartGameEvent:FireClient(player, StartGuiClone)
	end
end

ProximityPromptService.PromptTriggered:Connect(startGame)

Local script:

local ReplicatedStorage = game:GetService("ReplicatedStorage")

local StartGameFolder = ReplicatedStorage.StartGameFolder

local StartGameEvent = StartGameFolder.StartGameEvent

local connection = nil

local function startGui(startGuiClone)
	connection = startGuiClone:FindFirstChild("ExitScreen", true).Activated:Connect(function()
		connection:Disconnect()
		startGuiClone:Destroy()
	end)
end

StartGameEvent.OnClientEvent:Connect(startGui)
1 Like

Hello! I believe it might be because you are cloning the GUI to the player server-side and then deleting it only client-side which may cause issues. Also make sure to check the output for any errors, those are very helpful when debugging.

There weren’t any errors but deleting it clientside may have been the problem, I’ll try doing it on the server, thanks!

Here is what I added, and it does delete the Gui, but also my character and player. I didn’t even remotely add anything having to do with either, do you know what could’ve happened?

StartGameEvent:FireServer(startGuiClone)
StartGameEvent.OnServerEvent:Connect(function(guiToDelete)
	guiToDelete:Destroy()
end)

This is a pretty painful thing to figure out the first time around, but it’s a pretty simple fix - OnServerEvent passes the player as the first argument.

This means your guiToDelete isn’t the GUI. You are deleting the player, because the events don’t care about the names of the values you pass, they just pass them in order with the Player always first. (The Player associated with the client that the FireServer() call originates from.)

The fix is as simple as adding a value to handle the player instance first:

StartGameEvent.OnServerEvent:Connect(function(player,guiToDelete)
	guiToDelete:Destroy()
end)

Here’s a link to the documentation that explains it:

https://create.roblox.com/docs/reference/engine/classes/RemoteEvent#OnServerEvent

My bad, I’m just now getting back into scripting. Even after doing so, it won’t delete the screen a second time around. If you keep pressing the prompt it will stack the clones and pressing the X will delete all those clones down to just one, and I have no idea why. Looking back on it, deleting the ScreenGui locally wouldn’t make any difference visually rather than deleting it on the server.

I see. The least confusing fix to implement here would just be to change the design and make the GUI handle deleting itself instead of searching for it, to be honest.

Instead of having to find the GUI just have the Local Script in the GUI listen for the close button and do something like:

if close signal received then
localscript.Parent:Destroy()

Or something similar.

I’m no GUI expert, but aren’t all screen GUIs for players supposed to be in the player themselves, not server sided?
What would happen if you did all this with the this GUI loaded into a starter player GUI script?

I do believe this GUI could be a starter GUI that is just invisible or out of sight, however I didn’t want to change OP’s existing design drastically.

I guess I could put the local script in StarterPlayerScripts, but I’m not sure how that would work different

That would be unnecessary. The Local Script should sit in the GUI.

1 Like

Something has to change in the scripts because it works different when you activate it. I was thinking that the .Activated connection wasn’t disconnecting for some reason, but I don’t imagine it’s a bug with the engine so I have no idea.

I was thinking that every duplicate local script was firing every single time that the RemoteEvent fires, but the ScreenGui it’s parented to is destroyed so I don’t get how that could be. Something contradictory that I tested was putting a print statement after the script’s parent was destroyed, and it ran! Is the local script not closing/being destroyed?

If you are willing to change your approach, you can do the following to make the design less problematic.

Don’t clone the GUI into the Player every time and delete it. Instead, we would be changing the Enabled proprety of the ScreenGUI to make it invisible and inaccessible. Validate your ProximityPrompt interaction on the server and signal the client to flip the Enabled boolean instead in the Local Script (make it the opposite of what it is regardless of what it is).

This means that even if you spam the proximity prompt, all the GUI will do is switch between on and off instead of undergoing mitosis.

Then, add another behavior to the Local Script that will listen to the close button and disable the GUI when it’s clicked.

Perhaps I’ll try this, but I made this topic so I could figure out what part of my code was making this so inconsistent. Thank you for your effort!

LocalScript
--LocalScript in StarterPlayerScripts
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")

local player = Players.LocalPlayer
local StartGameFolder = ReplicatedStorage:WaitForChild("StartGameFolder")
local StartGameGui = StartGameFolder:WaitForChild("StartGameGui")
local StartGameEvent = StartGameFolder:WaitForChild("StartGameEvent")

local activeGui

local function openGui()
	if activeGui then
		activeGui:Destroy()
	end

	activeGui = StartGameGui:Clone()
	activeGui.Parent = player.PlayerGui

	local exitButton = activeGui:WaitForChild("ExitScreen", true)

	exitButton.Activated:Once(function()
		activeGui:Destroy()
		activeGui = nil
	end)
end

StartGameEvent.OnClientEvent:Connect(openGui)
ServerScript
--ServerScript in ServerScriptService
local ProximityPromptService = game:GetService("ProximityPromptService")
local CollectionService = game:GetService("CollectionService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local StartGameEvent = ReplicatedStorage.StartGameFolder.StartGameEvent

for _, prompt in CollectionService:GetTagged("startgame") do
	prompt.ObjectText = "Start game"
end

ProximityPromptService.PromptTriggered:Connect(function(prompt, player)
	if CollectionService:HasTag(prompt, "startgame") then
		StartGameEvent:FireClient(player)
	end
end)

May need some more :WaitForChild()'s, was trying to be quick about it.
And I can’t test anything here😒. Tried to stick with your style.