Ideal approach to creating a stopwatch system

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
1 Like

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

3 Likes

What is the class module you use? if your able to give it out?

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.

Alright thank you, this will be very useful

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.