Improve `TweenService` to allow decoupling tweens from instances/properties

As a Roblox developer, it is currently too hard to correctly reproduce the behaviour of tweens in a way that does not rely on having instances and appropriate properties.

Fusion for example manually re-implements almost all of the tween’s behaviour manually. It must also re-implement looping. TweenService’s behaviour is not difficult to replace, but it is extremely difficult to reproduce in a 100% consistent way, and there are many interesting edge cases such as boolean values being tweenable, and -1 repeat count allowing for endless tweens. Lots of these behaviours create complexity in code which attempts to reproduce tween behaviours, and on top of this, many tween reproductions that I have seen don’t even bother to reimplement these due to that complexity, resulting in inconsistencies and confusion. Additionally, without the ability to easily reproduce TweenService’s in luau code it is not possible to, for example, tween attributes.

If Roblox is able to address this issue, it would improve my development experience because it would allow for light weight and easy tweening that doesn’t rely on instances.


Some possible partial solutions

These are some different partial solutions to pieces of the problem to help create a better picture of the problem space as a whole.

A method which tweens two values with the minimum number of inputs.
E.g. local tweenedValue = TweenService:GetTweenedValue(elapsedTime, tweenInfo, startValue, endValue)
Pros:

  • Simple and easy. If your goal is only to reproduce what TweenService allows and nothing more, this just works.
    Cons:
  • Can’t produce more complex tweens, such as combinations of tweens, tweens with custom interpolation curves, etc.

Add a method to TweenService for interpolating arbitrary values in a way that is perfectly consistent with tweens.
E.g. local tweenedValue = TweenService:Interpolate(alpha, startValue, goalValue)
Pros:

  • You can now interpolate in a way consistent with Tweens using :GetValue.
  • You can linearly interpolate any value type that TweenService supports very easily, including values like booleans.
  • Low overhead, and very simple.
    Cons:
  • Does not reproduce full tween repetition/reversal/delay behaviour, which must be solved through other means.

Extend :GetValue to calculate the correct alpha value while taking repeatCount, reverses, and delayTime into consideration.
E.g. local alpha = TweenService:GetValue(elapsedTime/tweenInfo.Time, tweenInfo.EasingStyle, tweenInfo.EasingDirection, tweenInfo.RepeatCount, tweenInfo.Reverses, tweenInfo.DelayTime)
Pros:

  • Reproduces repetition/reversal/delay behaviour.
    Cons:
  • You must still implement correct interpolation for different value types yourself. This would be solved by adding a dedicated method.
  • Clunky and unnecessarily high overhead if you already have a tween info because it requires grabbing the fields every time, or alternatively creating values for each field, which doesn’t feel very clean.

Add a method which determines the alpha of a tween from a duration and a tween info.
E.g. local alpha = TweenService:GetAlpha(elapsedTime, tweenInfo)
Pros:

  • Reproduces repetition/reversal/delay behaviour.
  • Low overhead if you already have a tween info.
  • Really simple, like the TweenService:Interpolate example.
    Cons:
  • High overhead if you want to change individual values.
  • Basically would end up being just another :GetValue with different mechanics.
  • You must still implement correct interpolation for different value types yourself.
  • Replaces some uses of :GetValue while adding overhead to those uses by requiring the creation of a new TweenInfo object every time inputs change.
14 Likes

I support this. I think that TweenService’s Create function should also accept no instance or Goals to be attached either. It can just create some sort of object with a callback:

local myNumber = 0

local tween = TweenService:Create(TweenInfo, 0, 4)
--TweenInfo, start, end; the start and end can be different value types like
--CFrames or Vector3s like you see in instances.
tween.Callback = function(value)
    myNumber = value --fires about every frame the tween runs
end

tween:Play()

Shouldn’t be too hard to implement this yourself either.

2 Likes

It would be nice, but TweenService was designed with instances in mind. What I would like to see is an expansion of the types of data that can be tweened. Numbers, CFrames, UDim2s, etc… can be tweened. But if you have a number value or a number sequence, forget it. However, I have discovered a way to tween a number value using tweens.

Having an LUA callback for tweens…I’m not sure about that one. The issue is that the callback will be in interpreted LUA bytecode which will slow things down. On the other hand, you can tween anything you want at that point.

2 Likes