按源仓库内容呈现,保留标题、案例、代码、表格、链接以及原文引用的演示图片。
Roblox UI
Foundations
- Put UI in
StarterGuiasScreenGuis (copied to each player'sPlayerGui), or create it from a
client controller. UI code is client-only; the server never touches PlayerGui in normal designs.
ScreenGuidefaults to set:ResetOnSpawn = false(keep UI across deaths),ZIndexBehavior = Sibling,
IgnoreGuiInset per design, ScreenInsets = CoreUISafeInsets for interactive UI (clears notches and the Roblox top bar); DeviceSafeInsets/None only for full-bleed backgrounds.
- Size and position with Scale (
UDim2.fromScale) plus constraints; use Offset only for fixed
details (padding, strokes, icons). Pure-Offset UI breaks on phones and 4K screens.
AnchorPoint = Vector2.new(0.5, 0.5)+Position = UDim2.fromScale(0.5, 0.5)centers an element.UIAspectRatioConstraintkeeps shapes square/consistent;UISizeConstraintcaps sizes;
UITextSizeConstraint with TextScaled bounds text size (prefer fixed TextSize + AutomaticSize where possible — TextScaled produces inconsistent sizes).
AutomaticSizelets frames grow with content (lists, tooltips, chat bubbles).- Test every screen in the Device Emulator (phone portrait/landscape, tablet, console 10-foot, 4K).
Layout
| Need | Use |
|---|---|
| Rows/columns that adapt | UIListLayout (FillDirection, Padding, HorizontalFlex/VerticalFlex, Wraps) |
| One item grows to fill space | UIFlexItem child with FlexMode = Fill (or Grow/Shrink/Custom) |
| Grids (inventory) | UIGridLayout inside a ScrollingFrame with AutomaticCanvasSize |
| Carousels/pages | UIPageLayout |
| Rounded corners, borders, padding | UICorner, UIStroke, UIPadding |
| Gradients, shadows | UIGradient, UIShadow |
| Scale a whole group | UIScale |
--!strict
local Players = game:GetService("Players")
local playerGui = Players.LocalPlayer:WaitForChild("PlayerGui")
local screenGui = Instance.new("ScreenGui")
screenGui.Name = "Hud"
screenGui.ResetOnSpawn = false
screenGui.ScreenInsets = Enum.ScreenInsets.CoreUISafeInsets
local bar = Instance.new("Frame")
bar.Name = "Toolbar"
bar.AnchorPoint = Vector2.new(0.5, 1)
bar.Position = UDim2.new(0.5, 0, 1, -16)
bar.Size = UDim2.fromScale(0.6, 0.1)
bar.BackgroundTransparency = 0.3
bar.Parent = screenGui
local list = Instance.new("UIListLayout")
list.FillDirection = Enum.FillDirection.Horizontal
list.HorizontalFlex = Enum.UIFlexAlignment.Fill -- slots share the width equally
list.Padding = UDim.new(0, 8)
list.Parent = bar
Instance.new("UICorner").Parent = bar
local padding = Instance.new("UIPadding")
padding.PaddingLeft, padding.PaddingRight = UDim.new(0, 8), UDim.new(0, 8)
padding.Parent = bar
for slot = 1, 5 do
local button = Instance.new("TextButton")
button.Name = `Slot{slot}`
button.Text = tostring(slot)
button.Size = UDim2.fromScale(0, 1)
button.Parent = bar
Instance.new("UIAspectRatioConstraint").Parent = button
end
screenGui.Parent = playerGui -- parent last: one layout pass, no flickerStyling: StyleSheets (CSS-like)
Roblox's engine-level styling replaces copy-pasted property values:
StyleSheetwithStyleRules (Selectorlike"TextButton",".Primary"(tag),"#Close"(name),
"TextButton:Hover" (state), "::UICorner" (modifier pseudo-instance)) and rule:SetProperties({...}).
- Tokens are attributes on a token sheet (
$Primary,$Radius); themes are sheets deriving tokens
(StyleDerive); swap themes by retargeting the StyleDerive.
- A
StyleLinkunder aScreenGuiapplies one sheet to that tree. Style queries (@Name) switch
styles by screen size, input type, or accessibility settings.
- Edit visually in Studio's Style Editor. Details and a code example: references/styling-and-frameworks.md.
Cross-platform input in UI
- Read
UserInputService.PreferredInput(KeyboardAndMouse,Touch,Gamepad) and listen to
UserInputService:GetPropertyChangedSignal("PreferredInput") to swap button prompts and layouts. Don't infer platform from TouchEnabled/KeyboardEnabled alone.
- Use
GuiButton.Activated(works for mouse, touch, and gamepad) instead ofMouseButton1Click. - Gamepad/console: every interactive element
Selectable, setGuiService.SelectedObjectwhen a menu
opens, group with SelectionGroup/NextSelection* for predictable D-pad navigation, support ButtonB to close. Minimum touch target ≈ 44×44 px; readable text ≥ 14 px on phones.
- Bind actions through the Input Action System (
InputBinding.UIButtonmakes an on-screen button
trigger an action) — see roblox-input.
Text
RichText = trueenables<b>,<i>,<font color="#FF0">,<stroke>, etc. Escape user text.- Any text from one player shown to another must be filtered with
TextService:FilterStringAsync
on the server (roblox-text-chat). That includes pet names, signs, guild names, trade notes.
- Use
FontFace = Font.new("rbxasset://fonts/families/GothamSSm.json", Enum.FontWeight.Bold)for weights. - Localize with
LocalizationTables and auto-translation; avoid baking text into images.
Animation and feel
- Tween on the client:
TweenService:Create(frame, TweenInfo.new(0.2, Enum.EasingStyle.Quad), { Position = ... }):Play(). - Animate
UIScale.Scalefor pop effects rather thanSize(keeps layout stable). CanvasGroupfades a whole subtree (GroupTransparency) but renders to a texture: use it for
fading panels, not for large always-visible UI (memory/quality cost).
- Drag-and-drop:
UIDragDetector(the deprecatedGuiObject.Draggableno longer works for new work).
World-space UI
BillboardGui(faces camera: names, health bars) — setMaxDistance,AlwaysOnTopsparingly,
use Size in Scale for distance scaling; SurfaceGui for screens/signs on parts. Prefer Adornee + keep them in PlayerGui for per-player content. They stop rendering when their part streams out.
ProximityPromptgives cross-platform interaction UI for free (validate on the server!).
Frameworks
For more than a few screens, a declarative framework keeps state and UI in sync:
- React (
jsdotlua/react+react-roblox) — components + hooks, familiar to web devs, scales to big apps. - Fusion — reactive state objects and declarative instance creation.
- Vide — small, fast reactive library.
Match whatever the project already uses. Keep game state in controllers; UI only renders it and emits user intents.
Performance
- Avoid updating text/properties every frame when values haven't changed.
- Hide closed menus with
Visible = false(orEnabled = falseon the ScreenGui) instead of
destroying/recreating them repeatedly; build large lists lazily or virtualize them.
- Too many
UIGradient/CanvasGroup/UIStrokeon dynamic UI costs frame time; check the MicroProfiler.
Related skills
roblox-input (actions, gamepad), roblox-text-chat (filtering), roblox-monetization (shops), roblox-luau, roblox-performance.

