this is the stopwatch counting system I have right now and I’m wondering how I can make a function that effectively “Stops” and “Starts” the stopwatch.
local count = tick()
local milliseconds = 0
local seconds = 0
local minutes = 0
while true do
task.wait()
local diff = tick() - count
local min = math.floor(diff / 60)
local sec = diff - min*60
local mil = math.floor((sec - math.floor(sec)) * 1000)
sec = math.floor(sec)
local formatted = tostring(min)..":"..tostring(sec)..":"..tostring(mil)
end
Divorce string formatting from the actual timer. You should have two separate scripts here - one that provides the stopwatch functionality and the other can be used to format the time. My preference to stopwatch systems is classes for statefulness.
In terms of counting the time, I recommend using a frame event from RunService and using the delta between frames to increment a variable where you can check the amount of time that has elapsed since starting the timer. Your time formatter can then pull that elapsed time (a member of the timer class) and then format it accordingly.
Here’s a fully written class that I currently use with my other project to create a stopwatch. I do change it occasionally when I find that I’m unhappy with its formatting and whatnot but for all intents and purposes it works.
--[[
Generic stateful timer class.
@author colbert2677
]]
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local import = require(ReplicatedStorage.Packages.import)
local class = import("Packages/class")
local Signal = import("Packages/Signal")
local GenericTimer = class("GenericTimer")
function GenericTimer:__init()
self._elapsed = 0
self._stopped = true
self.Started = Signal.new()
self.Stopped = Signal.new()
self.Tick = Signal.new()
self:_ConnectTimeEvent()
end
function GenericTimer:_ConnectTimeEvent()
if self._running then return end
self._running = RunService.PreSimulation:Connect(function(deltaTimeSim)
if self._stopped then return end
self._elapsed += deltaTimeSim
self.Tick:Fire(deltaTimeSim, self._elapsed)
end)
end
function GenericTimer:Start()
if not self._stopped then return end
self._stopped = false
self.Started:Fire()
end
function GenericTimer:Stop()
if self._stopped then return end
self._stopped = true
self.Stopped:Fire()
end
function GenericTimer:SetTime(newTime: number)
self._elapsed = newTime
end
function GenericTimer:AddTime(increment: number)
self._elapsed += increment
end
function GenericTimer:SubtractTime(decrement: number)
self._elapsed = math.min(self._elapsed - decrement, 0)
end
function GenericTimer:Reset()
self._elapsed = 0
end
function GenericTimer:IsRunning()
return not self._stopped
end
function GenericTimer:Destroy()
self._running:Disconnect()
self._running = nil
self.Started:Destroy()
self.Stopped:Destroy()
self.Tick:Destroy()
self:Stop()
end
return GenericTimer
This has to be used with a ModuleScript. As for the import statements, those are because I work with Wally. You can remove the import and substitute Signal with a custom Luau signal class while class can be removed and you can change the constructor to suit your needs (should follow the standard OOP-in-Lua format for constructors, merge the contents of __init to it – I won’t explain how to do this because it’s against my principle to spoonfeed and instead guide to solutions).
I use vocksel/class. It’s great for getting rid of all the boilerplate you’d normally write for class constructors with the whole table creation and setmetatable, creates a standard for class creation (all classes are created the same way but populated differently depending on what you write in __init) and enables simple inheritance because you’re now defining a class’ members through __init instead of having varying constructors and methods of populating objects.