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.
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.
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)
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
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
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
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