Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
1

How can I simplify the code for a UI animation I made?

Asked by 3 years ago

Video of the animation: https://streamable.com/rj7zkn


local TweenService = game:GetService("TweenService") local frame = script.Parent local one = script.Parent.one local two = script.Parent.two local three = script.Parent.three local Shop = script.Parent.Shop --mouse leave variables local ogSize = UDim2.fromScale(0.75, 0.166) local ogPos = UDim2.fromScale(0.12, 0.205) local ogPos2 = UDim2.new(0.12, 0,0.417, 0) local ogPos3 = UDim2.new(0.12, 0,0.639, 0) --info local tweenInfoRotation = TweenInfo.new( 0.1, Enum.EasingStyle.Linear, Enum.EasingDirection.Out, 0, false, 0 ) local goalRotation = { Rotation = 20 } local goalSizePosition = { Size = UDim2.new(0.56, 0, 0.11, 0), Position = UDim2.new(0.21, 0 ,0.261, 0) } local goalSizePosition2 = { Size = UDim2.new(0.56, 0, 0.11, 0), Position = UDim2.new(0.21, 0,0.441, 0) } local goalSizePosition3 = { Size = UDim2.new(0.56, 0, 0.11, 0), Position = UDim2.new(0.21, 0,0.641, 0) } local tweenInfoSizePosition = TweenInfo.new( 0.1, Enum.EasingStyle.Linear, Enum.EasingDirection.Out, 0, false, 0 ) local tweenInfoShop = TweenInfo.new( 0.3, Enum.EasingStyle.Quad, Enum.EasingDirection.Out, 0, false, 0 ) local shopNewPos = UDim2.new(-7, 0, -0.001, 0) local shopOgPos = UDim2.new(1.2, 0, -0.001, 0) local tweenEnterShop = TweenService:Create(Shop, tweenInfoShop, {Position = shopNewPos}) local tweenLeaveShop = TweenService:Create(Shop, tweenInfoShop, {Position = shopOgPos}) --one local tweenEnterRotation = TweenService:Create(one, tweenInfoRotation, goalRotation) local tweenEnterSizePosition = TweenService:Create(one, tweenInfoSizePosition, goalSizePosition) local tweenLeaveSizePos = TweenService:Create(one, tweenInfoRotation, {Size = ogSize, Position = ogPos}) local tweenLeaveRotation = TweenService:Create(one, tweenInfoRotation, {Rotation = 0}) frame.one.MouseEnter:Connect(function() tweenEnterRotation:Play() tweenEnterSizePosition:Play() end) frame.one.MouseLeave:Connect(function() tweenLeaveSizePos:Play() tweenLeaveRotation:Play() end) --two local tweenEnterRotation2 = TweenService:Create(two, tweenInfoRotation, goalRotation) local tweenEnterSizePosition2 = TweenService:Create(two, tweenInfoSizePosition, goalSizePosition2) local tweenLeaveSizePos2 = TweenService:Create(two, tweenInfoRotation, {Size = ogSize, Position = ogPos2}) local tweenLeaveRotation2 = TweenService:Create(two, tweenInfoRotation, {Rotation = 0}) frame.two.MouseEnter:Connect(function() tweenEnterRotation2:Play() tweenEnterSizePosition2:Play() end) frame.two.MouseLeave:Connect(function() tweenLeaveSizePos2:Play() tweenLeaveRotation2:Play() end) --three local tweenEnterRotation3 = TweenService:Create(three, tweenInfoRotation, goalRotation) local tweenEnterSizePosition3 = TweenService:Create(three, tweenInfoSizePosition, goalSizePosition3) local tweenLeaveSizePos3 = TweenService:Create(three, tweenInfoRotation, {Size = ogSize, Position = ogPos3}) local tweenLeaveRotation3 = TweenService:Create(three, tweenInfoRotation, {Rotation = 0}) frame.three.MouseEnter:Connect(function() tweenEnterRotation3:Play() tweenEnterSizePosition3:Play() end) frame.three.MouseLeave:Connect(function() tweenLeaveSizePos3:Play() tweenLeaveRotation3:Play() end) --Shop local isShopOpen = false one.MouseButton1Up:Connect(function() if isShopOpen == false then tweenEnterShop:Play() isShopOpen = true else tweenLeaveShop:Play() isShopOpen = false end end)

