Roblox Studio · Lesson 15

Screen Interface: Make Your Own Adventure HUD

Use complete LocalScript to create task cards, digital progress and progress bars, and then use Scale, Offset, AnchorPoint and size constraints to complete mobile phone adaptation.

10–15 years old 60 minutes ScreenGui / UDim2 Hands-on practice Testing and Saving

Course overview

Make a truly executable task HUD from a blank Baseplate: display task, crystal progress and completion status, and remain readable on mobile phones and computers.

This lesson does not allow you to copy the properties of the concept. You will create a StarterGui, and generate task panels with a complete LocalScript. Press Play and there will be an "Exploration on Energy Island" at the top left corner, target text, progress bar and 1/3.

After completion, you can also change the progress to 0, 2, and 3, and immediately see how the interface changes; then deliberately make a wrong version of the fixed width, and compare the problems solved by Scale, Offset, AnchorPoint, and size constraints.

01 · BEFORE

Before starting

Explorer, Properties, Output, and Script Editor will open. Create a new Baseplate and save it as Lesson15.

02 · MAKE

Your work

A responsive task HUD created with LocalScript that contains a title, task, progress bar, numeric progress, and completion prompt.

03 · CHECK

Complete flag

F5 displays 1 / 3 during testing; after changing the code to 3 / 3, the progress bar fills up and displays "Task Complete"; the middle panel of the mobile phone's vertical screen does not exceed the right border.

Official screenshot of ScreenGui in the game screen
Figure 15-1: Official ScreenGui effect reference: the interface covers the game screen and does not move with the parts in the scene.Roblox Creator Documentation · CC BY 4.0 · The original image has not been modified · Description of the permit

Learning route

Prepare first, then do it; after seeing the result, remember to save it.

Every time a small step is completed, it is tested. Say what you expect to see first, and then use Play and Output to check it. Don't rush to write all the code at once.

1

01·First clarify the results to be achieved today

After opening Lesson15 Baseplate, first open Explorer, Properties and Output in the View menu. There is only one acceptance goal today: after pressing F5, a task card will appear in the upper left corner of the player's screen; change the number on the last line from 1 to 3, and the interface will immediately change to the completed state.

2

02 · Create only two objects and put the script in the right place

ScreenGui and LocalScript are created manually first, and the rest of Frame, TextLabel and progress bar are all generated by code. In this way, you can understand the Explorer hierarchy without getting stuck due to missing a dozen properties.

3

03 · Paste the complete code and let the finished product run first

The following create() is a small factory function: create objects, set properties in batches, and finally specify Parent. The entire HUD is built from this LocalScript, so you can just copy and run it.

4

04 · Make four changes and understand the program with your own eyes

Don't continue copying new code now. Make only one change, run it once, and record the results. When engineers adjust the interface, they will push the input to the boundary value, because 0, just completed, and exceeding the total number are most likely to expose errors.

5

05 · Use a failed experiment to understand Scale, Offset and AnchorPoint

The four numbers of Panel.Size = UDim2.new(0.36, 0, 0, 176) are X Scale, X Offset, Y Scale, Y Offset. The width is taken as 36% of the parent container and the height is fixed at 176 pixels; UISizeConstraint then limits the final width to 280 to 420 pixels.

6

06 · Test computers and mobile phones as before going live

The normal desktop preview is just the first level. Device Simulator can simulate different screen sizes, orientations and touchscreen environments. During the test, we not only looked at "whether it is displayed", but also checked whether the text was cropped, whether the task card blocked the moving area, and whether the screen went beyond the screen after switching between horizontal and vertical screens.

Step by step

01·First clarify the results to be achieved today

After opening Lesson15 Baseplate, first open Explorer, Properties and Output in the View menu. There is only one acceptance goal today: after pressing F5, a task card will appear in the upper left corner of the player's screen; change the number on the last line from 1 to 3, and the interface will immediately change to the completed state.

