Unstable behaviour when setting text on gui

  1. What do you want to achieve? I want to have a minigame where if a player touches an activator part (2), a GUI will open where they are prompted to click a button when the timer is the closest to 0 without it going over.

  2. What is the issue? The GUI timer is unstable and switches between 2 different random timers IF any previous activation ended up running out of time.

  3. What solutions have you tried so far? I have tried to implement resetting of EVERY variable related to timing and score in the script.

Example of unstable behaviour (didn’t click before timer ran out on last minigame):

Script for gui:

math.randomseed(os.time())

local player = game.Players.LocalPlayer
local pl_gui = player.PlayerGui.MinigameGUI
local button = pl_gui.ImageButton
local label = pl_gui.TimeLabel
local storage = game.ReplicatedStorage
local db = false

button.MouseButton1Click:Connect(function()
	if db == false then
		db = true
		print("You clicked with ".. tostring(ticktimer) .." seconds left!")
		savedtimer = timer
		savedticktimer = ticktimer
		score = math.round((savedtimer / 10 - savedticktimer) * 10)
		print(score)
	end
	print("firing...")
	storage.GivePoints:FireServer(score)
	
	pl_gui.Enabled = false
	timer = 0
	ticktimer = 0
	savedtimer = 0
	savedticktimer = 0
	score = 0
	script.Enabled = false
end)

while true do
	timer = 0
	ticktimer = 0
	savedtimer = 0
	savedticktimer = 0
	score = 0
	
	timer = Random.new():NextInteger(30, 50)
	ticktimer = timer / 10
	
	wait(1)
	for i = 1, timer do
		if i <= timer then
--			print(tostring(i))
			wait(0.1)
			ticktimer = math.round((ticktimer - 0.1) * 10) / 10
--			print(tostring(ticktimer))
			label.Text = "Time: " ..tostring(ticktimer)
		end
	end	
	
	pl_gui.Enabled = false
	script.Enabled = false
end

Script for activator (2)

local activator = script.Parent
local db = false
local db_time = 8
local storage = game.ReplicatedStorage

activator.Touched:Connect(function(hit)
	print("fired!")
	local player = game.Players:GetPlayerFromCharacter(hit.Parent)
	print(tostring(db))
	
	if player and db == false then
		db = true
		wait(1)
		storage.OpenScript:FireClient(player)
		script.Parent.Parent:Destroy()
		wait(db_time)
		db = false
	end
end)

(btw I know my code is really inefficient lol)

Thanks for reading this post!

1 Like

(btw if anyone wants to know the ‘real’ timer that is taken for non-text purposes according to the script is the 2 second one)

1 Like

Could be that its activating multiple times cause of multiple body parts touching the activator part

Wait nvm I do have a db and it still doesn’t work

I slowed down the video, and from what I can tell the text seems to jitter only when the fractional part is 0

To fix the issue, you’ll need to use the string.format function with %.1f as the format specifier, like so:

math.randomseed(os.time())

local player = game.Players.LocalPlayer
local pl_gui = player.PlayerGui.MinigameGUI
local button = pl_gui.ImageButton
local label = pl_gui.TimeLabel
local storage = game.ReplicatedStorage
local db = false

button.MouseButton1Click:Connect(function()
	if db == false then
		db = true
		print("You clicked with ".. tostring(ticktimer) .." seconds left!")
		savedtimer = timer
		savedticktimer = ticktimer
		score = math.round((savedtimer / 10 - savedticktimer) * 10)
		print(score)
	end
	print("firing...")
	storage.GivePoints:FireServer(score)
	
	pl_gui.Enabled = false
	timer = 0
	ticktimer = 0
	savedtimer = 0
	savedticktimer = 0
	score = 0
	script.Enabled = false
end)

while true do
	timer = 0
	ticktimer = 0
	savedtimer = 0
	savedticktimer = 0
	score = 0
	
	timer = Random.new():NextInteger(30, 50)
	ticktimer = timer / 10
	
	wait(1)
	for i = 1, timer do
		if i <= timer then
--			print(tostring(i))
			wait(0.1)
			ticktimer = math.round((ticktimer - 0.1) * 10) / 10
--			print(tostring(ticktimer))
			label.Text = string.format("Time: %.1f", ticktimer)
		end
	end	
	
	pl_gui.Enabled = false
	script.Enabled = false
end
1 Like

Unfortunately the text still jitters when I use this script. I think it might have something to do with timer being set wrong?