2 answers

Log in to vote
1
Answered by 3 years ago

A good programming habit is to follow the DRY (Don't Repeat Yourself) principle, this principle helps keep code short and concise. In your case a good improvement would be to create a function which handles the animation of the GUI object.

The function might look like this:

local TweenService = game:GetService("TweenService")

function tweenUI(guiObject)
    local info = TweenInfo.new(
        -- Tween info
    )
    local properties = {
        -- Tween properties
    }
    local tween = TweenService:Create(guiObject,info,properties) -- Create the actual tween
    return tween -- return the tween
end

After looking at your code I see that you want to have a tween when the mouse enters, as well as a tween when the mouse leaves. The properties of the tween (Size,Position,Rotation etc.) are also different for each guiObject, as to be expected.

So we can create a new function for when the enter animation should play and another for when the leave animation should play. We can also change it so that the properties of the created tween will be an argument of the function.

local TweenService = game:GetService("TweenService")

function tweenEnter(guiObject,properties)
    local info = TweenInfo.new(
        -- Tween info
    )
    local properties = {
        properties
    }
    local tween = TweenService:Create(guiObject,info,properties) -- Create the actual tween
    return tween -- return the tween
end
function tweenLeave(guiObject,properties)
    local info = TweenInfo.new(
        -- Tween info
    )
    local properties = {
        properties
    }
    local tween = TweenService:Create(guiObject,info,properties) -- Create the actual tween
    return tween -- return the tween
end

Actually animating the UI objects would look something like this:

local TweenService = game:GetService("TweenService")

function tweenEnter(guiObject,properties)
    local info = TweenInfo.new(
        -- Tween info
    )
    local properties = {
        properties
    }
    local tween = TweenService:Create(guiObject,info,properties) -- Create the actual tween
    return tween -- return the tween
end
function tweenLeave(guiObject,properties)
    local info = TweenInfo.new(
        -- Tween info
    )
    local properties = {
        properties
    }
    local tween = TweenService:Create(guiObject,info,properties) -- Create the actual tween
    return tween -- return the tween
end

local one = script.Parent.one
local two = script.Parent.two
local three = script.Parent.three

local enter1 = tweenEnter(one,PROPERTIES)
local enter2 = tweenEnter(two,PROPERTIES)
local enter3 = tweenEnter(three,PROPERTIES)

local leave1 = tweenEnter(one,PROPERTIES)
local leave2 = tweenEnter(two,PROPERTIES)
local leave3 = tweenEnter(three,PROPERTIES)

one.MouseEnter:Connect(function()
    enter1:Play()
end)
one.MouseLeave:Connect(function()
    leave1:Play()
end)

two.MouseEnter:Connect(function()
    enter2:Play()
end)
twp.MouseLeave:Connect(function()
    leave2:Play()
end)

three.MouseEnter:Connect(function()
    enter3:Play()
end)
three.MouseLeave:Connect(function()
    leave3:Play()
end)

The code above works quite well but there's still quite a lot of repetition, and if you were to add more guiObjects that would become quite a hassle.

So what we can do is to create another function which handles the MouseEnter and MouseLeave events of the GUI, and then applies the tweens to it.

The function might look like this:

function handleTweening(guiObject,enterProperties,leaveProperties)
    local enter = tweenEnter(guiObject,enterProperties)
    local leave = tweenLeave(guiObject,leaveProperties)

    guiObject.MouseEnter:Connect(function())
        enter:Play()
    end)
    guiObject.MouseLeave:Connect(function())
        leave:Play()
    end)