Task cards belong to each player's own screen and are therefore placed in the StarterGui. Do not create a Part in the Workspace to pretend to be a HUD; the scene Part will be affected by the camera distance and angle, and the interface in ScreenGui is fixed on the screen.

  1. File → Save to File, save the project as Lesson15.rbxl; press Ctrl+S after each section is completed.
  2. View → Explorer, Properties, Output, ensure that all three windows are visible.
  3. Confirm that StarterGui is present in Explorer. No need to import models or images today.
Expected resultsThe first run displays "Goal: Find 3 Gems", a blue progress bar, and "1 / 3". After changing the last line of the code to renderQuest(3, 3), it displays "The task is completed, you can go to the exit".

Step by step

02 · Create only two objects and put the script in the right place

ScreenGui and LocalScript are created manually first, and the rest of Frame, TextLabel and progress bar are all generated by code. In this way, you can understand the Explorer hierarchy without getting stuck due to missing a dozen properties.

LocalScript must be in a location where the player client can run it. In this lesson, we put it into AdventureGui under StarterGui. When Play is pressed, Roblox copies the AdventureGui to the current player's PlayerGui and then runs the HudController.

  1. Move the mouse to StarterGui in Explorer, click + on the right, search for ScreenGui, insert it and press F2 to rename it to AdventureGui.
  2. Select the AdventureGui, unticking ResetOnSpawon in Properties; this does not recreate a full HUD when the role is reborn.
  3. Click + on the right side of AdventureGui again, search for LocalScript, and rename it to HudController. Note that the icon must be LocalScript, not an ordinary Script.
  4. Double-click HudController, delete the default content, and prepare to paste the complete code of the next section.
Explorer structure · not Luau codeExplorer
StarterGui
└─ AdventureGui (ScreenGui)
   └─ HudController (LocalScript)

运行 F5 后,代码会在 PlayerGui 的副本中加入:
AdventureGui
├─ HudController
└─ Panel (Frame)
   ├─ Title (TextLabel)
   ├─ Objective (TextLabel)
   ├─ ProgressTrack (Frame)
   │  └─ ProgressFill (Frame)
   ├─ Count (TextLabel)
   └─ Status (TextLabel)

Step by step

03 · Paste the complete code and let the finished product run first

The following create() is a small factory function: create objects, set properties in batches, and finally specify Parent. The entire HUD is built from this LocalScript, so you can just copy and run it.

The renderQuest(1, 3) at the end of the code means "1 has been found, and a total of 3 are needed." renderQuest() updates the task text, numbers, progress bar width and status color at the same time to avoid conflicting displays in the four places.

  1. Paste the complete code into the HudController and press Ctrl+S.
  2. Press F5 (Play), don't just press F8 (Run); this lesson requires creating a player client to run LocalScript.
  3. After seeing the HUD, expand Players → Your Name → PlayerGui → AdventureGui, and compare the running level of the previous section.
  4. Look at the Output: [HUD] 1/3 should appear with no red errors. Press Shift+F5 to stop the test.
StarterGui > AdventureGui > HudController(LocalScript)Luau
local gui = script.Parent

-- 方便重复测试:如果旧面板还在,先删除再重建。
local oldPanel = gui:FindFirstChild("Panel")
if oldPanel then
    oldPanel:Destroy()
end

local function create(className, properties, parent)
    local object = Instance.new(className)
    for property, value in pairs(properties) do
        object[property] = value
    end
    object.Parent = parent
    return object
end

local panel = create("Frame", {
    Name = "Panel",
    AnchorPoint = Vector2.new(0, 0),
    Position = UDim2.fromOffset(18, 18),
    Size = UDim2.new(0.36, 0, 0, 176),
    BackgroundColor3 = Color3.fromRGB(16, 24, 40),
    BackgroundTransparency = 0.08,
    BorderSizePixel = 0,
}, gui)

create("UICorner", {
    CornerRadius = UDim.new(0, 14),
}, panel)

create("UIStroke", {
    Color = Color3.fromRGB(93, 126, 180),
    Transparency = 0.35,
    Thickness = 1,
}, panel)

-- 0.36 让宽度跟屏幕变化;约束保证它不会小得难读或大得挡住画面。
local sizeConstraint = create("UISizeConstraint", {
    MinSize = Vector2.new(280, 176),
    MaxSize = Vector2.new(420, 176),
}, panel)

