Simple alternative for optimization instead of using TweenService (explaining interpolation)

I made this post to help programmers frequently using TweenService optimize their code, as well as helping them understand how interpolation and lerping works. I Hope you’ll find this post useful :smile:

Interpolating more objects' properties at once

When using TweenService, you create a new thread each time you play the tween - using this method on more objects at once may impact performance.

A quick solution is using a while loop.
This solution yields and is especially useful for when you need to wait for the tween to end before performing a new task. If you want it not to yield, you can just wrap it into a coroutine, creating a new thread, which is still better than creating one for each object.

Here’s how you can tween the transparency of a number of objects, using linear interpolation (aka EasingStyle), without the use of TweenService:

Code
--- an array of objects you want to tween
local objects = workspace.Objects:GetChildren()

--- the time it takes to fully tween the properties
local timeDuration = 10 -- can be any number

local startTick = tick() -- you can use any type of global for getting the time, like os.clock()

--- the start and end values of the objects
local startValue, endValue = 0, 1

--- looping until the time's up
while tick() - startTick <= timeDuration do
    --- looping again through all objects
    for i = 1, #objects do
        --- changing the transparency of them (I'll explain the equation in a few moments)
        objects[i].Transparency = startValue + ((endValue - startValue) * (tick() - startTick) / timeDuration)
    end

    --- waiting so it doesn't crash, you can also use RunService's (.event) :Wait() for smoother transactions, though I suggest just going for wait()
    wait()
end

--- sometimes it doesn't finish interpolating until the end, as tick() overshoots the time without being equal to it
--- this is why it's good to make sure that the you finish by changing the property to the endValue
for i = 1, #objects do
    objects[i].Transparency = endValue
end

To break down the equation for interpolation, we have 3 variables: a, b, alpha

a - is the start value (startValue)
b - is the end/goal value (endValue)
alpha - is a number between 0 and 1 which determines how far between a and b the number returned is ((tick() - startTick) / timeDuration)

e.g.

e.g.: if alpha is 0.5, the number is half way between a and b, so if a = 0 and b = 3 then the number returned would be 1.5; if alpha = 0.25, a = 0, b = 4 the number returned would be 1 - a quarter away from a to b.
(I hope this helped, if you still have questions regarding this, feel free to ask :smile:)

PS: You can, of course, use any other values than 0 for the start value, but I used it as it’s easier to visualize how it works

We determine alpha by using (currentTime - startTime) / duration

Now that we got over the theoretical explanation, what about writing the formula?

a + (b - a) * alpha

The way it works is that you add the startValue (a) to the difference between the startValue and endValue (b-a), which difference is multiplied by alpha, being a number between 0 and 1, so (b-a) * alpha cannot be lower than 0 or greater than b-a.
In the end, the returned value cannot be lower than a <= (a + 0 = a) or higher than b <= (a + b - a = b)

Interpolating other properties than numbers (using Lerp)

Some of you might be wondering how we can tween properties that aren’t numbers, and for that Roblox has offered us a solution: Lerping

Properties such as Color3, Vector3, Vector2, CFrame, UDim and UDim2 have a function called :Lerp() that lets us easily interpolate between numbers:

Code

Example from above:

object.Transparency = startValue + ((endValue - startValue) * (tick() - startTick) / timeDuration)
a + (b - a) * alpha

Changing the Color:

object.Color3 = startValue:Lerp(endValue, (tick() - startTick)/timeDuration)
a:Lerp(b, alpha)

Interpolating using other easing styles

TweenService has yet another useful function called :GetValue() which can be used to get a new alpha, depending on the easing style you’re looking for.

How to use it:

--- getting the new alpha depending on the easing style and easing direction (I'm using Bounce and Out, but these can be changed)
local newAlpha = TweenService:GetValue((tick() - startTick) / timeDuration, Enum.EasingStyle.Bounce, Enum.EasingDirection.Out)

-- using the newAlpha for interpolation
object.CFrame = startValue:Lerp(endValue, newAlpha)

PS: As you’re using the variable only once, I suggest that you instead just put it inside the :Lerp function like this:

object.CFrame = startValue:Lerp(endValue, 
    TweenService:GetValue((tick() - startTick) / timeDuration, Enum.EasingStyle.Bounce, Enum.EasingDirection.Out)
)

Hopefully you found this useful and learned something new

:smiley:
For any suggestions or questions, comment below! I’ll try to help as much as I can :wink:

19 Likes

Disclaimer: Don’t micro-optimize unless you need to. (1% lows are terrible, or overall fps is tanking on your good desktop computer and that’s no good.)

EDIT: I just realized I deleted this and a little rough draft which was the intended deletion.
EDIT: Also some extra content and error correction.

Very constructive thread! It’s not hard to follow, and I like the idea of spreading the downsides of TweenService, although it can be used rather cleverly sometimes. I was simply browsing around and this thread intrigued me, so here’s a thought I had.

I hope you don’t mind if I add on, that in some cases with things like tweens in which namecalls become exponentially more expensive across mass objects; try predefining a variable called “TweenCreate” i.e. local TweenCreate = TweenService["Create"]
Now when creating a new tween you just add the TweenService variable as the very first argument before all others. This is rather quick and possibly unneeded, but might be worth a shot if you need the smallest 1% lows boost, might even help overall; just experiment when you’re done with the base script!

Sincerely, IDoLua

Shower thoughts ~ 20 21 is 12 in reverse… Happy new year!

2 Likes

Hello!

One of the most common applications for this is tweening the transparency of the character’s parts, even more if it’s R15. This is not exactly “micro-optimization” as I’m pretty sure, even if you decide that you want it to not yield, it’s still better, as I said before:

From the example above, having one thread for the whole character is a great optimization compared to having 15 threads run all at once, doing the exact thing.

Imagine if in a game, besides all the things happening, you also have like 10 characters disappearing. That makes it 150 more threads instead of 10.
Now imagine if the whole game had features optimized that way. The number of threads would get lowered exponentially, only using this method.

The other primary reason I’ve posted this is because people can learn from this. I feel like this post can help a lot of people understand how interpolation and lerping works and how to use it in an effective way. Outside of Roblox, it’s hard to find services like TweenService to interpolate for you.

Best regards,
Mike

1 Like
local object = workspace.Object
local TweenService = game:GetService("TweenService")
local startTick = os.clock()
local TimeDuration =10
wait(4)
local newAlpha = TweenService:GetValue((os.clock() - startTick) / TimeDuration, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut)

-- using the newAlpha for interpolation
object.CFrame = object.CFrame:Lerp(object.CFrame * CFrame.new(0,4,0), newAlpha)
``` Can you explain it more? I am a bit confused about TimeDuration. Its not smooth at all.

Hello!

Here’s a problem I found that should fix it for you:

Save “object.CFrame” into a variable before, as you keep updating it and adding to the updated CFrame.

Something like this should fix it:

local startCFrame = object.CFrame
object.CFrame = startCFrame:Lerp(startCFrame * CFrame.new(0,4,0), newAlpha)

I also advise that you have a variable for endCFrame (startCFrame * CFrame.new(0,4,0)) so roblox doesn’t calculate it everytime. This way you’ll save some resources while just using a variable-worth of memory.

I’m guessing you’re already using a loop, like I’ve explain above, but just in case: make sure to do that as well :smiley:

Sorry for the late response, I haven’t visited this site in a while and just saw the notification. I hope this solves your problem :+1:

Best regards,
Mike

1 Like

Thank you! Using your method I was able to make a silky smooth egg hatch!

1 Like