Wowozers this looks very cool. Is there any way to reduce the amount of repeated code in the script maybe using tweens or for loops?
You can probably do something like this for the same effect with easier configuration (there’s better ways to make it smoother as well).
local ScreenGui = script.Parent
local TextLabel = ScreenGui.TextLabel
local UIGradient = TextLabel.UIGradient
local GradientColors = {
BrickColor.new("Bright red").Color,
BrickColor.new("Bright orange").Color,
BrickColor.new("Bright yellow").Color,
BrickColor.new("Bright green").Color,
BrickColor.new("Bright blue").Color,
BrickColor.new("Royal purple").Color
}
while wait(0.1) do
local tVal = GradientColors[1]
table.move(GradientColors, 2, #GradientColors, 1)
GradientColors[#GradientColors] = tVal
local transitColors = {}
for seq, color in pairs (GradientColors) do
table.insert(transitColors, ColorSequenceKeypoint.new(((seq-1))/(#GradientColors-1), color))
end
UIGradient.Color = ColorSequence.new(transitColors)
end
The problem with your script is that ColorSequnce one has the position from 0 -1. Your position number are increasing
ColorSequences are required to have a time of 0.0 and 1.0 otherwise it won’t run, this one will automatically determine time positions depending on how many colors you assign to the GradientColors list (as long as it’s 2 or more, the amount needed for keypoints).
It works just fine.
O wow, i didnt know that. TY
One smoother option is that you could manually calculate the color of each keypoint every frame, which is complex but probably not a bad solution. Here’s a working implementation (sorry formatting is a little stuffed up mb):
local colors = {
BrickColor.new("Bright red").Color,
BrickColor.new("Bright orange").Color,
BrickColor.new("Bright yellow").Color,
BrickColor.new("Bright green").Color,
BrickColor.new("Bright blue").Color,
BrickColor.new("Royal purple").Color
}
local num_colors = #colors
local color_length = 1 / num_colors
local period = 3 -- how long it takes for full animation
game:GetService("RunService").RenderStepped:Connect(function()
local progress = (tick() % period) / period -- ranges from 0 to 1 based on progress
local newColors = {}
local wrapColor = false -- the color at the edges
for i = 1, num_colors + 1 do -- add 1 for extra edge
local color = colors[i] or colors[i-num_colors] -- accounting for +1
local position = progress + (i-1)/num_colors -- the current color's position
if position > 1 then position = position - 1 end -- wrap positions
if position == 0 or position == 1 then wrapColor = true end -- the color is at edge
table.insert(newColors, ColorSequenceKeypoint.new(position, color))
end
if not wrapColor then -- if there were no clean matches on the edge
local indexProgress = ((1 - progress) / color_length) + 1
local col1 = colors[math.floor(indexProgress)]
local col2 = colors[math.ceil(indexProgress)] or colors[1]
local finalCol = col1:Lerp(col2, indexProgress % 1) -- find the color between these two cols
table.insert(newColors, ColorSequenceKeypoint.new(0, finalCol))
table.insert(newColors, ColorSequenceKeypoint.new(1, finalCol))
end
table.sort(newColors, function(a, b)
return a.Time < b.Time
end)
script.Parent.Frame.UIGradient.Color = ColorSequence.new(newColors)
end)
Alternatively, if you’re tweening a frame background and not text itself, you could treat this like the infinitely scrolling background image problem (example How to make moving background scroll infinitely?). The solution here is to tween both of the gradient frames in one direction, then reset their positions and repeat.
local frame1, frame2
local tweenInfo = TweenInfo.new(1, Enum.EasingStyle.Linear)
local tween1 = game:GetService('TweenService'):Create(frame1, tweenInfo, {Position = UDim2.new(1, 0, 0, 0})
local tween2 = game:GetService('TweenService'):Create(frame2, tweenInfo, {Position = UDim2.new(0, 0, 0, 0})
tween1.Completed:Connect(function()
tween2:Cancel() -- make sure the other tween isn't running
frame1.Position = UDim2.new(0, 0, 0, 0)
frame2.Position = UDim2.new(-1, 0, 0, 0)
tween1:Play()
tween2:Play()
end)
frame1.Position = UDim2.new(0, 0, 0, 0)
frame2.Position = UDim2.new(-1, 0, 0, 0)
tween1:Play()
tween2:Play()
In this example, frame1
is the default frame you see at the start of the animation, and frame2
is to the left of it, hidden by the ClipDescendants property. Both tween to the right, and then when the animation is done it resets their positions to the default positions and tweens them to the right again.
That is not what the OP is asking for it to look like, though. You cannot do that while showing all of the colors at once like the OP wants as there is no way to show a gradient twice within a single gradient in that way. One idea I had was putting the gradient into the ColorSequence twice, but that still won’t work as we can’t scale a gradient’s size (in this case to be double its normal size).
Just to add on, I proposed a solution that takes up a lot less work with smooth results (and is what I use currently). I talk more about it here if you are interested:
But as a rundown, you can just use sinusoidals to generate rainbow (and other repetitive) colour sequences. And due to them being sinusoidals, you can offset them by a phase shift which can be updated every frame. The code for this is below, it’s also in the post I made above.
I didn’t come up with this myself, I read about it here. It’s a long read (and engaging!) but it will help you understand where the code is coming from and what the technique is really doing if you wish to take a look.
local RS = game:GetService("RunService")
local rainbow = script.Parent -- GUI object
local grad = rainbow.UIGradient
local counter = 0 -- phase shift
local w = math.pi / 12 -- frequency
local CS = {} -- colorsequence table
local num = 15 -- number of colorsequence points (maxed at 15 or 16 I believe)
local frames = 0 -- frame counter, used to buffer if you want lower framerate updates
RS.Heartbeat:Connect(function()
if math.fmod(frames, 2) == 0 then
for i = 0, num do
local c = Color3.fromRGB(127 * math.sin(w*i + counter) + 128, 127 * math.sin(w*i + 2 * math.pi/3 + counter) + 128, 127*math.sin(w*i + 4*math.pi/3 + counter) + 128)
table.insert(CS, i+1, ColorSequenceKeypoint.new(i/num, c))
end
grad.Color = ColorSequence.new(CS)
CS = {}
counter = counter + math.pi/40
if (counter >= math.pi * 2) then
counter = 0
end
end
if frames >= 1000 then
frames = 0
end
frames = frames + 1
end)
This really works well, but its moving right to left, I want it to move left the right
All you have to do is subtract counter
inside math.sin
instead of adding it. Glad it works well for you
You can also do this with UIGradient’s offset property. Just flip the rotation every once in a while and it’s way easier and shorter than anything posted here.
Code:
(could probably be cleaned up but I made it in like 5 minutes)
local speed = 1
rs.RenderStepped:Connect(function(dt)
gradient.Offset = Vector2.new(gradient.Offset.X + (speed*dt), 0)
if gradient.Offset.X >= 1 then
local isRotated = (gradient.Rotation == 180)
gradient.Rotation = isRotated and 0 or 180; gradient.Offset = Vector2.new(-1, 0) --isRotated and -0.5 or -1
end
end)
This is similar to the one provided by roblox in documentation.
In all honesty, everything that’s been discussed previously on this thread can be accomplished using the RenderStepped loop provided by roblox without taking an entire day of concentration as stated by @Ashp116.
No it took a day of full concentration to make it
A lot of the code that’s been provided sort of complicates things and could potentially cause more client side stress especially because a lot of the loops I’ve seen been being used are loops such as:
while wait() do
-- code
end
So unless I’m misunderstanding what the purpose of the thread is or what’s trying to be accomplished I suggest just using the link I provided above to create your own moving gradient script with a renderStepped loop that will not stress the client and is very easy to understand.
Is there a way to make it serversided? If it’s put in a local script only the local player can see it.
Yes use remove event
Do this:
game.ReplicatedStorage.Event.OnClientEvent()
local speed = 1
rs.RenderStepped:Connect(function(dt)
gradient.Offset = Vector2.new(gradient.Offset.X + (speed*dt), 0)
if gradient.Offset.X >= 1 then
local isRotated = (gradient.Rotation == 180)
gradient.Rotation = isRotated and 0 or 180; gradient.Offset = Vector2.new(-1, 0) --isRotated and -0.5 or -1
end
end)
end)
And is server do
game.ReplicatedStorage.Event:FireAllClients()
Hope it helps!
I did this in a localscript:
game.ReplicatedStorage.Event.OnClientEvent()
local speed = 1
local gradient = script.Parent.UIGradient
local rs = game:GetService("RunService")
rs.RenderStepped:Connect(function(dt)
gradient.Offset = Vector2.new(gradient.Offset.X + (speed*dt), 0)
if gradient.Offset.X >= 1 then
local isRotated = (gradient.Rotation == 180)
gradient.Rotation = isRotated and 0 or 180; gradient.Offset = Vector2.new(-1, 0) --isRotated and -0.5 or -1
end
end)
And this in the script:
game.ReplicatedStorage.Event:FireAllClients()
Both are under a nametag script in serverscriptservice under the textlabels
but the rainbow isn’t showing up
How hard is it to make it between a select few colors? Like Red, Dark Red, Light Red. Or White, Black, Gray
Could u elaborate on your question
I mean as simple as making the text black and white gradient instead of a rainbow gradient? Just wondering how hard it would be to make it a black and white pattern.
one of the easiest ways to do that would probably be just using the one he shows in the post but with only black and white