expert
70 min

Advanced UI Systems

Build complex, animated UI systems with custom frameworks.

UI FrameworksState ManagementAnimation SystemsResponsive Design

1
UI Framework Architecture

Create a reusable UI component system.

Base UI Component

local UIComponent = {}
UIComponent.__index = UIComponent

function UIComponent.new(parent, properties)
  local self = setmetatable({}, UIComponent)
  self.instance = Instance.new("Frame")
  self.instance.Parent = parent
  self.children = {}
  self.state = {}
  
  -- Apply properties
  for property, value in pairs(properties or {}) do
    self.instance[property] = value
  end
  
  return self
end

function UIComponent:setState(newState)
  for key, value in pairs(newState) do
    self.state[key] = value
  end
  self:render()
end

function UIComponent:render()
  -- Override in child classes
end

function UIComponent:addChild(child)
  table.insert(self.children, child)
  return child
end

function UIComponent:destroy()
  for _, child in ipairs(self.children) do
    child:destroy()
  end
  self.instance:Destroy()
end

Base component class with state management and lifecycle methods.

Button Component

local Button = setmetatable({}, {__index = UIComponent})
Button.__index = Button

function Button.new(parent, properties)
  local self = setmetatable(UIComponent.new(parent, properties), Button)
  
  self.textLabel = Instance.new("TextLabel")
  self.textLabel.Size = UDim2.new(1, 0, 1, 0)
  self.textLabel.BackgroundTransparency = 1
  self.textLabel.TextColor3 = Color3.new(1, 1, 1)
  self.textLabel.Parent = self.instance
  
  -- Hover effects
  self.instance.MouseEnter:Connect(function()
    self:onHover()
  end)
  
  self.instance.MouseLeave:Connect(function()
    self:onLeave()
  end)
  
  self.instance.MouseButton1Click:Connect(function()
    if self.onClick then
      self:onClick()
    end
  end)
  
  return self
end

function Button:setText(text)
  self.textLabel.Text = text
end

function Button:onHover()
  self.instance.BackgroundColor3 = Color3.fromRGB(100, 100, 255)
end

function Button:onLeave()
  self.instance.BackgroundColor3 = Color3.fromRGB(70, 70, 200)
end

-- Usage
local myButton = Button.new(screenGui, {
  Size = UDim2.new(0, 200, 0, 50),
  Position = UDim2.new(0.5, -100, 0.5, -25),
  BackgroundColor3 = Color3.fromRGB(70, 70, 200)
})

myButton:setText("Click Me!")
myButton.onClick = function()
  print("Button clicked!")
end

Reusable button component with hover effects and click handling.

2
Advanced Animations

Create smooth, complex UI animations.

Animation Queue System

local TweenService = game:GetService("TweenService")

local AnimationQueue = {}
AnimationQueue.__index = AnimationQueue

function AnimationQueue.new()
  local self = setmetatable({}, AnimationQueue)
  self.queue = {}
  self.isPlaying = false
  return self
end

function AnimationQueue:add(guiObject, tweenInfo, goals)
  table.insert(self.queue, {
    object = guiObject,
    info = tweenInfo,
    goals = goals
  })
  return self  -- For chaining
end

function AnimationQueue:play(callback)
  if self.isPlaying then return end
  
  self.isPlaying = true
  local index = 1
  
  local function playNext()
    if index > #self.queue then
      self.isPlaying = false
      if callback then callback() end
      return
    end
    
    local anim = self.queue[index]
    local tween = TweenService:Create(anim.object, anim.info, anim.goals)
    
    tween.Completed:Connect(function()
      index = index + 1
      playNext()
    end)
    
    tween:Play()
  end
  
  playNext()
end

-- Usage
local queue = AnimationQueue.new()

queue:add(frame1, TweenInfo.new(0.5), {Position = UDim2.new(0.5, 0, 0.5, 0)})
queue:add(frame2, TweenInfo.new(0.3), {Size = UDim2.new(0, 200, 0, 200)})
queue:add(frame3, TweenInfo.new(0.4), {BackgroundTransparency = 0})

queue:play(function()
  print("All animations complete!")
end)

Queue system for sequential animations.

Notification System

local NotificationSystem = {}

