EzTimer - Make timers and stopwatches with ease!

Hi! I’m here to announce the creation of EzTimer! A fool-proof module that enables you to create timers and stopwatches with ease! But that’s not all. I’ve also made a handy-dandy plugin to go with it, that makes timer creation a breeze!

Module Information

How to use

Installation

To use this brilliant module, get it here:

pluginbutton

Then insert it into studio and put it in ReplicatedStorage.

Then add a LocalScript and put it in StarterGui (The module can be used on the server) and type:

local EzTimer = require(game:GetService('ReplicatedStorage'):WaitForChild('EzTimer'))


Timers

How to make timers

Creating a timer

To create a timer, simply type

local timer = EzTimer:Create('Timer') -- You can name the timer whatever you want

Setting Parameters

To set the timer’s parameters, you must first create a dictionary containing the parameters you want like this:

local timerParameters = {
	Start = 10,
	IntervalDuration = 1,
	Separator = ':',
	Separators = 0,
	Countdown = true,
	Target = 0
}
Explanation of parameters

Parameters Eplained

Here’s an explanation of all the Timer Parameters

Start (number)

This is what the timer will start at.

IntervalDuration

This is how long the timer will between seconds. If IntervalDuration was equal to 2 and the Start was equal to 0, the timer would start at 0, wait 2 seconds, then move to 1.

Separator (string)

