How do i make task.wait(n) not dependant on client fps?

task.wait(n) seems to wait less when the player has an fps unlocker in comparison to a player locked at 60fps, and i don’t want that happening as it’s not very just. anyone know a possible solution/workaround?

task.wait() waits at least 1 frame, regardless of whether or not it has a number fed to it.

So if FPS > 60 then that 1 frame minimum lasts shorter so the script can continue faster.

Why is this a problem? Make sure that n is set to 1 / 60 at minimum

you can base your delays off of os.time or tick if you want them to be based on time rather than framerate

all my task.wait(n) are all above 1/60, so it’s strange

could you give an example of how that would be possible?

What is your use case, that requires this sensitive timing?

the timing difference in my case is very obvious, ranging from 0.1s to 1s differences, which is very noticeable.

Do you chain the task.wait() calls, one after the other? Might want to use another approach in that case.

what do you mean by chaining them? if you mean something like

task.wait(1)
task.wait(1)
task.wait(1)

then definitely not

Could you share the relevant code / context where the problem occurs? Trying to paint a picture to help.

local function SyncedWait(Time)
	local Start = os.time()
	
	repeat game:GetService("RunService").Heartbeat:Wait() until os.time() >= Start + Time
	return Time
end

this should work, obviously you should clean it up so it’s not running getservice every time

1 Like

task.wait(n) will wait until n seconds have passed, however it uses RunService.Heartbeat under the hood. Depending on the use case, you might not want to use task.wait, and most use cases can be replaced by using an event, I would like to now the use case so I can suggest a different way of doing things that do not require task.wait.

This is one of the examples on where it’s happening:

local function slash(slashtype, click)
	if db == false and unequipping == false then
		if spiritType.Value == "mercy" and isAbility.Value == true then
			return
		end
		db = true
		repeat task.wait() until newHitbox		
		walkAnim:Stop()
		idleAnim:Stop()
		if spiritType.Value == "eter" then
			Trail.Color = ColorSequence.new(Color3.fromRGB(0, 113, 255), Color3.fromRGB(0, 113, 255))
		elseif spiritType.Value == "war" then
			Trail.Color = ColorSequence.new(Color3.fromRGB(255, 0, 0), Color3.fromRGB(255, 0, 0))
		elseif spiritType.Value == "tranq" then
			Trail.Color = ColorSequence.new(Color3.fromRGB(219, 135, 211), Color3.fromRGB(219, 135, 211)) 
		elseif spiritType.Value == "mercy" then
			Trail.Color = ColorSequence.new(Color3.fromRGB(200, 194, 101), Color3.fromRGB(200, 194, 101))
		else
			Trail.Color = ColorSequence.new(Color3.fromRGB(255, 255, 255), Color3.fromRGB(255, 255, 255)) 
		end
		Trail.Enabled = true
		servTrailChange:FireServer(true)
		hitPlayers = {}
		print("collideOn")
		local character = player.Character or player.CharacterAdded:Wait()
		slashType.Value = slashtype
		if newHitbox then
			newHitbox.Visualizer = false
			newHitbox:HitStart()	
			print("slash" .. slashType.Value)
		end
		defaultSpeed()
		if slashtype == "norm" then	
			coroutine.resume(coroutine.create(function()
				if click == 1 then
					Swing1Sfx:Play()					
					playSound:FireServer(character:WaitForChild("Sounds"):WaitForChild("Swing1"))
					character:WaitForChild("Torso"):WaitForChild("Swing1").Volume = 0					
				elseif click == 2 then
					Swing2Sfx:Play()					
					playSound:FireServer(character:WaitForChild("Sounds"):WaitForChild("Swing2"))
					character:WaitForChild("Torso"):WaitForChild("Swing2").Volume = 0						
				elseif click == 3 then
					Swing3Sfx:Play()					
					playSound:FireServer(character:WaitForChild("Sounds"):WaitForChild("Swing3"))
					character:WaitForChild("Torso"):WaitForChild("Swing3").Volume = 0						
				elseif click == 4 then
					Swing4Sfx:Play()					
					playSound:FireServer(character:WaitForChild("Sounds"):WaitForChild("Swing4"))
					character:WaitForChild("Torso"):WaitForChild("Swing4").Volume = 0						
				end	
			end))	
			if tool.Name:match("Katana") then
				task.wait(0.165/Speed) 
			elseif tool.Name:match("Odachi") then
				task.wait(0.2/Speed) 
			end			
		elseif slashtype == "heavy" then
			coroutine.resume(coroutine.create(function()
				Swing4Sfx:Play()					
				playSound:FireServer(character:WaitForChild("Sounds"):WaitForChild("Swing4"))
				character:WaitForChild("Torso"):WaitForChild("Swing4").Volume = 0
			end))			
			if tool.Name:match("Katana") then
				task.wait(0.24/Speed) 
			elseif tool.Name:match("Odachi") then
				task.wait(0.36/Speed) 
			end	
		end
		if newHitbox then
			newHitbox:HitStop()
		end
		slashType.Value = "nil"
		Trail.Enabled = false
		servTrailChange:FireServer(false)
		db = false
	end
end		

Problem is that the trail that happens when slash is triggered lasts longer when the fps is locked to 60, and lasts for less time when i have an fps unlocker.

thanks, i’ll try it out! (filler)

Instead of relying on task.wait(), you could make use of event markers;
https://create.roblox.com/docs/building-and-visuals/animation/animation-events

you make a marker in the animation for when the trail should appear, and a marker for when it should disappear.

1 Like

wow, i’ve never heard of this! i’ll try it out. however, i also use task.wait(n) for another thing, specifically move cooldowns. do you know a workaround for that?

1 Like

When someone does a move you can check the amount of time that has passed since the last time that they did the move. Then don’t do the move if not enough time has passed.

2 Likes

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