-- 把 false 改成 true,可以故意制造一次“小屏溢出”并观察原因。
local RUN_OVERFLOW_TEST = false
if RUN_OVERFLOW_TEST then
    sizeConstraint.MinSize = Vector2.new(0, 176)
    sizeConstraint.MaxSize = Vector2.new(2000, 176)
    panel.Size = UDim2.fromOffset(700, 176)
end

create("TextLabel", {
    Name = "Title",
    Position = UDim2.fromOffset(18, 12),
    Size = UDim2.new(1, -36, 0, 28),
    BackgroundTransparency = 1,
    Font = Enum.Font.GothamBold,
    Text = "能量岛探险",
    TextColor3 = Color3.fromRGB(255, 255, 255),
    TextSize = 22,
    TextXAlignment = Enum.TextXAlignment.Left,
}, panel)

local objective = create("TextLabel", {
    Name = "Objective",
    Position = UDim2.fromOffset(18, 44),
    Size = UDim2.new(1, -36, 0, 42),
    BackgroundTransparency = 1,
    Font = Enum.Font.Gotham,
    Text = "",
    TextColor3 = Color3.fromRGB(211, 220, 235),
    TextSize = 17,
    TextWrapped = true,
    TextXAlignment = Enum.TextXAlignment.Left,
    TextYAlignment = Enum.TextYAlignment.Top,
}, panel)

local track = create("Frame", {
    Name = "ProgressTrack",
    Position = UDim2.fromOffset(18, 100),
    Size = UDim2.new(1, -92, 0, 14),
    BackgroundColor3 = Color3.fromRGB(48, 61, 82),
    BorderSizePixel = 0,
    ClipsDescendants = true,
}, panel)
create("UICorner", {CornerRadius = UDim.new(1, 0)}, track)

local fill = create("Frame", {
    Name = "ProgressFill",
    Size = UDim2.fromScale(0, 1),
    BackgroundColor3 = Color3.fromRGB(72, 167, 255),
    BorderSizePixel = 0,
}, track)
create("UICorner", {CornerRadius = UDim.new(1, 0)}, fill)

local countLabel = create("TextLabel", {
    Name = "Count",
    AnchorPoint = Vector2.new(1, 0),
    Position = UDim2.new(1, -18, 0, 93),
    Size = UDim2.fromOffset(56, 28),
    BackgroundTransparency = 1,
    Font = Enum.Font.GothamBold,
    Text = "",
    TextColor3 = Color3.fromRGB(255, 255, 255),
    TextSize = 17,
    TextXAlignment = Enum.TextXAlignment.Right,
}, panel)

local statusLabel = create("TextLabel", {
    Name = "Status",
    Position = UDim2.fromOffset(18, 132),
    Size = UDim2.new(1, -36, 0, 26),
    BackgroundTransparency = 1,
    Font = Enum.Font.GothamMedium,
    Text = "",
    TextColor3 = Color3.fromRGB(150, 190, 230),
    TextSize = 15,
    TextXAlignment = Enum.TextXAlignment.Left,
}, panel)

local function renderQuest(current, total)
    total = math.max(total, 1)
    current = math.clamp(current, 0, total)
    local ratio = current / total

    objective.Text = string.format("目标:找到 %d 颗晶石", total)
    countLabel.Text = string.format("%d / %d", current, total)
    fill.Size = UDim2.fromScale(ratio, 1)

    if current == total then
        statusLabel.Text = "任务完成,可以前往出口"
        statusLabel.TextColor3 = Color3.fromRGB(109, 232, 160)
        fill.BackgroundColor3 = Color3.fromRGB(75, 220, 130)
    else
        statusLabel.Text = "继续探索,留意发光的平台"
        statusLabel.TextColor3 = Color3.fromRGB(150, 190, 230)
        fill.BackgroundColor3 = Color3.fromRGB(72, 167, 255)
    end

    print(string.format("[HUD] %d/%d", current, total))
end

