Table of Contents

UI Animations

Intro

Creating cool UI animations can make your game feel more polished and fun. Scripts are a powerful tool to add dynamic effects to your game interface. In this guide, we’ll show you how to use Lua to bring your UI to life with smooth animations, from basic fades to slick slides. If you’re just starting out or looking to improve your game, we've got you covered. this guide will help you get the hang of scripting UI animations in Lua.

Guide

In this article, you will learn how to create and implement various types of UI animations using scripts. By the end of this guide, you will have knowledge of:

- Using the CustomUI Class: Learn how to utilize the CustomUI class to manipulate UI elements dynamically and create animations. - Learn about multithreading - Learn how to create the following animations with scripts. Opacity, linear movement, rotation, zoom, combined - Learn how to use linear interpolation

Linear interpolation basics

Linear interpolation is a method of estimating unknown values that lie within the range of two known values, the formula that we're going to use is for linear step interpolation.:

 Step = (TargetValue - StartingValue) / (Time / TimeBetweenChanges) 

Where:

And this is the formula that we're using to ensure that animations run smoothly.

1. Opacity Animation

This animation changes the transparency of a UI element over time, creating, for example, fade-in or fade-out effects. This can be useful for transitions, highlighting elements, or creating dynamic visual effects.

Effect Present

Explanation

We're going to explain how to create the opacity animation, the explanation can be used to create any other animation, as they follow the same principles.

First, we make a function that takes the playeruid (to update the uid to the player), the id of the ui (uiid), the element that we're going to update. The initial opacity, The final opacity, and the animation time.

We will increment the current opacity every 0.05 seconds, so using the linear step interpolation formula, we get that AlphaStep = (FinalOpacity-InitialOpacity)/(AnimationTime/0.05), this will be the value that we're going to increment the Current opacity by, after that, we create a variable that holds the current opacity and set it to be the initial opacity.

Then we calculate the number of “steps” that we're going to use in our for loop to ensure that the opacity reaches the target value, we do this with (AnimationTime/0.05), after that, we create a for loop with this step variable that we have, where we add the (CurOpacity + AlphaStep) and we use the Customui:setAlpha() to the CurOpacity. we then wait 0.05 seconds, so there aren't any problems in the execution of the code.

Script

function opacity_animation(playerid,uiid,element,InitialOpacity,FinalOpacity,AnimationTime) --Opacity Animation Function
    local AlphaStep = (FinalOpacity-InitialOpacity)/(AnimationTime/0.05) --Set the opacity change of each for loop
    local CurOpacity = InitialOpacity --Set the initial opacity
 
    local IterationNum = AnimationTime/0.05
 
    for i = 1, IterationNum do --Based on the time, set the loop value
        CurOpacity = CurOpacity + AlphaStep --Set the opacity current value
        Customui:setAlpha(playerid,CurUi.Id,CurUi.AnimElem,CurOpacity) --Modify component opacity
        threadpool:wait(0.05) --Wait for 0.05 seconds
    end
end
 
function ubc(e)
    local p = e.eventobjid
    local uuid = e.CustomUI
    local el = e.uielement
    --opacity_animation(playerid,UIid,element,starting value, ending value, animation duration)
    opacity_animation(p,uiid,el,100,0,0.5)--Fade out Animation
    opacity_animation(p,uiid,el,0,100,0.5)--Fade in Animation
end 
--Register a listener to run when the any button is clicked by any player
ScriptSupportEvent:registerEvent("UI.Button.Click",ubc)

2. Linear movement Animation

Effect Present

Script

Please read the comments through the script below

function linear_animation(playerid, uiid, element, StartX, StartY, TargetX, TargetY, AnimationTime)
    local StepX = (TargetX - StartX) / (AnimationTime / 0.05)  -- Calculate the value x will increase every iteration
    local StepY = (TargetY - StartY) / (AnimationTime / 0.05)  -- Calculate the value y will increase every iteration
    local CurX = StartX 
    local CurY = StartY
    local IterationNum = AnimationTime / 0.05  -- Calculate the number of iterations
 
    for i = 1, IterationNum do  
        CurX = CurX + StepX  -- Update the current x-coordinate
        CurY = CurY + StepY  -- Update the current y-coordinate
        Customui:setPosition(playerid, uiid, element, CurX, CurY)  -- Set the new position of the component
        threadpool:wait(0.05)  -- Wait for 0.05 seconds
    end
end
 
function ubc(e)
    local p = e.eventobjid
    local uuid = e.CustomUI
    local el = e.uielement
    --opacity_animation(playerid,UIid,element,starting value, ending value, animation duration)
    linear_animation(p,uiid,el,150,190,600,300,1)--Animate to new coodinate
    linear_animation(p,uiid,el,600,300,150,190,1)--Animate to original coodinate
end 
--Register a listener to run when the any button is clicked by any player
ScriptSupportEvent:registerEvent("UI.Button.Click",ubc)

Please noted that the starting point coodinate(x=150,y=190) should be same as the UI coodinate

3. Rotate Animation

Effect Present

Script

Please read the comments through the script below

function rotate_animation(playerid, uiid, element, StartAngle, TargetAngle, AnimationTime) --Rotate Animation
    local StepAngle = (TargetAngle - StartAngle) / (AnimationTime / 0.05)  -- Calculate the increment for angle rotation for every iteration
    local CurAngle = StartAngle  -- Initialize the current angle
    local IterationNum = AnimationTime / 0.05  -- Calculate the number of iterations
 
    for i = 1, IterationNum do  -- Loop through each iteration
        CurAngle = CurAngle + StepAngle  -- Update the current angle by adding the increment
        Customui:rotateElement(playerid, uiid, element, CurAngle)  -- Set the new rotation angle of the component
        threadpool:wait(0.05)  -- Wait for 0.05 seconds
    end