end

The function makes it so that you don't have to create new events and tweens for each GUI object.

The final code would look something like this:

local TweenService = game:GetService("TweenService")

function tweenEnter(guiObject,properties)
    local info = TweenInfo.new(
        -- Tween info
    )
    local properties = {
        properties
    }
    local tween = TweenService:Create(guiObject,info,properties) -- Create the actual tween
    return tween -- return the tween
end
function tweenLeave(guiObject,properties)
    local info = TweenInfo.new(
        -- Tween info
    )
    local properties = {
        properties
    }
    local tween = TweenService:Create(guiObject,info,properties) -- Create the actual tween
    return tween -- return the tween
end

function handleTweening(guiObject,enterProperties,leaveProperties)
    local enter = tweenEnter(guiObject,enterProperties)
    local leave = tweenLeave(guiObject,leaveProperties)

    guiObject.MouseEnter:Connect(function())
        enter:Play()
    end)
    guiObject.MouseLeave:Connect(function())
        leave:Play()
    end)
end
local one = script.Parent.one
local two = script.Parent.two
local three = script.Parent.three

handleTweening(one,ENTERPROPERTIES,LEAVEPROPERTIES)
handleTweening(two,ENTERPROPERTIES,LEAVEPROPERTIES)
handleTweening(three,ENTERPROPERTIES,LEAVEPROPERTIES)
0
Thank you so much for this post, I agree with your method of simplification but I'm confused about one thing. On line 41 of the last code block you posted, the handleTweening function requires the argument "ENTERPROPERTIES" and "LEAVEPROPERTIES". What would I put here? Aren't the properties already defined in the individual functions? And on line 20, you create a tween with 3 parameters... xxkeithx 7 — 3y
0
...but on line 26 you use three arguments. I REALLY like this answer, but I'm having a hard time wrapping my head around it because since theres a lot of variables. Thank you again xxkeithx 7 — 3y
0
I understand your confusion. The ENTERPROPERTIES defines how properties (Size, Position, Rotation) should change when the mouse enters the gui. So you would enter the tween's properties as the argument. IE: {Size = UDim.new(Values here), Position = UDim.new(Values here), Rotation = (Put rotation here)} Vinceberget 1420 — 3y
0
And LEAVEPROPERTIES is the same thing but for when the mouse leaves the GUI Vinceberget 1420 — 3y
View all comments (3 more)
0
So in full it would look something like this: handleTweening(Your gui object,{Size = UDim.new(Enter size here), Position = UDim.new(Enter position here), Rotation = (Enter rotation here)},{Size = UDim.new(Leave size here), Position = UDim.new(Leave position here), Rotation = (Put Leave rotation here)}) Vinceberget 1420 — 3y
0
And as for using two arguments; we don't really need to create a new TweenInfo everytime we call the function, because most of the time you only want the position, size, and rotation of the object to change. Because we don't need to change the tweenInfo over each GUI object we can simply write the tweenInfo inside the function, so that the tweenInfo would be same for all objects. Vinceberget 1420 — 3y
0
If anyone on the internet needs this, know that the part in MouseLeave/MouseEnter, the properties table will cause an error and should be deleted. xxkeithx 7 — 3y
Ad
Log in to vote
1
Answered by 3 years ago

Hello,

It appears your code works and is written pretty well. In terms of simplicity, your code is already very simple, nonetheless, I can attempt to cut down the number of lines and make your code more simplistic.

Firstly, I notice that for each box, we are tweening each to the same size every time, this means that we can create 1 variable containing the desired size and assign it to each tween:

local globalsize = UDim2.new(0.56, 0, 0.11, 0)

local goalSizePosition = {
    Size = globalsize,
    Position = UDim2.new(0.21, 0 ,0.261, 0)
}

local goalSizePosition2 = {
    Size = globalsize,
    Position = UDim2.new(0.21, 0,0.441, 0)
}

