Hello! I’m currently making a stopwatch for my new game that will time the player on how long they complete a course (Obstacle Course, I forgot to mention this)and will fire a RemoteEvent to the server to save the time into a DataStore.
The problems that I’m having is that the stopwatch is inaccurate. Seen by this image.
I’m currently printing out a variable which is subtracting tick() with another variable with tick() saved
Here’s the code:
-- Services
local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- Variables
local Stopwatch = script.Parent
local EndTime = game.Workspace:WaitForChild("Part")
local StartTime = game.Workspace:WaitForChild("Part2")
local TickToServer = ReplicatedStorage.Remotes:WaitForChild("TickToServer")
local Debounce = false
-- Main
StartTime.Touched:Connect(function(hit)
local HumanoidRootPart = hit.Parent:FindFirstChild("HumanoidRootPart")
local Start = tick()
Stopwatch.Visible = true
if HumanoidRootPart ~= nil then
local connection
local connection2
connection = RunService.RenderStepped:Connect(function()
local Now = tick() - Start
local Minutes = (math.floor(Now) / 60) % 60
local Seconds = math.floor(Now) % 60
local Milliseconds = math.floor((Now % 1) * 1000)
print(Now)
Stopwatch.Text = string.format("%0.2i:%.02i.%.03i", Minutes, Seconds, Milliseconds)
connection2 = EndTime.Touched:Connect(function()
connection:Disconnect()
Stopwatch.TextColor3 = Color3.fromRGB(45, 255, 34)
if not Debounce then
Debounce = true
TickToServer:FireServer(Now)
wait(1)
Debounce = false
end
end)
end)
end
end)
It’s in a Localscript if any of you are wondering.
I actually made something similar to what you are trying to do for A drag racing timing system I made for me and my friends. It used to run on elapsedTime, however that has since been depracated and I opted for os.clock. Basically what I did to get each split time, or each time in between parts being touched was as follows…
local function calculateSplit(beginTime)
return os.clock() - beginTime -- this will give you the amount of time that has passed since they started the race
end
local startTime = os.clock()
-- than somewhere else in your code you just do
local endTime = calculateSplit(startTime)
And than for the timing conversions to get the minutes, seconds, milliseconds I used an algorithm similar to that written by @Wesley1041 which is here :
function convertT(timeN)
-- Credit to Wes for his time version algorithim
local min = math.floor(timeN/60)
local sec = math.floor(timeN)-60*min
local milisec = math.floor((timeN-sec-min*60)*1000+0.5)
if min < 1 then
min = ""
else
min = min..":"
end
if sec < 10 then
sec = "0"..sec
end
if milisec < 100 and milisec >= 10 then
milisec = "0"..milisec
elseif milisec < 10 then
milisec = "00"..milisec
end
local output = min..sec.."."..milisec
return output
end
The inaccuracy is because you’re doing this on a frame basis, the accuracy of the time is limited by how quickly a frame can pass. So 1 fps = 1 extra second of inaccuracy, 700fps = 0.0014 inaccuracy, etc.
The solution to this is to just not time it on a frame basis at all, all you need to do is; whenever what you’re trying to do ends, subtract the currentTime from the startTime. And that’s as high of an accuracy as you can get.
Also worth mentioning, tick() is deprecated and only counts up to a smaller amount of decimals, whereas os.clock is 6 decimals (micro seconds); so os.clock would be a better choice for time comparison here.
I’m doing as you told but obviously I’m doing something wrong here. The problem still persists and I’m now getting this after subtracting currentTime from startTime
Here’s the updated code if you’re curious:
-- Services
local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- Variables
local Stopwatch = script.Parent
local EndTime = game.Workspace:WaitForChild("Part")
local StartTime = game.Workspace:WaitForChild("Part2")
local TickToServer = ReplicatedStorage.Remotes:WaitForChild("TickToServer")
local BrokenLoop = false
local Debounce = false
-- Main
StartTime.Touched:Connect(function(hit)
local HumanoidRootPart = hit.Parent:FindFirstChild("HumanoidRootPart")
local startTime = os.clock()
Stopwatch.Visible = true
if HumanoidRootPart ~= nil then
while BrokenLoop == false do
wait()
local currentTime = os.clock() - startTime
local Minutes = (math.floor(currentTime) / 60) % 60
local Seconds = math.floor(currentTime) % 60
local Milliseconds = math.floor((currentTime % 1) * 1000)
print(currentTime)
Stopwatch.Text = string.format("%0.2i:%.02i.%.03i", Minutes, Seconds, Milliseconds)
EndTime.Touched:Connect(function()
local endTime = currentTime - startTime
if not Debounce then
Debounce = true
BrokenLoop = true
print(endTime)
Stopwatch.TextColor3 = Color3.fromRGB(45, 255, 34)
TickToServer:FireServer(endTime)
wait(1)
Debounce = false
end
end)
end
end
end)
What might help you here is actually instead of looking for if A part got touched by a humanoidrootpart, look for if the humanoidrootpart was touched by a part, so this method of reversing what you did might work better. In my case it did for my racing timers Which work a little bit like this
rootPt.Touched:connect(function(obj)
if obj.Parent.Name == "DragTimer" and char.Humanoid.Sit then
if obj.Name == "start" then
startHits = startHits + 1
if resetTime() == false then
timeTable.t1 = os.clock()
end
end
if string.find(obj.Name, "S") then
timeTable.t2 = os.clock()
if timeTable.t1 ~= "" then
local timeN = getSplit(obj.Name)
if timeN then
mainFolder.timeRelay:FireServer(obj.Name, timeN)
end
end
end
end
end)
I have provided you with this small snippet, but hopefully it helps with your timing system, reply to this if you continue to find issues.
Forgive me if the code is a bit messy, the timing system was one of my first large undertakings when I began to learn and become more proficient with coding.
To add to this I have made a simpler version of what I sent previously and commented it out for ease of understanding, since not everyone learns from just one little slice of code from a different project. I have reformatted it in simpler terms whilst preserving some of the variable names in the original piece of code you provided.
local player = game.Players.LocalPlayer
local char = player.Character or player.CharacterAdded:Wait()
local HumanoidRootPart = char:WaitForChild("HumanoidRootPart")
local timeData = {
startVal = nil,
endVal = nil
}
local function calcSplit()
if timeData.startVal and timeData.endVal then
return timeData.endVal - timeData.startVal
end
end
HumanoidRootPart.Touched:Connect(function(object)
if object ~= nil then
if object == StartTime then -- this will get triggered when the part touching the humanoidrootpart is the StartTime part
timeData.startVal = os.clock()
elseif object == EndTime then -- this will get triggered when the part touching the humanoidrootpart is the EndTime part
timeData.endVal = os.clock()
local splitTime = calcSplit() or 0 -- the split time is the amount of time that has passed since the start part was touched
-- after this you can just do your FE related stuff, sending the value to the server etc
end
end
end)