function NotificationSystem.show(message, duration)
  local screenGui = game.Players.LocalPlayer.PlayerGui:FindFirstChild("Notifications")
  if not screenGui then
    screenGui = Instance.new("ScreenGui")
    screenGui.Name = "Notifications"
    screenGui.Parent = game.Players.LocalPlayer.PlayerGui
  end
  
  -- Create notification
  local notification = Instance.new("Frame")
  notification.Size = UDim2.new(0, 300, 0, 60)
  notification.Position = UDim2.new(1, 320, 0.9, 0)  -- Start off-screen
  notification.BackgroundColor3 = Color3.fromRGB(40, 40, 40)
  notification.BorderSizePixel = 0
  notification.Parent = screenGui
  
  -- Rounded corners
  local corner = Instance.new("UICorner")
  corner.CornerRadius = UDim.new(0, 8)
  corner.Parent = notification
  
  -- Message text
  local textLabel = Instance.new("TextLabel")
  textLabel.Size = UDim2.new(1, -20, 1, 0)
  textLabel.Position = UDim2.new(0, 10, 0, 0)
  textLabel.BackgroundTransparency = 1
  textLabel.Text = message
  textLabel.TextColor3 = Color3.new(1, 1, 1)
  textLabel.TextXAlignment = Enum.TextXAlignment.Left
  textLabel.Parent = notification
  
  -- Animate in
  local tweenIn = TweenService:Create(
    notification,
    TweenInfo.new(0.5, Enum.EasingStyle.Back, Enum.EasingDirection.Out),
    {Position = UDim2.new(1, -320, 0.9, 0)}
  )
  
  tweenIn:Play()
  
  -- Wait and animate out
  task.wait(duration or 3)
  
  local tweenOut = TweenService:Create(
    notification,
    TweenInfo.new(0.3, Enum.EasingStyle.Quad, Enum.EasingDirection.In),
    {Position = UDim2.new(1, 320, 0.9, 0)}
  )
  
  tweenOut.Completed:Connect(function()
    notification:Destroy()
  end)
  
  tweenOut:Play()
end

-- Usage
NotificationSystem.show("Welcome to the game!", 3)
NotificationSystem.show("Quest completed!", 2)

Animated notification system with slide-in/out effects.

💡 Tips:

  • • Use UICorner for modern rounded designs
  • • UIGradient adds visual polish
  • • Use ZIndex to layer UI elements properly
  • • Cache TweenInfo objects for performance

3
Responsive Design

Make UI adapt to different screen sizes.

Scale-Based Layout

-- Use UDim2.fromScale for responsive sizing
local function createResponsiveFrame(parent)
  local frame = Instance.new("Frame")
  frame.Size = UDim2.fromScale(0.8, 0.6)  -- 80% width, 60% height
  frame.Position = UDim2.fromScale(0.1, 0.2)  -- Centered with margin
  frame.Parent = parent
  
  -- Add UIAspectRatioConstraint for consistent proportions
  local aspect = Instance.new("UIAspectRatioConstraint")
  aspect.AspectRatio = 16/9
  aspect.Parent = frame
  
  return frame
end

-- Use UISizeConstraint for limits
local function addSizeConstraints(guiObject)
  local constraint = Instance.new("UISizeConstraint")
  constraint.MinSize = Vector2.new(200, 100)
  constraint.MaxSize = Vector2.new(800, 600)
  constraint.Parent = guiObject
end

Use Scale instead of Offset for responsive layouts.

Practice Exercises

Exercise 1: Health Bar Component

Create an animated health bar UI component.

Starter Code:

-- Create a health bar that animates when health changes
Show Solution
local HealthBar = setmetatable({}, {__index = UIComponent})
HealthBar.__index = HealthBar

function HealthBar.new(parent, maxHealth)
  local self = setmetatable(UIComponent.new(parent, {
    Size = UDim2.new(0, 200, 0, 30),
    BackgroundColor3 = Color3.fromRGB(50, 50, 50)
  }), HealthBar)
  
  self.fill = Instance.new("Frame")
  self.fill.Size = UDim2.new(1, 0, 1, 0)
  self.fill.BackgroundColor3 = Color3.fromRGB(0, 255, 0)
  self.fill.BorderSizePixel = 0
  self.fill.Parent = self.instance
  
  self.maxHealth = maxHealth
  self.currentHealth = maxHealth
  
  return self
end

function HealthBar:setHealth(health)
  self.currentHealth = math.clamp(health, 0, self.maxHealth)
  local percentage = self.currentHealth / self.maxHealth
  
  TweenService:Create(
    self.fill,
    TweenInfo.new(0.3),
    {Size = UDim2.new(percentage, 0, 1, 0)}
  ):Play()
  
  -- Change color based on health
  local color
  if percentage > 0.5 then
    color = Color3.fromRGB(0, 255, 0)
  elseif percentage > 0.25 then
    color = Color3.fromRGB(255, 255, 0)
  else
    color = Color3.fromRGB(255, 0, 0)
  end
  
  self.fill.BackgroundColor3 = color
end

Ready for more?

Continue your learning journey

Executors.Online - Your Roblox Developer Hub