1 Like

There’s a way to make the timer work using only one server Script that’s a child of the MinigameGUI, like so:

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")

local activator -- Path to the activator part, like for example workspace.Model.Activator

local gui = script.Parent -- The MinigameGUI
local imageButton = gui.ImageButton
local timeLabel = gui.TimeLabel

local player = script:FindFirstAncestorOfClass("Player") -- Trick that I use to get the Player from a server Script inside of a GUI

local debounce = false

activator.Touched:Connect(function(hit)
	if debounce then return end

	local hitPlayer = Players:GetPlayerFromCharacter(hit.Parent)

	if hitPlayer and hitPlayer.UserId == player.UserId then
		debounce = true

		local time = Random.new():NextInteger(30, 50) / 10

		local loop
		loop = RunService.Heartbeat:Connect(function(dt)
			time = math.max(time - dt, 0)

			timeLabel.Text = string.format("Text: %.1f", time)

			if time == 0 then
				loop:Disconnect()
				loop = nil
			end
		end)

		imageButton.MouseButton1Click:Once(function()
			if loop then
				loop:Disconnect()
			end

			print(`You clicked with {time} seconds left!`)

			if time > 0 then -- Award points to player
				local score = math.round(time * 10) / 10

				print(score)
			end

			debounce = false
		end)
	end
end)

The benefits being that there’s no longer a need to use RemoteEvents, and the score and points awarding are now being handled by the server :slight_smile::+1:

2 Likes

I fixed the script, I think it was breaking bc I wasn’t resetting the variables properly

1 Like

Wait how are the guis and player accessed?
(also time is a built-in variable)

The MinigameGUI will be the server Script’s parent, so using script.Parent is enough to access it. Since the activator part is no longer the script’s parent, we’ll need to use a new path to access the activator, like the example path shown in the comment next to the activator variable

Since GUIs are automatically cloned and placed inside of the Player’s PlayerGui, the Player will always be the ancestor of the GUI with the sole exception of the GUI that’s inside of StarterGui. From testing in a new empty baseplate, scripts that are descendants of a GUI only run after the GUI is inside of a Player’s PlayerGui, which shouldn’t cause any problems with FindFirstAncestorWhichIsA("Player") returning nil

That’s correct, but in Luau it’s safe to name a variable the same as a built-in function (Although do note that it’s unsafe to name a variable the same as a keyword, like as an example while), which can be a convenient way to quickly name a variable, although comes at a cost that it will no longer refer to the function below the line where it’s declared and within the scope it’s declared + any sub-scopes. If you’re certain that you won’t need to use the time function within your script though, it shouldn’t cause any problems

1 Like

Is this doable with multiple activator parts?

Something like this should work for multiple activator parts:

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")

local activators = {} -- Now an array that contains all of the activator parts

local gui = script.Parent -- The MinigameGUI
local imageButton = gui.ImageButton
local timeLabel = gui.TimeLabel

local player = script:FindFirstAncestorOfClass("Player") -- Trick that I use to get the Player from a server Script inside of a GUI

local debounce = false

local function onActivatorTouched(hit)
	if debounce then return end

	local hitPlayer = Players:GetPlayerFromCharacter(hit.Parent)

	if hitPlayer and hitPlayer.UserId == player.UserId then
		debounce = true

		local time = Random.new():NextInteger(30, 50) / 10

		local loop
		loop = RunService.Heartbeat:Connect(function(dt)
			time = math.max(time - dt, 0)

			timeLabel.Text = string.format("Text: %.1f", time)

			if time == 0 then
				loop:Disconnect()
				loop = nil
			end
		end)

		imageButton.MouseButton1Click:Once(function()
			if loop then
				loop:Disconnect()
			end

			print(`You clicked with {time} seconds left!`)

			if time > 0 then -- Award points to player
				local score = math.round(time * 10) / 10

				print(score)
			end

			debounce = false
		end)
	end
end

for _, activator in activators do
	activator.Touched:Connect(onActivatorTouched)
end

Any activator part added to the activators array will have the onActivatorTouched function connected to their Touched event :slight_smile::+1:

1 Like

(btw sry that this is like the fourth question)
Do serverscripts work in local areas (like guis)?

1 Like

Server Scripts do work when placed in GUIs, you can test this by making a new baseplate and adding a ScreenGui inside of StarterGui, and add a server Script to that ScreenGui. Pressing Play or Play Here with the output window visible will show Hello world!, which confirms that the script is running