When Separators is more than 0, this is what will separate the seconds, minutes and hours in the timer’s UI, for example, if Separators is set to 2 and the Separator is set to : then the timer will looks like this: 00:00:00 (the first set of zeros showing the hours, the second set showing the minutes, and the third showing the seconds. If Separators is set to 1 and Separator is set to " , the timer will look like this: 00"00 (the first set of zeros showing the minutes and the second set showing the seconds.

Separators (number)

When Separators is equal to 1, the timer will split the time into minutes and seconds, when equal to 2, the timer will split the time into hours, minutes and seconds, and so on. When Separators is equal to 0, the timer will not split the time at all and it will show the remaining time in seconds.

Countdown (boolean)

When Countdown is equal to true, the timer will count down from Start, when false, the timer will count up from Start

Target (number)

When the timer reaches this number, it will stop.

Then set the parameters by using the SetParams() function like so:

timer:SetParams(timerParameters)

If something is wrong with the parameters, the timer’s parameters will not be set and the module will tell you what is wrong with them.

Setting the UI

To set the UI Element that will show where the timer is at, you’ll need to use the SetUI() function like this:

Timer:SetUI(UIElement) --Make sure the UI Element has a text property

If you want multiple UI Elements to show where the timer is at, just change UIElement to an array containing all the UI Elements you want like this:

local Elements = {Element1,Element2,Element3}

Timer:SetUI(Elements)

If you want a UIElement to show where the timer is when the timer has already started, use the ShowTimeOn() function like this:

Timer:ShowTimeOn(Element)

The timer will show the time on the UIElement at the next tick.

Setting the tick sound

(This step is optional)
If you want the timer to make a sound whenever the time left changes, use the SetSound() function like this:

timer:SetSound(SoundId)

Starting the timer

After you have set the parameters, to start the timer, use the Start() function.

timer:Start()

(If parameters have not been set, the timer will use these parameters instead:)

	Start = 60
	IntervalDuration = 1
	Separator = ':'
	Separators = 1
	Countdown = true
	Target = 0

Pausing the timer

If you’re following allong with the tutorial, your code should look like this:

local EzTimer = require(game:GetService('ReplicatedStorage'):WaitForChild('EzTimer'))

local timer = EzTimer:Create('Timer')

local timerParameters = {
	Start = 10,
	IntervalDuration = 1,
	Separator = ':',
	Separators = 0,
	Countdown = true,
	Target = 0
}

timer:SetParams(timerParameters)

timer:Start()

At this point, the timer should already be running. If you want to pause the timer before it’s finished, use the Pause() functions like this:

timer:Pause()

If the timer isn’t running, nothing will happen.
To resume the timer, use the Resume() function like this:

timer:Resume()

If the timer isn’t paused, nothing will happen.

Getting the status of the timer

If you want to get the status of the timer, use the GetStatus() function like this:

Timer:GetStatus()
Statuses Explained

Idle - The timer is not in use and hasn’t been started
Running - The timer has been started and is currently in use
Paused - The timer is not currently in use and has been paused

Events

Here are all the events of the timer

Finished

This event is fired when the timer has reached it’s target. To detect when the timer has finished in a script write this:

timer.Finished:Connect()

Paused

This event is fired when the timer has been paused. To detect when the timer has finished in a script write this:

timer.Paused:Connect()

Started

This event is fired when the timer has started. To detect when this happens, write this:

timer.Began:Connect()
Stopwatches

With this module, you can also make stopwatches

How to make a stopwatch

To create a stopwatch, use the ```CreateStopwatch() function like this:

local stopwatch = EzTimer:CreateStopwatch(Name)

Starting the stopwatch

To start the stopwatch, type:

stopwatch:Start(UIElement,Separator,StartTime)
Arguements Explained

UIElement - The UI Element that will show the time on the stopwatch. This must have a text property
Separator - This is what will separate the hours, minutes, seconds and milliseconds on the stopwatch. This must be a string
StartTime - This parameter is not neccesary for stopwatches. When provided, the watch will show how much time has past since this time. If not provided, the watch will show how much time has passed since the time it was started. When providing StartTime, use tick()

Stopping the stopwatch

To stop the stopwatch, use the Stop() function like this:

stopwatch:Stop()

If you want the time between when the stopwatch started and when it stopped, make the Stop() function a variable like this:

local endTime = stopwatch:Stop()
Plugin Information

A full tutorial and explanation of the EzTimer Creator can be found here.

Module Source Code
local funcs = {}
local RunService = game:GetService('RunService')
funcs.__index = funcs
local timers = {}
local watches = {}

local deafultParams = {
	Start = 60,
	IntervalDuration = 1,
	Separator = ':',
	Separators = 1,
	Countdown = false,
	Target = 0
}

function funcs:Create(Name)
	local finishedEvent = Instance.new('BindableEvent')
	local pausedEvent = Instance.new('BindableEvent')
	if Name then

		timers[Name] = {
			Parameters = {	Start = 0,
				IntervalDuration = 0,
				Separator = ':',
				Separators = 0,
				Countdown = false,
				Target = 0},
			Status = 'Idle',
			UI = nil,
			Current = 0,
			Sound = '',
			Start = os.time(),
			Paused = pausedEvent.Event,
			Finished = finishedEvent.Event,
			StartTimer = Instance.new('BindableEvent')
		}

		local new = {}

		--Timer Parameters
		new['Parameters'] = {
			Start = 60,
			IntervalDuration = 1,
			Separator = ':',
			Separators = 0,
			Countdown = true,
			Target = 0}
		new['Status'] = 'Idle'
		new['UI'] = nil
		new['Current'] = 0
		new['IntervalDuration'] = 0
		new['Sound'] = ''
		new['StartTime'] = math.floor(os.time())
		new['Paused'] = pausedEvent.Event
		new['Finished'] = finishedEvent.Event

		-- Timer functions

		function new:Start(Timer)
			local timer = new
			if timer then
				if not timer.UI then
					warn('Timer "'.. Timer .. '" has no UI')
				end
				local params = timer.Parameters
				timer.Status = 'Running'
				timer.Current = timer.Parameters.Start
				coroutine.wrap(function()
					repeat
						if params.Separators == 0 then
							if timer.Parameters.Countdown == true then
								timer.Current = timer.Current - 1
							else
								timer.Current = timer.Current + 1
							end

							if timer.UI then
								timer.UI.Text = timer.Current
							end

							wait(timer.Parameters.IntervalDuration)
						end

						if params.Separators == 1 then
							if params.Countdown == true then
								timer.Current = timer.Current - 1
								local show = ''
								local secs = timer.Current % 60
								local mins = math.floor(timer.Current/60)

								if string.len(secs) == 1 then
									secs = '0'.. secs
								end

								if string.len(mins) == 1 then
									mins = '0'.. mins
								end

								show = mins .. params.Separator .. secs
								timer.UI.Text = show
								wait(timer.Parameters.IntervalDuration)
							else
								timer.Current = timer.Current + 1
								local show = ''
								local secs = timer.Current % 60
								local mins = math.floor(timer.Current/60)

								if string.len(secs) == 1 then
									secs = '0'.. secs
								end

								if string.len(mins) == 1 then
									mins = '0'.. mins
								end

								show = mins .. params.Separator .. secs
								if new.Sound ~= '' then
									local sound = Instance.new('Sound')
									sound.Parent = game:GetService('SoundService')
									sound.Name = 'Tick'
									sound.SoundId = new.Sound
									sound:Play()
									sound.Ended:Connect(function()
										sound:Destroy()
									end)
								end
								timer.UI.Text = show
								wait(timer.Parameters.IntervalDuration)
							end
						end

						if params.Separators == 2 then
							if params.Countdown == true then
								timer.Current = timer.Current - 1

								local show = ''
								local secs = timer.Current % 60
								local mins = math.floor(timer.Current / 60)
								local hrs = math.floor(timer.Current/3600)

								if mins > 59 then
									repeat mins = mins - 60

									until mins < 60

								end

								if string.len(secs) == 1 then
									secs = '0'.. secs
								end

								if string.len(mins) == 1 then
									mins = '0'.. mins

								end

								if string.len(hrs) == 1 then
									hrs = '0'.. hrs
								end

								show = hrs.. params.Separator .. mins.. params.Separator .. secs
								timer.UI.Text = show
								wait(timer.Parameters.IntervalDuration)
							end
						end
					until 
					timer.Current == timer.Parameters.Target or timer.Status == 'Paused'
					if timer.Current == timer.Parameters.Target then
						finishedEvent:Fire()
					else
						pausedEvent:Fire()
					end
				end)()
			else
				warn('Timer '.. Timer .. ' not found')
			end
		end
		
		timers[Name].StartTimer.Event:Connect(function()
			new:Start()
		end)

		function new:SetParams(TimerParams)

			if TimerParams then
				local valid = true
				local start = TimerParams.Start
				local interval = TimerParams.IntervalDuration
				local separator = TimerParams.Separator
				local separators = TimerParams.Separators
				local countdown = TimerParams.Countdown
				local target = TimerParams.Target

				if start then
					if type(start) ~= 'number' then
						warn('Start must be a number')
						valid = false
					end
				else
					TimerParams.Start = deafultParams.Start
					warn('Start not specified')
				end

				if interval then
					if type(interval) ~= 'number' then
						warn('IntervalDuration must be a number')
						valid = false
					end
				else
					TimerParams.IntervalDuration = deafultParams.IntervalDuration
					warn('IntervalDuration not specified')
				end

				if separator then
					if type(separator) ~= 'string' then
						warn('Separator must be a string')
						valid = false
					end
				else
					TimerParams.Target = deafultParams.Target
					warn('Separator not specified')
					TimerParams.Separator = deafultParams.Separator
				end

				if separators then
					if type(separators) ~= 'number' then
						warn('Separators must be a number')
						valid = false
					else
						if separators < 0 or separators > 2 then
							warn('There can only between 0 to 2 separators')
							valid = false
						end
					end
				else
					TimerParams.Separators = deafultParams.Separators
					warn('Separators not specified')
				end

				if countdown then
					if type(countdown) ~= 'boolean' then
						warn('Countdown must be a boolean')
						valid = false
					end
				else
					TimerParams.Countdown = deafultParams.Countdown
					warn('Countdown not specified')
				end

				if target then
					if type(target) ~= 'number' then
						warn('Target must be a number')
						valid = false
					end
				else
					TimerParams.Target = deafultParams.Target
					warn('Target not specified')
				end


				if valid == true then
					timers[Name].Parameters = TimerParams
					new.Parameters = TimerParams
				end

			else
				warn('TimerParams not specified')

			end
		end

		function new:Pause()
			if new.Status == 'Running' then
				new.Status = 'Paused'
			else
				warn('Timer "'.. Name .. '" is not running; did not pause')
			end
		end

		function new:Resume()
			if new.Status == 'Paused' then
				new.Status = 'Running'
				new:SetParams({
					Start = new.Current,
					IntervalDuration = new.Parameters.IntervalDuration,
					Separator = new.Parameters.Separator,
					Separators = new.Parameters.Separators,
					Countdown = new.Parameters.Countdown,
					Target = new.Parameters.Target})
				new:Start()
			else
				warn('Timer "'.. Name .. '" is not paused; did not resume')
			end
		end

		function new:SetUI(Element)
			local success,errorMessage = pcall(function()
				Element.Text = ''
			end)

			if success then
				new.UI = Element
			else
				warn('UI Element "'.. Element .. '" does not have a Text property')
			end
		end

		function new:SetSound(SoundID)
			local MarketplaceService = game:GetService'MarketplaceService'
			local Success, Response = pcall(MarketplaceService.GetProductInfo, MarketplaceService, SoundID)

			if Success then
				if Response.AssetTypeId == Enum.AssetType.Audio.Value then
					new.Sound = 'rbxassetid://' .. SoundID
				else
					warn(SoundID .. ' is not a sound ID')
				end
			else
				warn(SoundID .. ' not found')
			end
		end

		function new:GetStatus()
			return new.Status
		end

		return new

	else
		warn('No name specified for timer')
	end
end

function funcs:CreateStopWatch(Name)
if Name then
	local watch = {}
	local stopEvent = Instance.new('BindableEvent')
	watch['Stopped'] = stopEvent.Event
	watch['Status'] = 'Idle'
	watch['Begin'] = 0

	function watch:Start(UI,Separator,StartTime)
		if not UI then
			warn('UI not specified')
		end
		local start = tick()
		watch.Begin = start
		local stop = false
		local sep = Separator

		if not Separator then
			sep = ':'
		end
		
		if StartTime then
			start = StartTime
		end
		
		coroutine.wrap(function()
		repeat
			local diff = tick() - start
			local min = math.floor(diff / 60)
			local hr = math.floor(diff / 3600)
			local sec = diff - min*60
			local mil = math.floor((sec - math.floor(sec)) * 100)
			sec = math.floor(sec)

			if string.len(min) == 1 then
				if min ~= 0 then
					min = '0'.. min
				end
			end

			if string.len(sec) == 1 then
				sec = '0'.. sec
			end

			if mil == 0 then
				mil = '00'
			end

			if min == 0 then
				UI.Text = sec .. sep .. mil
			else
				if hr == 0 then
					UI.Text = min.. sep.. sec.. sep.. mil
				else
					UI.Text = hr.. sep.. min.. sep.. sec..sep ..mil ..sep
				end
			end
				watches[Name] = watch
			RunService.Heartbeat:Wait()
		until watch.Status == 'Stopped'
			end)()


	end
	
	function watch:Stop()
		watch.Status = 'Stopped'
		stopEvent:Fire()
		return tick() - watch.Begin
		
		end
		watches[Name] = watch
		return watch
	else
		warn('No name provided for stop watch')
		end
end

function funcs:GetTimers()
	local TIMERS = {}
	
	for i,v in pairs(timers) do
		TIMERS[i] = {
			Start = v.Start,
			IntervalDuration = v.IntervalDuration,
			Separator = v.Separator,
			Separators = v.Separators,
			Countdown = v.Countdown,
			Target = v.Target}
	end
	
	return TIMERS
end

function funcs:Start(Name)
	if Name then
		if timers[Name] then
			timers[Name].StartTimer:Fire()
		end
	else
		warn('No name provided')
	end
end

return funcs

Hope you can put this module to good use! :happy3:

18 Likes

Please report any errors in the replies!

Just wondering, what is the point of making the plugin instead of just using the Module?

You could just make a Module that takes input from requiree instead of using UI to take them in.

1 Like

The purpose of the plugin is to make creating a timer faster and simpler by writing the code for you, but limiting the number of editable properties. You could just use the module if you want to, but if you want a timer quickly and with less faff, use the plugin

Inputing arguements with the functions in module instead of UI textboxes would be easier. Just connecting a tick and complete function should be enough!

But I like your idea of creating source!

1 Like

You can use functions in the module to input arguements. The plugin just writes the code to do this for you. The use of the plugin is completely optional and it’s purpose is to save time.

2 Likes

Update!

You can now show where the timer is at on multiple UI Elements! All you have to do is change UIElement to an array containing all the UI Elements you want like this:

local Elements = {Element1,Element2,Element3}

Timer:SetUI(Elements)

You can also make a UI Element to show where the timer is at when the timer has already started! To do this, use the new ShowTimeOn() function like this:

Timer:ShowTimeOn(Element)
1 Like

Umm, could you explain with more detail pls?
Does this go by minutes, seconds or hours?

What does this mean?

Isn’t it obvious that it should end when it comes down to 0?

Umm… What does this mean?

1 Like
  1. When setting the start, it should be stated in seconds. You can split the time remaining into hours, minutes and seconds with the Separators property.

  2. As stated in the tutorial, when Separators is equal to 1, the timer will split the time into minutes and seconds, when equal to 2, the timer will split the time into hours, minutes and seconds, and so on. When Separators is equal to 0, the timer will not split the time at all and it will show the remaining time in seconds. The separator will be what splits the seconds, minutes and hours in the actual ui, for example, if Separators is set to 2 and the Separator is set to : then the timer will looks like this: 00:00:00 (the first set of zeros showing the hours, the second set showing the minutes, and the third showing the seconds. If Separators is set to 1 and Separator is set to " , the timer will look like this: 00"00 (the first set of zeros showing the minutes and the second set showing the seconds.

  3. If the timer is set to count down then yes, this would be obvious, but if it is set to count up, the timer wouldn’t know when to stop, which is why this property is needed.

  4. The UI Element property is the name of the GuiObject that will show the timer. If using the EzTimer Creator to set this property, the name of the GuiObject must be unique or the module may error.

I was a bit vague with my tutorial, I’ll be sure to update it with a more detailed description.

Hope this helps! :happy3:

I’ve updated the plugin with a help button if you encounter any further issues. I have also added some new features. You can view all of them in detail here

Hi,

I am running this code but the timer is counting down why is this?

local EzTimer = require(game:GetService(‘ReplicatedStorage’):WaitForChild(‘Modules’):WaitForChild(‘EzTimer’))
local timer = EzTimer:Create(‘Timer’)
local timerParameters = {
Start = 1,
IntervalDuration = 1,
Separator = ‘:’,
Separators = 2,
Countdown = false,
Target = 60
}
timer:SetParams(timerParameters)
timer:SetUI(script.Parent)
timer:ShowTimeOn(script.Parent)
–timer:SetSound(SoundId)
timer:Start()

Do you have to use a ui element for the stopwatch function or can it just exist in the background, I think this module will be great for what I need a stopwatch for but I want it to run in the background and not constantly updating a ui element that isnt needing to be updated, and the seperator is also not needed as I just need change in time