I have a problem: you can’t tween the color of a Beam. This simply won’t do.
SUPERTWEEN
THAT’S RIGHT, NOW YOU CAN EVEN TWEEN TEXT!!!
SuperTween - Creator Store (roblox.com)
SuperTween is a utility for tweening anything you want. You can create your own rules for any data type or instance property. It’s compatible with the usual TweenService
code, so you can swap it in and out effortlessly. It is fully open source and actively tested in my projects.
Here’s an example of tweening various particle properties:
local tweenInfo = TweenInfo.new(3, Enum.EasingStyle.Elastic)
local properties = {
Size = NumberSequence.new({
NumberSequenceKeypoint.new(0, 1),
NumberSequenceKeypoint.new(1, 5)
}),
Transparency = NumberSequence.new({
NumberSequenceKeypoint.new(0, 0.5),
NumberSequenceKeypoint.new(1, 0)
}),
Color = ColorSequence.new({
ColorSequenceKeypoint.new(0, Color3.fromRGB(255, 0, 0)),
ColorSequenceKeypoint.new(1, Color3.fromRGB(255, 0, 255))
})
}
local tween = SuperTween.new(script.Parent.ParticleEmitter, tweenInfo, properties)
tween:Play()
Here’s an example of tweening a beam:
local tweenInfo = TweenInfo.new(5, Enum.EasingStyle.Linear)
local properties = {
Transparency = NumberSequence.new({
NumberSequenceKeypoint.new(0, 1),
NumberSequenceKeypoint.new(1, 0)
}),
Color = ColorSequence.new({
ColorSequenceKeypoint.new(0, Color3.fromRGB(255, 0, 0)),
ColorSequenceKeypoint.new(1, Color3.fromRGB(255, 0, 255))
})
}
local tween = SuperTween.new(script.Parent.Beam, tweenInfo, properties)
tween:Play()
Here’s an example of tweening text:
local tweenInfo = TweenInfo.new(5, Enum.EasingStyle.Sine)
local properties = {Text = "This is SUPER TEXT"}
local tween = SuperTween.new(script.Parent.TextLabel, tweenInfo, properties)
tween:Play()
Example of tweening a model’s scale:
local tweenInfo = TweenInfo.new(5, Enum.EasingStyle.Elastic)
local properties = {Scale = 0.5}
local tween = SuperTween.new(workspace.Chrythm, tweenInfo, properties)
tween:Play()
Here’s an example of tweening a model’s pivot:
local tweenInfo = TweenInfo.new(self.MoveTime, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut, -1, true)
local properties = {Pivot = self.EndCFrame}
local tween = SuperTween.new(self.Instance, tweenInfo, properties)
tween:Play()
These floating flower platforms are made up of parts, and use SuperTween to get around.
You may notice that while they are moving back and forth, they are also hovering up and down. This is possible through the Updated
event.
Events
Along with the typical Completed
event, SuperTween gives you an Updated
event. This event passes the current alpha
of the tween, which is a value from 0 to 1 used for lerping.
In the case of the flowers, the Update
event is simply used to apply an additional vertical offset.
tween.Updated:Connect(function()
local timeNow = tick() % 10000
local height = math.sin(timeNow * MovingFlower.OscillateSpeed) * MovingFlower.OscillateHeight
local rotation = timeNow * MovingFlower.SpinSpeed
local cframe = self.Instance:GetPivot()
cframe += Vector3.yAxis * height
cframe *= CFrame.Angles(0, rotation, 0)
self.Instance:PivotTo(cframe)
end)
This is my implementation of a spinning, oscillating movement. What’s nice is that because this event is fired after SuperTween modifies the Pivot, we can assume the flower is horizontally positioned correctly. And from here, we just apply some offsets using math to get the desired motion.
And as an added bonus, we can move any players standing on the platform after every update for a precise physics feel.
Note: The
Completed
event does not pass anEnum.PlaybackState
like TweenService. Instead, it is an event that fires when the tween is over. This is because multiple SuperTweens can overlap, and do not cause the other to cancel playback. This behavior deviates from TweenService and may need to be taken into consideration if something unexpected occurs.
Data Types
In the DataTypes module, you will find all of my current implementations of tweening data types. Some of these, like ColorSequence and NumberSequence, are only possible through SuperTween or another custom utility. The nice thing here is that you can control how ANY data type is tweened!
For example, here is how tweening a Vector3 is implemented:
function DataTypes.Vector3(a, b, t)
return a:Lerp(b, t)
end
Other than dealing with TweenInfo, this is pretty much built-in for us. Lerp is a function of Vector3s and some other data types, so we can use the same code for them as well.
function DataTypes.Vector2(...)
return DataTypes.Vector3(...)
end
function DataTypes.CFrame(...)
return DataTypes.Vector3(...)
end
function DataTypes.Color3(...)
return DataTypes.Vector3(...)
end
In this module, the data types are implemented by name. For example, if you run this code in the command bar:
print(typeof(Color3.fromRGB(255, 170, 120)))
The console prints out a string that says “Color3” — names are interpreted from the typeof
function. So, if you want to tween the number 5 for example, find out what typeof(5)
is, then use the result as the name of the function. (It’s “number”)
Speaking of numbers, do you recognize what I did for tweening the number data type?
function DataTypes.number(a, b, t)
return a + ((b - a) * t)
end
This is the classic lerp formula! My understanding of this is that we:
- Take the starting point
- Find the space between the points
- Add a percentage of that space
This is essentially the definition of a lerp, and it works with most data types based on numbers. So if we use an alpha of 0.5, that would represent 50% of the way from point A to B. The result would be halfway between the two points. (I used the letter “t” for “time” to represent alpha in this implementation)
Custom Properties
In the Customs module, you will find an implementation of tweening pivots.
Customs.Pivot = {
Whitelist = {"PVInstance"},
Set = function(instance, a, b, t)
local cframe = DataTypes.CFrame(a, b, t)
instance:PivotTo(cframe)
end,
Get = function(instance)
return instance:GetPivot()
end
}
There are a few things to talk about here, but let’s start with the whitelist. Pivots exist for any instance that inherits from PVInstance
, like a BasePart or Model. (A Part, UnionOperation, and a MeshPart are all BaseParts!)
Because Roblox has a class inheritance structure, we can specify groups of object behavior by name. For example, a Part gets most of its behavior from BasePart. You can see this in the documentation for a Part:
This is also the case for MeshParts and UnionOperations. Anyways, only objects that inherit from PVInstance will have Pivots. So, that is exactly what we put in the whitelist.
The Get and Set functions tell SuperTween what to do for each update.
-
Get
returns the current value of what is being modified — is passed the instance being tweened. -
Set
applies a modification — is passed the instance being tweened, the starting value, the target value, and the alpha. It is up to you to determine how to implement this lerp. You have the DataTypes module at your disposal for lerping all sorts of data types.
Conclusion
That about covers it! I am continuing to improve this utility as I use it in my projects. It’s open source, so feel free to suggest edits in the replies. The motivation behind developing this was to implement sticking to tweening platforms, but it has since come in handy for many other things, such as getting around the TweenService framerate limit. Let me know what you think.
Note: TweenService dynamically lowers its animation framerate. SuperTween does not. If you are trying to work around that behavior, this utility will fix that for you. However, because of that, it may be less performant. You may need to use SuperTween sparingly and ensure that you clean up any ongoing tweens that are no longer needed.