local goalSizePosition3 = {
    Size = globalsize,
    Position = UDim2.new(0.21, 0,0.641, 0)
}

This has simplified your code by making one size variable for each tween. This will allow you to change your code faster if you ever want to tweak this value.

Secondly, I noticed that all your tweens use the style Linear. Because you are using the Linear style, we can use different methods to achieve the same effect:

My preferred method of achieving this effect is by using the :Lerp() function. Lerp stands for Linear Interpolation, which means to take two different values, and find all the points between them (a line between the two points). Using this information, we can take a starting position and an ending position, and use all the points in between to smoothly animate a figure moving from a starting position to an ending position.

Lastly, we can use loops to automatically assign each button's functions.

All together, it looks like this:

local frame = script.Parent

local one = frame.one 
local two = frame.two
local three = frame.three

local Shop = script.Parent.Shop

local uis = {one,two,three} --keep all uis in table so we can loop through them
local startposes = {one.Position,two.Position,three.Position} --keep all positions in table so we can loop through them
local startrotation = 0 -- all have same starting rotation
local startsize = one.Size -- assuming all sizes are the same

local endposes = {UDim2.new(0.21, 0 ,0.261, 0),UDim2.new(0.21, 0,0.441, 0),UDim2.new(0.21, 0,0.641, 0)}
local endrotation = 20 --all use same end rotation
local endsize = UDim2.new(0.56, 0, 0.11, 0) -- all use same end size

local animtime = .3 --time for animations to take palce
local animframes = 100 --amount of frames in each animaton

for i, v in pairs(uis) do --loop through our buttons
    v.MouseEnter:Connect(function() --create function for button v
        for n = 1, animframes do
            v.Position:Lerp(endposes[i],n/animframes) --n/animframes% from start to end pos
            v.Size:Lerp(endsize,n/animframes)--n/animframes% from start to end size

            v.Rotation = startrotation + ((endrotation - startrotation) * (n/animframes)) --roblox's built in linear interpolation function does not work for single numbers, so I made my own version here
            wait(animtime/animframes)
        end
    end)

    v.MouseLeave:Connect(function()
        for n = 1, animframes do
            v.Position:Lerp(startposes[i],n/animframes) --n/animframes% from start to end pos 
            v.Size:Lerp(startsize,n/animframes) --n/animframes% from start to end size

            v.Rotation = endrotation - ((endrotation - startrotation) * (n/animframes))
            wait(animtime/animframes) --roblox's built in linear interpolation function does not work for single numbers, so I made my own version here
        end
    end)
end -- completed all functions for small buttons, continue with rest of code

local tweenInfoShop = TweenInfo.new(
    0.3, 
    Enum.EasingStyle.Quad, 
    Enum.EasingDirection.Out, 
    0, 
    false,
    0 
)
local shopNewPos = UDim2.new(-7, 0, -0.001, 0)

local shopOgPos = UDim2.new(1.2, 0, -0.001, 0)

local tweenEnterShop = TweenService:Create(Shop, tweenInfoShop, {Position = shopNewPos})

local tweenLeaveShop = TweenService:Create(Shop, tweenInfoShop, {Position = shopOgPos})

--Shop

local isShopOpen = false

one.MouseButton1Up:Connect(function()
    if isShopOpen == false then
        tweenEnterShop:Play()
    else 
        tweenLeaveShop:Play()
    end

    isShopOpen = not isShopOpen --micro-optimization, allows you to toggle from false to true easily, saves 1 line
end)

This code takes the number of lines from 138, down to just 70, and achieves the same effect.

Inside the code block, there are annotations describing every change. Below are resources if you'd like to learn more about the concepts used to give this answer:

What is linear interpolation

Roblox's linear interpolation function

Roblox loops

Roblox not function and variables

I hope my answer helped you!

Cheers,

Chase

Answer this question