renderQuest(1, 3)
Run first, then disassembleIf Output reports an error, double-clicking the red message will jump to the corresponding line of code. First check whether it is LocalScript and whether the code is complete before starting to change the value.

Step by step

04 · Make four changes and understand the program with your own eyes

Don't continue copying new code now. Make only one change, run it once, and record the results. When engineers adjust the interface, they will push the input to the boundary value, because 0, just completed, and exceeding the total number are most likely to expose errors.

math.clamp(current, 0, total) limits the abnormal progress to the legal range. Therefore renderQuest(9, 3) will not draw a progress bar that is three times wide, but will be processed as 3 / 3.

  1. Change the last line to renderQuest(0, 3): 0 / 3 should be displayed and the progress bar is empty.
  2. Change to renderQuest(2, 3): 2 / 3 should be displayed, and blue accounts for about two-thirds.
  3. Change to renderQuest(3, 3): The green bar is filled, and the status is changed to "Task completed, you can go to the exit."
  4. Change to renderQuest(9, 3): The interface still only displays 3 / 3. After confirmation, it returns to renderQuest(1, 3).
Four test values · Only one row is reserved for actual useLuau
-- 只改这一行,然后每次按 F5 检查:
renderQuest(0, 3) -- 空进度
renderQuest(2, 3) -- 三分之二
renderQuest(3, 3) -- 完成状态
renderQuest(9, 3) -- 越界输入会被 math.clamp 修正
04 · Make four changes and understand the program with your own eyes
EnterShould seeWhat does it verify?
0, 30/3, empty progress barStart Status
2, 32 / 3, blue about 67%Scale can represent proportion
3, 33/3, green completion promptComplete branch
9, 3Still 3 / 3Transboundary protection

Step by step

05 · Use a failed experiment to understand Scale, Offset and AnchorPoint

The four numbers of Panel.Size = UDim2.new(0.36, 0, 0, 176) are X Scale, X Offset, Y Scale, Y Offset. The width is taken as 36% of the parent container and the height is fixed at 176 pixels; UISizeConstraint then limits the final width to 280 to 420 pixels.

The progress bar best illustrates the use of Scale. In fill. Size = Udim2. FromScale (rateo, 1) the width of the parent 's chute is two-thirds when it is 2/3. It does not need to know how many pixels there are on a mobile phone. Count uses AncherPoint = (10), so Postion 's X coordinates are fixed to its right edge; when the panel is wide, the number remains on the right side.

  1. Change RUN_OVERFLOW_TEST in the complete code from false to true. Simulating using a mobile phone's portrait screen, a 700-pixel wide panel would extend beyond the right border.
  2. Returning RUN_OVERFLOW_TEST to false causes the panel to reuse the 36% width and 280–420 pixel constraints.
  3. Change renderQuest to 2, 3, and confirm that the ProgressFill width changes with the proportion, rather than hard-coding the pixels.
  4. Temporarily change the AnchorPoint of Count to Vector2.new(0, 0) and observe why the number runs outside the right boundary; restore Vector2.new(1, 0) after testing.
Overflow experiment . Modify the same name switch in the full programLuau
-- 完整程序中已有这个实验开关:
local RUN_OVERFLOW_TEST = true
if RUN_OVERFLOW_TEST then
    sizeConstraint.MinSize = Vector2.new(0, 176)
    sizeConstraint.MaxSize = Vector2.new(2000, 176)
    panel.Size = UDim2.fromOffset(700, 176)
end

-- 测完改回 false,再测试正常的响应式方案。
05 · Use a failed experiment to understand Scale, Offset and AnchorPoint
Writing methodMeaningThe location of this lesson
UDim2.fromOffset(18, 18)18 pixels from left and upperPanel.Position
UDim2.new(0.36, 0, 0, 176)Width 36%, Height 176 pixelsPanel.Size
UDim2.fromScale(ratio, 1)Width is proportional to progress, height is 100%ProgressFill.Size
AnchorPoint = Vector2.new(1, 0)Align the coordinates with the upper right corner of the objectCount
Screenshot of Scale and Offset properties of interface Size
Figure 15-2: Official Size attribute screenshot: Expand X and Y, find Scale and Offset respectively, and input the values ​​according to this lesson.Roblox Creator Documentation · CC BY 4.0 · The original image has not been modified · Description of the permit
Turn off the switch after testingRUN_OVERFLOW_TEST=true is an intentional fault. After seeing the phone overflow, change it back to false, and the final saved version will return to responsive size.