end
 
function ubc(e)
    local p = e.eventobjid
    local uuid = e.CustomUI
    local el = e.uielement
    --opacity_animation(playerid,UIid,element,starting value, ending value, animation duration)
    rotate_animation(p,uiid,el,0,360,1)
end 
--Register a listener to run when the any button is clicked by any player
ScriptSupportEvent:registerEvent("UI.Button.Click",ubc)

As you can see, the element will rotate 360 degrees in 1 second. But for better rotation we expect the element to be centered. How can we present it?
* Create a new parent element

We create a new parent element with the width and height zero, and put the orignal element as subset element.

Then, we have to replace the “el” variable to new parent element.

local el = [[7258119162206233205_3]]--Replace to your element id that want to animate

Here is the result:

4. Zoom Animation

Effect Present

Script

Please read the comments through the script below

function zoom_animation(playerid, uiid, elementid, StartWidth, StartHeight, TargetWidth, TargetHeight, AnimationTime)
    local StepWidth = (TargetWidth - StartWidth) / (AnimationTime / 0.05)
    local StepHeight = (TargetHeight - StartHeight) / (AnimationTime / 0.05)
 
    local CurWidth = StartWidth
    local CurHeight = StartHeight
 
    local IterationNum = AnimationTime / 0.05
 
    for i = 1, IterationNum do
        CurWidth = CurWidth + StepWidth
        CurHeight = CurHeight + StepHeight
        Customui:setSize(playerid, uiid, elementid, CurWidth, CurHeight)
        threadpool:wait(0.05)
    end
end
 
function ubc(e)
    local p = e.eventobjid
    local uiid = e.CustomUI 
    local el = e.uielement
 
    zoom_animation(p, uiid, el, 120, 126, 360, 376, 1)
    zoom_animation(p, uiid, el, 360, 376, 120, 126, 1)
end 
 
ScriptSupportEvent:registerEvent("UI.Button.Click", ubc)

5. Combined Animation

Before starting with this, we will need to explain something called “multithreading”, as it is important when playing more than 2 animations at any given time.

What is multithreading

The game is run in multiple of this so called “threads of execution”. Threads are like different workers in a team. Usually, the scripts run in a single thread, this thread is called the Main thread and when we do this, we are running it Concurrently. Imagine this, you want to build a house, and you only have 1 worker (thread). This worker would have to do everything secuentially, for example, build the walls, build the roof, add decorations, etc. Multithreading uses multiple workers or threads to achieve a task, so, if we have more workers (threads), One worker would build the wall, another the roof, another would decorate and so on, when we do this, we are running it in Parallel.

As the script by default run in only a single thread, we are executing it concurrently. When we try executing the two animations concurrently, They are executed in an interleaved manner on a single thread. Because this is concurrent execution, the tasks make progress in overlapping time periods, but they are not truly running simultaneously.

But if we execute them in different threads, we will explain later how to do this, the two animations are running on separate threads. This is parallel execution because the tasks are running simultaneously, or in parallel in different threads.

Here's an illustration on how this works:

How to achieve multithreading

We can achieve it using a method that the game provides. This method is threadpool:work() and it isn't just useful for animations, it is useful for many things, ranging from executing things in the background, to updating graphics and leaderboards in real time, to scheduling tasks, etc. The way this method works, is that we provide it with a function, and it executes it for us in a different thread.

Effect Present

Script1

local function ubc(e)
  local playerid = e.eventobjid -- Set the player
  local uiid = "7258119162206233205" -- Set the UI
  local el = "7258119162206233205_2" -- Set the element
  local o_locat = {x=-60,y=-63,w=120,h=126} -- Original location of the element
 
  -- Translation animation
  linear_animation(playerid, uiid, el, o_locat.x, o_locat.y, o_locat.x + 200, o_locat.y + 200, 1)
  -- Rotation animation
  rotate_animation(playerid, uiid, el, 0, 360, 1)
  -- Scaling animation
  zoom_animation(playerid, uiid, el, o_locat.x, o_locat.y, o_locat.x + 200, o_locat.y + 200, 1)
  -- Opacity animation
  opacity_animation(playerid, uiid, el, 100, 0, 1)
end
 
ScriptSupportEvent:registerEvent("UI.Button.Click", ubc)

This code snippet defines a function ubc that is registered as an event handler for the “UI.Button.Click” event. Within the function, it sets the player ID, UI ID, and element ID. Then, it applies four different animations to the element: translation, rotation, scaling, and opacity animations. Each animation has its own function (linear_animation, rotate_animation, zoom_animation, opacity_animation) and parameters are passed to these functions to define the specific animation behavior.

Combining multiple animations simultaneously is not considered a true combined animation. In order to play multiple animations at the same time, we need to utilize the multi-threading function threadpool:work().
Before using this function, it is important to understand the concept of threads and multi-threading.

Effect Present

Script2

threadpool:work(function()
    -- Translation animation in a new thread
    linear_animation(playerid, uiid, el, o_locat.x, o_locat.y, o_locat.x + 200, o_locat.y + 200, 1)
  end)
-- Opacity animation
opacity_animation(playerid, uiid, el, 0, 100, 1)
ScriptSupportEvent:registerEvent("UI.Button.Click", ubc)

This is what everything does: