RunService issue

Hello developers. I’m in a bit of a sticky situation here. The following code does what it does, which is move the part 10 studs by 5 seconds.

local Part = script.Parent
local Goal = 10 -- 10 studs.
local Length = 5 -- Meaning that the goal should be reached by 5 seconds in this case.
local StudsPerSecond = (Goal / Length) -- How many studs the part will move in a second.
local StudsTraveled = 0

local RunService = game:GetService("RunService")
local Connection = nil -- For disconnecting it later.

Connection = RunService.Heartbeat:Connect(function(deltaTime)
	local Increment = StudsPerSecond * deltaTime -- The number of studs that I need the part to move each frame.
	Part.Size += Vector3.new(0, Increment, 0)
	StudsTraveled = math.min(Goal, StudsTraveled + Increment)
	if StudsTraveled == Goal then
		Connection:Disconnect() -- So the event doesn't constantly fire.
	end
end)

However, depending on if the frame is delayed because of FPS drop, or if the device freezes, the part will literally skip a few studs, making it look unsmooth. Any way to fix this?

2 Likes

Can’t you just tween the part’s position (way more performant than using Heartbeat)?:

local Part = script.Parent
local Goal = 10 -- 10 studs.
local Length = 5 -- Meaning that the goal should be reached by 5 seconds in this case.
local TweenService = game:GetService("TweenService")
local tInfo = TweenInfo.new(Length, Enum.EasingStyle.Linear, Enum.EasingDirection.In) --  creates tween info

local tween = TweenService:Create(Part, tInfo, {Position = Part.Position + Vector3.new(0, Goal, 0)}) -- create tween
tween:Play()
--if you want to stop it: tween:Pause() or tween:Cancel()

The code should reduce the chance of FPS drops

1 Like

I really wish people wouldn’t keep recommending TweenService for everything. I’m using RunService for a reason here. If you can actually help me out with this, please do that instead. This is just an example of what I am trying to do, not the actual code, else this would be way longer than it needs to be, and nobody would be willing to help out.

What you have already is the correct response to frame drops. It’ll approach the target in 5 seconds regardless of frame variation.

You can sync it with a certain frame rate by assuming deltaTime is a specific value like 1/60, but when your frames drop it will take longer than 5 seconds to reach the target. Such is the cost of desyncing from the heartbeat step!

This goes against modern practices though, is there a reason why you want to do this?

Thanks for your reply. How would I do that? My reason for this is I have a part that I want to scale up in 0.1 seconds, although it’s 5 seconds in the example for this case. However, if the game freezes for any reason, the part becomes bigger than what the goal is, which is 10 studs, and that completely throws it off.

Since I have other stuff running in the same script, TweenService won’t do. I need a way to make it stop at the goal, even if the game freezes or lag in general happens.

1 Like

Ah I see what your problem is here. Instead of adding a Vector3 with the increment, you could instead try to add time instead:

local t = 0
Connection = RunService.Heartbeat:Connect(function(deltaTime)
    t += deltaTime
    t = math.min(Length, t)
    Part.Size = Vector3.new(0, t * StudsPerSecond, 0)
    
    if t == Length then
        Connection:Disconnect()
    end
end)

This way you’re basing your size off of time properly instead of just adding whatever deltaTime spits at you. Upon the disconnect happening, the t value will be at 5 seconds, which makes the part size finalize at the correct size.

The StudsTraveled value you have acts similar to this, but does not provide anything for the part size and instead only provides for the condition to stop the event. What I wrote essentially limits what time could end up being for the last frame, and as a result is used for the condition as well since that makes the most sense.

Edit: the first part of what I said is already something you’re doing lol

1 Like

This worked fine, but the problem is that the part resets its size now. Trying to add
Part.Size += Vector3.new(0, t * StudsPerSecond, 0) only causes the size to overflow. I don’t think this will work sadly :frowning:

Yeah so I did forget to mention about that earlier. The original size wasn’t taken into account.

You must first cache the part’s original size outside of the heartbeat:

local oSize = Part.Size

Then you must offset from the original size. This should be equivalent to what you wanted before:

Part.Size = oSize + Vector3.new(0, t * StudsPerSecond, 0)
1 Like