Step by step

06 · Test computers and mobile phones as before going live

The normal desktop preview is just the first level. Device Simulator can simulate different screen sizes, orientations and touchscreen environments. During the test, we not only looked at "whether it is displayed", but also checked whether the text was cropped, whether the task card blocked the moving area, and whether the screen went beyond the screen after switching between horizontal and vertical screens.

If you cannot see Device Simulator temporarily in your Studio, search for New Device Simulator in File → Beta Features, enable it and restart Studio as prompted. Roblox will update the Studio interface, so the button location may change. Finding by function name is more reliable than memorizing the icon location.

  1. Press F5 to start testing, then open Device Simulator; first select a Phone portrait preset.
  2. Receiving and inspection tablet: The left and upper edge of the panel is about 18 pixels, there are still gaps on the right and the title and target have not been removed.
  3. Switch to landscape: the panel will not be infinitely widened, and the final width will not exceed 420 pixels.
  4. Switch back to Desktop: the progress bar, 1 / 3 and status text are still in the same line sequence, without overlap.
  5. After Output has no red errors, Shift+F5 stops and Ctrl+S saves.
06 · Test computers and mobile phones as before going live
PhenomenonMost likely causeCheck now
No HUD at allScript type or location errorIt must be LocalScript under AdventureGui and tested with F5
Output reports syntax errorEnd, brackets or quotation marks are missing when copyingDouble-click the red error and check the corresponding line of the complete code
Beyond the right side on the mobile phoneStill using 700 pixel failed exampleRestore 0.36 width and UISizeConstraint
Numbers run outside the panelCount's AnchorPoint is changed to 0,0Restore Vector2.new(1, 0)
The progress bar is always empty.renderQuest is not called or current is 0Keep the last line renderQuest(1, 3)
Panel disappears after StopThis is the object created when the code runs.OK; HudController will be rebuilt on next F5

Homework

It's your turn to change and make your own version.

1 Change to your own tasks

Change the title to your own game name, replace the "spar" with keys, gold coins or robot parts, and change the total number to 5.

2 Make an acceptance comparison chart

Save a screenshot of Desktop and Phone portrait screens, and circle the panel border, progress bar and right side count.

3 Select: Add dangerous state

When current is 0, change the Status to "The crystal has not been found yet" and the color to orange; from 1 to total-1, the exploration prompt is still displayed.

Completion criteria:First verify the work in Playtest, then click Stop and save it as "Initials_Lesson15" or a copy of the corresponding Homework; do not overwrite the runnable version of the previous lesson.

Exit questions

Answer it yourself first, and then expand on it.

Why does HudController use LocalScript?

The HUD belongs to the current player's screen. LocalScript will run on the player client and operate on that player's PlayerGui.

What are 0.36 and 176 of UDim2.new(0.36, 0, 0, 176)?

0.36 is 36% of the parent container's width; 176 is a fixed height pixel offset.

Why does Count use AnchorPoint=(1,0)?

Position's coordinates are aligned with Count's upper right corner, so when the panel width changes, the count remains attached to the right.

Why does renderQuest(9, 3) not let the progress bar rush out of the panel?

math.clamp clamps current between 0 and total, with a final scale of at most 1.

Is it a bug that the Panel disappears during runtime after Stop?

No. It is created by LocalScript in the PlayerGui copy; it will be regenerated next time you F5. For long-term modification, the HudController in the StarterGui should be modified.

Explore further

This is where we start when we need to check the data.

The text code can be copied directly. First confirm the script type, placement, and whether this code is a complete version or a small experiment; do not run multiple versions of the same script superimposed. The source of the interface reference picture has been marked. The object names in the picture may be different from those in this lesson. Please refer to the operation steps.

Download the code and placement instructions for this lesson