Wow, really nice tutorial!
I hope this will be helping other developers who want their game to have a good GUI.
thanks for the help we really appreciated your work
This is really cool especially since it’s something sort of new to roblox; gradients. You did an amazing job on animations (smooth) and making this open sourced.
I don’t know if this is me or applies to a lot of users; gradients don’t fit “my theme” of user interface. They just don’t look right having them as a button because of the way they look.
This is a really good tutorial
Thank you for this tutorial on gradients! Helped me out a lot.
This is so useful! Thank you for this I’ll be using it for my future games.
This is a great guide! Learned a lot from it.
Although I think there’s a much nicer way to do rainbows (it does require a loop rather than TweenService). For those interested, you can read more about it on this site explaining rainbows and some colour theory behind them, as well as how to implement them (it’s in JavaScript, not Lua). The code carries over quite easily and it’s pretty easy to follow along in case you don’t know JS.
The overhead is fairly simple, if you have a UIGradient all you need to do is the following:
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)
It’s different than how a TweenService implementation would look like, but in a way the update loop every frame is in itself a custom Tweening (it’s linear anyways, so TweenService doesn’t really add functionality). It’s much shorter this way, and if you wanted to get a hover effect that changes directions, all you need to do is set up a state system (again, different from TweenService - instead of hovering calling a certain Tween into action, we make a custom state system using booleans or by changing the variables directly if they’re in the scope). This would involve something like:
rainbow.MouseEnter:Connect(function()
w = -math.pi/12
end)
rainbow.MouseLeave:Connect(function()
w = math.pi/12
end)
And thus you get the same hover effect. I think it’s a lot cleaner for how little code is required (thanks to sinusoidals). I still think the Tweening method is more elegant, maybe I’ll look into optimizing that so it takes less code to write. Although I’m thinking it’s probably within reach since TweenService has a sine easing style…
Looks like I have some work to do.
Today I learned we need a lot more 10 bit displays.
Will we be expecting a module with alot of UIgradient animations?
Interesting idea. I did think of this before but I thought that there were so many possibilities and ways to customize them that having preset ones in a module. Plus, these animations are mostly easy and they’re in a tutorial so everyone should be able to know how to create them. However, I suppose that I can maybe create such a module.
Don’t mind if i do. Yoink! Thanks for that
Wow, this is amazing! Wasn’t looking for this, but now I’ll be sure to use this for future projects!
At first, I would like to thank you for this awesome idea. This is really cool and might help me earn x2.
I’m facing a problem with HoverStay. The script is same as yours, but-
create1.Completed:Connect(function()
if rot == 0 then rot = 180 elseif rot == 180 then rot = 0 end
completed() -- Here, the completed word is underlined and it says that "W001: Unknow global 'completed' "
end)
It works when it’s hovered for the first time, but after it stops - it doesn’t work anymore.
Would be really helpful if you could help. Anyways, if anyone wanna see it live in a game, play this game or watch a GIF at gyazo
I also made a HoverShine effect.
Cool tutorial! I learned a few things from this!
I really loved @tralalah’s code, but for some reason it was taking up about 0.3% in the script performance analyzer for a single instance every few hundred milliseconds. I decided to pre-cache all of the ColorSequenceKeypoints, since they repeat, and minimize the actual loop that runs during the heartbeat. I also added some more comments to help those who are more beginners understand what is going on. I am going to expand this to be a general “rainbowifier” where you can inject any UIGradient into the process. It should be pretty trivial to make all gradients animate the same or set a random offset and have them all be different. I will post it to this thread when I finish it.
local RS = game:GetService("RunService")
local rainbow = script.Parent -- GUI object
local grad = rainbow.UIGradient
local counter = 0 -- phase shift {def = 0}
local w = math.pi / 6 -- frequency [the lower the number the tighter the rainbow] {def = 12}
local CS = {} -- colorsequence table
local num = 15 -- number of colorsequence points (maxed at 16) [provides a more granular rainbow] {def = 15}
local frames = 0 -- frame counter, used to buffer if you want lower framerate updates {def = 0}
local count = 0
local cskCache = {}
while true do
-- build a ColorSequenceKeyPoint
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
local newCS = ColorSequence.new(CS)
-- build the cache
if #cskCache > 0 then
if newCS == cskCache[1] then
CS = {}
break
end
end
table.insert(cskCache, newCS)
-- clear out the CS table
CS = {}
-- this counter effectively sets the total cache to 80 ColorSequences (countStart = 0, countIncrement = math.pi/40, countMax = 2*math.pi)
counter = counter + math.pi/40
if (counter >= math.pi * 2) then counter = 0 end
end
local finalCacheCt = #cskCache
local rotation = 1
RS.Heartbeat:Connect(function()
if math.fmod(frames, 2) == 0 then
-- set the new gradient.
grad.Color = cskCache[rotation]
-- reset rotation once we have iterated through all the frames
if rotation >= #cskCache then rotation = 0 end
-- move to the next frame
rotation = rotation + 1
end
-- controls when the frame fires based off the fmod() above. We reset to prevent the number from endlessly increasing
if frames >= 1000 then frames = 0 end
frames = frames + 1
end)
The loop itself is pretty heavy, calling table.insert
and resetting the UIGradient by calling a new ColorSequence
every 2 frames will take its toll. Thanks for cleaning up the code and expanding on it
There is one improvement I made in the meantime that I think is quite important for those who don’t want to cache (idk, maybe for memory constraints or something) - reset the color sequence table CS = {}
before the for i = 0, num do
loop when you are running this on a RunService event. Sometimes if the loop is running behind, a new event will fire before the previous one is finished (which means, CS = {}
might not be called before it starts adding new elements to the table). Calling it at the start of the loop is safer and doesn’t change anything performance wise (in theory).
If you guys can’t find the animation you want here, I really recommend this plugin. Great tutorial though!
Thanks for sharing this post! I was looking for a rainbow like effect to add into my game and I got inspired! I’ve got some code here to share in case it helps anyone. It assumes you already have a UIGradient
with pre-set keypoints and takes those and shuffles them around. It’s not a perfect rainbow effect but looks neat!
local function splitKeypoint(keypoint, startTime)
return ColorSequenceKeypoint.new(startTime, keypoint.Value)
end
local function shuffleKeypoints(keypoints)
local newColors = {}
math.randomseed(os.clock())
for i = 1, #keypoints do
local j = math.random(i)
local startTime = keypoints[i].Time
table.insert(newColors, splitKeypoint(keypoints[j], startTime))
end
return newColors
end
local function animate(gradient)
local tweenInfo = TweenInfo.new(1, Enum.EasingStyle.Linear, Enum.EasingDirection.Out)
local tween = TweenService:Create(gradient, tweenInfo, {Offset = Vector2.new(1, 0)})
local keypoints = gradient.Color.Keypoints
while true do
tween:Play()
tween.Completed:Wait()
gradient.Offset = Vector2.new(-1, 0)
gradient.Rotation = gradient.Rotation == 0 and 180 or 0
gradient.Color = ColorSequence.new(shuffleKeypoints(keypoints))
end
end
Really good Tutorial !!!
Thanks a lot
how to do that rainbow reverse but shift from right to left instead
maybe do a negative tween?
local tween = TweenService:Create(gradient, tweenInfo, {Offset = Vector2.new(-1, 0)})