Hi guys! I'm trying to make a music game similar to Friday Night Funkin'. Everything was right until I tried to chart the song. I personally play Roblox with 240 FPS. I tested the song at that frame rate and everything was right. The problem comes when I go to 60 FPS. The chart is VERY delayed to the song. It seems that those wait(0.15)
and similar wait less time in higher framerates. Is there any solution to replace those awfully inconsistent wait()
to other thing, more consistent to make the charting of the song? Thank you!
The code of the charting.
game.ReplicatedStorage.SongBegin.OnServerEvent:Connect(function() local ts = game:GetService("TweenService") local SongInfo = { NotesSkins = { NormalArrow = "rbxassetid://7382833833", ExtendArrow = "rbxassetid://7385411096" }, songbpm = 0.6 } local NotesColors = { Note1 = Color3.fromRGB(175,0,255), Note2 = Color3.fromRGB(0,255,255), Note3 = Color3.fromRGB(0,255,0), Note4 = Color3.fromRGB(255,50,50) } local ti = TweenInfo.new( SongInfo.songbpm, Enum.EasingStyle.Linear, Enum.EasingDirection.InOut, 0, false, 0 ) local go = {} go.Position = UDim2.new(0,0,1,0) local function chartnormalnote1() local nt1 = Instance.new("ImageLabel") nt1.Name = "Note" nt1.ZIndex = 2 nt1.BackgroundTransparency = 1 nt1.Image = SongInfo.NotesSkins.NormalArrow nt1.Position = UDim2.new(0,0,-0.137,0) nt1.Size = UDim2.new(1,0,0.137,0) nt1.ImageColor3 = NotesColors.Note1 nt1.Parent = plr.PlayerGui:WaitForChild("Chart").Template.Chart.Arrow1 nt1.Rotation = -90 ts:Create(nt1,ti,go):Play() end local function chartlongnote1() local nt1 = Instance.new("ImageLabel") nt1.Name = "NoteExtension" nt1.ZIndex = 1 nt1.BackgroundTransparency = 1 nt1.Image = SongInfo.NotesSkins.ExtendArrow nt1.Position = UDim2.new(0,0,-0.137,0) nt1.Size = UDim2.new(1,0,0.137,0) nt1.ImageColor3 = NotesColors.Note1 nt1.Parent = plr.PlayerGui:WaitForChild("Chart").Template.Chart.Arrow1 nt1.Rotation = 0 ts:Create(nt1,ti,go):Play() end local function chartnormalnote2() local nt1 = Instance.new("ImageLabel") nt1.Name = "Note" nt1.ZIndex = 2 nt1.BackgroundTransparency = 1 nt1.Image = SongInfo.NotesSkins.NormalArrow nt1.Position = UDim2.new(0,0,-0.137,0) nt1.Size = UDim2.new(1,0,0.137,0) nt1.ImageColor3 = NotesColors.Note2 nt1.Parent = plr.PlayerGui:WaitForChild("Chart").Template.Chart.Arrow2 nt1.Rotation = 180 ts:Create(nt1,ti,go):Play() end local function chartlongnote2() local nt1 = Instance.new("ImageLabel") nt1.Name = "NoteExtension" nt1.ZIndex = 1 nt1.BackgroundTransparency = 1 nt1.Image = SongInfo.NotesSkins.ExtendArrow nt1.Position = UDim2.new(0,0,-0.137,0) nt1.Size = UDim2.new(1,0,0.137,0) nt1.ImageColor3 = NotesColors.Note2 nt1.Parent = plr.PlayerGui:WaitForChild("Chart").Template.Chart.Arrow2 nt1.Rotation = 0 ts:Create(nt1,ti,go):Play() end local function chartnormalnote3() local nt1 = Instance.new("ImageLabel") nt1.Name = "Note" nt1.ZIndex = 2 nt1.BackgroundTransparency = 1 nt1.Image = SongInfo.NotesSkins.NormalArrow nt1.Position = UDim2.new(0,0,-0.137,0) nt1.Size = UDim2.new(1,0,0.137,0) nt1.ImageColor3 = NotesColors.Note3 nt1.Parent = plr.PlayerGui:WaitForChild("Chart").Template.Chart.Arrow3 nt1.Rotation = 0 ts:Create(nt1,ti,go):Play() end local function chartlongnote3() local nt1 = Instance.new("ImageLabel") nt1.Name = "NoteExtension" nt1.ZIndex = 1 nt1.BackgroundTransparency = 1 nt1.Image = SongInfo.NotesSkins.ExtendArrow nt1.Position = UDim2.new(0,0,-0.137,0) nt1.Size = UDim2.new(1,0,0.137,0) nt1.ImageColor3 = NotesColors.Note3 nt1.Parent = plr.PlayerGui:WaitForChild("Chart").Template.Chart.Arrow3 nt1.Rotation = 0 ts:Create(nt1,ti,go):Play() end local function chartnormalnote4() local nt1 = Instance.new("ImageLabel") nt1.Name = "Note" nt1.ZIndex = 2 nt1.BackgroundTransparency = 1 nt1.Image = SongInfo.NotesSkins.NormalArrow nt1.Position = UDim2.new(0,0,-0.137,0) nt1.Size = UDim2.new(1,0,0.137,0) nt1.ImageColor3 = NotesColors.Note4 nt1.Parent = plr.PlayerGui:WaitForChild("Chart").Template.Chart.Arrow4 nt1.Rotation = 90 ts:Create(nt1,ti,go):Play() end local function chartlongnote4() local nt1 = Instance.new("ImageLabel") nt1.Name = "NoteExtension" nt1.ZIndex = 1 nt1.BackgroundTransparency = 1 nt1.Image = SongInfo.NotesSkins.ExtendArrow nt1.Position = UDim2.new(0,0,-0.137,0) nt1.Size = UDim2.new(1,0,0.137,0) nt1.ImageColor3 = NotesColors.Note4 nt1.Parent = plr.PlayerGui:WaitForChild("Chart").Template.Chart.Arrow4 nt1.Rotation = 0 ts:Create(nt1,ti,go):Play() end plr.PlayerGui:WaitForChild("Chart").Start.Visible = false game.Workspace.Music:Play() wait(3.9) chartnormalnote1() wait(0.02) chartlongnote1() wait(0.055) chartlongnote1() wait(0.15) chartnormalnote2() wait(0.02) chartlongnote2() wait(0.055) chartlongnote2() wait(0.15) chartnormalnote4() wait(0.15) chartnormalnote4() wait(0.1) chartnormalnote1() wait(0.15) chartnormalnote2() wait(0.15) chartnormalnote3() wait(0.15) chartnormalnote2() wait(0.15) chartnormalnote1() wait(0.2) chartnormalnote4() wait(0.2) chartnormalnote3() wait(0.02) chartlongnote3() wait(0.055) chartlongnote3() wait(0.2) chartnormalnote2() end)
wait(t)
will always try to yield the current thread by t
seconds, however, the accuracy is dependent on your fps: at a much higher fps, the actual time the thread has been yielded for will be much closer to the desired waiting time than at a lower fps. wait
has a minimum wait time of 0.03 (~ 1/30th of a second) so wait
really is only capped to a 30Hz pipeline, regardless of whether or not you're capped at 60 or using an fps unlocker to get a higher fps, however the frequency will drop if you do go below 30 fps, as in this case, the frequency of wait's pipeline will be correspondent to your current fps when it is less than 30. So, any waiting times less than 0.03 will have the thread wait for 0.03 seconds: an unintended wait time off by 10 milliseconds every time you wait 0.02 seconds in your code. The more of these you have, the more delayed the song will be.
Playing at 240 fps will have the accuracy of the actual time yielded compared to the desired time be ~4x as accurate as playing at 60 fps. wait spam can also further delay your game as often times, thread resumptions are deterred. However, a better solution would be to use task.wait(t)
since it internalises the Heartbeat event which fires at the exact same frequency as your fps, since Heartbeat gets fired every frame. With this, you're no longer constrained to the 30Hz pipeline, which makes it so playing at 60 fps will have the accuracy be double the amount as if you're using wait