Một HUD (Heads-Up Display) trong trải nghiệm của bạn thường hiển thị các phần tử UI không tương tác như đồng hồ sức khỏe được chứng minh trong Tạo đồng hồ HUD. Ngoài ra, gần như mọi trải nghiệm đều yêu cầu UI tương tác như các nút phản hồi khi người chơi kích hoạt, hoạt hình khi được kích hoạt, tween vào/ra các menu với các điều khiển tương tác khác, v.v.

Sử dụng UI Fundamentals - HUD Meter làm điểm khởi đầu và UI Fundamentals - Tương tác làm tài liệu tham khảo hoàn thiện, hướng dẫn này trình bày:
- Cách định vị một nút cài đặt dọc theo cạnh trên của màn hình.
- Thiết kế một menu cài đặt chứa các thanh trượt kéo thả tương tác.
- Sử dụng một ModuleScripts để hình thành các module "controller" cho việc điều khiển mở rộng các đối tượng UI với các khai báo trạng thái.
- Cách kết nối các nút với việc kích hoạt của người chơi để bật tắt menu cài đặt.
- Cách kết nối các thanh trượt UI kéo thả để điều chỉnh âm lượng của hiệu ứng âm thanh và môi trường nền một cách riêng biệt.
Bật Trình Giả Lập Thiết Bị
Như đã lưu ý trong Tạo đồng hồ HUD, điện thoại và máy tính bảng có ít không gian màn hình nhất, vì vậy điều quan trọng là các phần tử UI của bạn phù hợp với màn hình nhỏ hơn và chúng rõ ràng với người chơi. Nếu bạn chưa làm như vậy, hãy bật Trình Giả Lập Thiết Bị trong Studio:
Mở mẫu UI Fundamentals - HUD Meter trong Studio.
Từ menu Test của Studio, bật Trình Giả Lập Thiết Bị.
Từ thanh trực tiếp trên viewport chính, chọn một mô phỏng điện thoại như iPhone X hoặc Samsung Galaxy A51. Sau đó, đặt kích thước hiển thị thành Vừa với Cửa Sổ để sử dụng tối đa không gian trong Studio.

Tạo nút cài đặt
GuiButtons là các phần tử giao diện người dùng tương tác có chức năng tích hợp như sự kiện đa nền tảng Activated được kích hoạt khi nút được nhấn hoặc chạm vào. Lớp GuiButton mở rộng đến hai biến thể, TextButton và ImageButton, và hướng dẫn này sử dụng một ImageButton hình "gear" để bật hoặc tắt menu cài đặt.

Để xây dựng nút cài đặt:
Trong cửa sổ Explorer, tìm HUDContainer bên trong StarterGui.

Với nút mới được chọn, đặt các thuộc tính sau trong cửa sổ Properties:
- AnchorPoint = 0.5, 0.25 (giữa theo chiều ngang và phần tư trên theo chiều dọc)
- BackgroundTransparency = 1 (hoàn toàn trong suốt)
- Position = 0.5, 0, 0, 0 (định vị ở giữa trên)
- Size = 0.1, 0, 0.1, 0 (10% chiều cao màn hình khi tỉ lệ khía cạnh được giới hạn)
- Image = rbxassetid://104919049969988 (ID tài sản của ký hiệu "gear")
Chèn một UIAspectRatioConstraint vào nút với các thuộc tính mặc định (tỷ lệ 1:1 giữa chiều rộng và chiều cao).

Để giới hạn chiều cao pixel tối đa của nút, chèn một UISizeConstraint và đặt thuộc tính MaxSize của nó thành inf, 44.



Tạo menu cài đặt
Nút cài đặt mới sẽ được lập trình để bật và tắt một menu cài đặt, cung cấp cho người chơi quyền truy cập vào cài đặt hoặc thông tin khác theo yêu cầu. Trong hướng dẫn này, menu sẽ chứa các thanh trượt tương tác để điều chỉnh mức âm lượng của âm thanh nền và hiệu ứng âm thanh một cách độc lập.

Tạo khung cha
Như đã lưu ý trong Tạo đồng hồ HUD, một Frame hoạt động như một container cho các đối tượng UI khác. Toàn bộ menu cài đặt sẽ được xây dựng với một khung cha duy nhất, giúp đơn giản hóa việc quản lý như một đối tượng trạng thái phản ứng với đầu vào khác nhau tùy theo trạng thái hiện tại.
Chèn một Frame vào HUDContainer và đổi tên thành SettingsMenu.

Với SettingsMenu được chọn, đặt các thuộc tính sau:
- AnchorPoint = 0.5 (neo giữa)
- BackgroundColor3 = 30, 30, 60 (màu xanh hải quân)
- BackgroundTransparency = 0.25 (75% mờ đục)
- Position = 0.5, 0, 0.5, 0 (giữa màn hình)
- Size = 0.75, 0, 0.75, 0 (75% chiều rộng/chiều cao của khu vực bên trong màn hình)

Chèn một UIAspectRatioConstraint vào SettingsMenu và đặt thuộc tính AspectRatio của nó thành 2.5. (tỷ lệ 2.5:1 giữa chiều rộng và chiều cao).


Chèn một bộ điều chỉnh UICorner vào SettingsMenu và đặt thuộc tính CornerRadius của nó thành 0.1, 0.


Để giới hạn chiều rộng và chiều cao pixel của SettingsMenu, chèn một UISizeConstraint. Đặt thuộc tính MaxSize của nó là 800, inf và MinSize là 350, 0.



Xây dựng một thanh trượt
Để cho phép người chơi điều chỉnh mức âm lượng, menu cài đặt sẽ chứa hai widget thanh trượt kéo thả được cung cấp bởi UIDragDetector, một đối tượng tiện lợi giúp tương tác với các phần tử giao diện người dùng 2D.
Để tạo một container cha cho thanh trượt đầu tiên:
Chèn một Frame mới vào container SettingsMenu và đổi tên thành EffectsVolumeSlider.

Với khung EffectsVolumeSlider được chọn, đặt các thuộc tính sau:
AnchorPoint = 0.5 (neo giữa)
BackgroundTransparency = 1 (hoàn toàn trong suốt)
Position = 0.5, 0, 0.35, 0 (giữa theo chiều ngang và 35% từ trên cùng của container)
Size = 0.8, 0, 0.1, 0 (80% chiều rộng của container và 10% chiều cao của nó)

Chèn một UIListLayout mới vào EffectsVolumeSlider. Bộ điều chỉnh bố cục này là cách mạnh mẽ để tự động sắp xếp các GuiObjects anh em thành các hàng ngang hoặc cột dọc bên trong container cha của chúng, bao gồm khả năng áp dụng các khái niệm flex.

Đặt các thuộc tính sau cho UIListLayout mới:
- Padding = 0.06, 0 (không gian giữa các phần tử danh sách là 6%)
- FillDirection = Horizontal (sắp xếp các phần tử từ trái sang phải)
- HorizontalFlex = Fill (điều chỉnh lại kích thước các phần tử theo chiều ngang để lấp đầy toàn bộ container cha, ghi đè kích thước đã định nghĩa của chúng)
- VerticalAlignment = Center (căn giữa các phần tử theo chiều dọc)
Biểu tượng của thanh trượt
Một biểu tượng đơn giản bao gồm một nốt nhạc âm thanh và ký hiệu "bùng nổ" giúp người chơi nhận biết mục đích của thanh trượt khi họ mở menu cài đặt.

Chèn một ImageLabel vào khung EffectsVolumeSlider và đổi tên thành Icon. Biểu tượng này sẽ giúp người chơi hiểu cái gì trong trải nghiệm mà thanh trượt điều khiển.

Với Icon được chọn, đặt các thuộc tính sau:
- BackgroundTransparency = 1 (hoàn toàn trong suốt)
- Size = 2.5, 0, 2.5, 0 (250% chiều cao của khung cha khi tỉ lệ khía cạnh được giới hạn)
- Image = rbxassetid://90019827067389 (ID tài sản của nốt nhạc và ký hiệu "bùng nổ")
Chèn một UIAspectRatioConstraint vào Icon và để các thuộc tính của nó là mặc định (tỷ lệ 1:1 giữa chiều rộng và chiều cao).


Khung phạm vi
Ngay bên phải của biểu tượng, phần tương tác của thanh trượt nên được chứa trong một Frame khác.

Chèn một Frame mới vào khung EffectsVolumeSlider và đổi tên thành SliderFrame. Lưu ý rằng nó phải là một anh em trực tiếp của bộ điều chỉnh bố cục UIListLayout.

Với SliderFrame được chọn, đặt các thuộc tính sau:
- BackgroundColor3 = 0 (đen)
- BackgroundTransparency = 0.75 (25% mờ đục)
- LayoutOrder = 1
- Size = 1, 0, 1, 0 (100% chiều rộng/chiều cao của khung cha)
Chèn các bộ điều chỉnh sau vào SliderFrame:
- UIStroke với thuộc tính Color được thiết lập thành 0, Thickness là 3, và Transparency là 0.25 (75% mờ đục đen viền)

Tay cầm tương tác
Với khung thanh trượt đã được xây dựng, bạn có thể tạo một tay cầm kéo thả cho người chơi tương tác trong quá trình chơi trò chơi.

Chèn một Frame mới vào container SliderFrame và đổi tên thành Handle.

Với Handle được chọn, đặt các thuộc tính sau:
- AnchorPoint = 0.5 (neo giữa)
- Position = 0.5, 0, 0.5, 0 (giữa theo chiều ngang và chiều dọc của khung cha)
- Size = 1.2, 0, 1.2, 0 (120% chiều cao của khung cha khi tỷ lệ khía cạnh được giới hạn)
- ZIndex = 3 (lớp tay cầm hiển thị phía trước các phần tử thanh trượt khác)
Chèn các bộ điều chỉnh sau vào Handle:
- UIAspectRatioConstraint với các thuộc tính mặc định (tỷ lệ 1:1 giữa chiều rộng và chiều cao)
Chèn một đối tượng UIDragDetector vào Handle. Đối tượng tiện lợi này khuyến khích tương tác với các phần tử giao diện người dùng 2D.

Đặt các thuộc tính sau cho UIDragDetector mới:
- ResponseStyle = Scale (di chuyển theo giá trị tỉ lệ của vị trí cha của detector)
Để đảm bảo phạm vi kéo thả của tay cầm được giới hạn trong container của nó, liên kết thuộc tính BoundingUI với container SliderFrame:
- Chọn UIDragDetector.
- Nhấn thuộc tính BoundingUI của nó trong cửa sổ Properties.
- Quay lại cửa sổ Explorer, nhấp vào cha SliderFrame của tay cầm.

Thuộc tính BoundingUI liên kết hiện phản ánh container SliderFrame:

Nếu bạn kiểm tra trải nghiệm bây giờ, bạn sẽ có thể kéo tay cầm sang trái và phải trong container cha của nó:
Lấp đầy bên trong
Để cho thấy rõ hơn rằng thanh trượt điều khiển một phạm vi từ 0% đến 100%, bạn có thể thêm một lấp đầy bên trong vào phía bên trái của container sẽ đồng bộ với vị trí biến đổi của tay cầm.

Chèn một Frame mới vào container SliderFrame và đổi tên thành InnerFill.

Đặt các thuộc tính sau cho khung InnerFill:
- AnchorPoint = 0, 0.5 (điểm đầu bên trái và giữa theo chiều dọc)
- BackgroundColor3 = [0, 150, 255] (màu xanh khớp với tay cầm của thanh trượt)
- BackgroundTransparency = 0.35 (65% mờ đục)
- Position = 0, 0, 0.5, 0
- Size = 0.5, 0, 1, 0 (50% chiều rộng và 100% chiều cao của khung cha)
- ZIndex = 2 (lớp hiển thị ở phía trước phần lấp đầy/viền của khung cha)
Để khớp với hình "viên thuốc" của container cha SliderFrame, chèn một bộ điều chỉnh UICorner và đặt thuộc tính CornerRadius của nó thành 0.5, 0.



Nhân bản thanh trượt
Với thanh trượt đầu tiên đã được xây dựng, bạn có thể dễ dàng nhân bản nó và thay đổi một số khía cạnh hình ảnh để chỉ ra một mục đích khác, trong trường hợp này là mức âm lượng của âm thanh nền được biểu thị bằng hình ảnh ký hiệu nốt nhạc.

Chọn đối tượng EffectsVolumeSlider đã hoàn thành.

Nhân bản nó (CtrlD hoặc ⌘D) và sau đó đổi tên bản sao thành BackgroundVolumeSlider.

Thay đổi Position của bản sao thành 0.5, 0, 0.7, 0 để di chuyển nó bên dưới thanh trượt đầu tiên.

Mở rộng nhánh trên cùng của BackgroundVolumeSlider, chọn nhãn hình ảnh Icon, và thay đổi thuộc tính Image của nó thành rbxassetid://101125859760167 (ID tài sản của hình ảnh ký hiệu với nốt nhạc).



Chọn khung InnerFill bên trong SliderFrame và thay đổi thuộc tính BackgroundColor3 của nó thành màu tím giống nhau ([255, 0, 125]).



Tạo nút đóng
Phần tử cuối cùng của menu cài đặt là một nút đóng cung cấp cho người chơi một đầu vào bổ sung để đóng menu (nút SettingsButton ở giữa trên sẽ phục vụ cùng một mục đích).

Với CloseButton được chọn, đặt các thuộc tính sau:
- AnchorPoint = 1, 0 (góc trên bên phải)
- BackgroundTransparency = 1 (hoàn toàn trong suốt)
- Position = 1, -10, 0, 10 (10 pixel vào từ góc trên bên phải)
- Size = 0.15, 0, 0.15, 0 (15% chiều cao của khung khi tỉ lệ khía cạnh được giới hạn)
- Image = rbxassetid://5577404210 (ID tài sản của ký hiệu nút đóng)
- ImageTransparency = 0.25 (75% mờ đục)
Chèn một UIAspectRatioConstraint vào nút với các thuộc tính mặc định (tỷ lệ 1:1 giữa chiều rộng và chiều cao).


Tạo các module điều khiển
Một bộ module điều khiển có thể mở rộng giúp quản lý UI tương tác dễ dàng hơn so với các script riêng lẻ được đặt bên trong mỗi đối tượng. ModuleScripts tạo điều kiện cho chức năng có thể mở rộng này bằng cách cho phép bạn tái sử dụng mã giữa các script ở các phía khác nhau của biên client-server hoặc cùng một phía của biên giới.
Điều khiển đối tượng có trạng thái
Module điều khiển đối tượng có trạng thái sau đây cho phép bạn gán hành vi cho các đối tượng UI như SettingsButton và SettingsMenu, và dễ dàng bật/tween giữa các trạng thái khác nhau. Để tạo module:
Chèn một ModuleScript vào container ReplicatedStorage và đổi tên thành StatefulObjectController.

Dán đoạn mã sau vào trong module:
StatefulObjectControllerlocal TweenService = game:GetService("TweenService")local StatefulObjectController = {}StatefulObjectController.__index = StatefulObjectControllerexport type StateName = stringexport type State = {transition: TweenInfo,properties: { [string]: any },}function StatefulObjectController.hydrate(props: {object: Instance,states: { [StateName]: State },initialStateName: StateName})local object, states, initialStateName = props.object, props.states, props.initialStateNamelocal self = setmetatable({states = states,currentStateName = initialStateName,tweens = {},}, StatefulObjectController)-- Tạo các tween để tái sử dụng nhằm tránh việc tạo mới các tween mỗi khi trạng thái thay đổifor stateName, state in states doself.tweens[stateName] = TweenService:Create(object, state.transition, state.properties)endself:setState(self.currentStateName)return selfendfunction StatefulObjectController:setState(stateName: StateName)local stateTween: Tween = self.tweens[stateName]if not stateTween thenwarn(string.format("Đã cố gắng đặt %s thành trạng thái không xác định '%s'", self.object:GetFullName(), stateName))returnendself.currentStateName = stateName-- Đảm bảo rằng không có tween khác đang xung độtfor _, tween in self.tweens dotween:Cancel()endstateTween:Play()endreturn StatefulObjectControllerGiải thích mãDòng Mục đích 6‑10 Xác định và xuất khẩu các kiểu Luau để cải thiện autocomplete và linting khi sử dụng module này. 12‑33 Hàm được sử dụng để khởi tạo và gán hành vi cho một đối tượng UI có trạng thái, chấp nhận một GuiObject làm object, một bảng các trạng thái và dữ liệu TweenInfo tương ứng của chúng, và một tên trạng thái ban đầu. Bên trong hàm này, các dòng 26‑28 xây dựng Tweens cho mỗi trạng thái, và dòng 30 gọi hàm setState() của module để đặt đối tượng vào trạng thái ban đầu. 35‑50 Hàm được sử dụng để thiết lập/thay đổi trạng thái của một đối tượng có trạng thái. Trong dòng 42, currentStateName của đối tượng được đặt thành trạng thái đang được thiết lập, các dòng 45‑47 hủy bỏ bất kỳ tween nào đang chạy để ngăn xung đột, và dòng 49 thực thi tween của trạng thái.
Điều khiển thanh trượt
Một module bổ sung khởi tạo và điều khiển hai thanh trượt âm lượng. Nó cũng cho phép bạn kết nối một hàm callback với mỗi thanh trượt để phát hiện sự tương tác của người chơi với thanh trượt và áp dụng các thay đổi mong muốn trong trải nghiệm.
Chèn một ModuleScript vào container ReplicatedStorage và đổi tên thành SliderController.

Dán đoạn mã sau vào trong module:
SliderControllerlocal SliderController = {}SliderController.__index = SliderControllerexport type Value = numberexport type OnChanged = (Value) -> ()function SliderController.hydrate(props: {object: Instance,onChanged: OnChanged,initialValue: Value?})local object, onChanged, initialValue = props.object, props.onChanged, props.initialValuelocal handle = object:FindFirstChild("Handle", true)if not handle thenwarn(string.format("Đã cố gắng khôi phục thanh trượt %s nhưng không tìm thấy Handle", object:GetFullName()))endlocal innerFill = object:FindFirstChild("InnerFill", true)if not innerFill thenwarn(string.format("Đã cố gắng khôi phục thanh trượt %s nhưng không tìm thấy InnerFill", object:GetFullName()))endlocal dragDetector = handle:FindFirstChildWhichIsA("UIDragDetector")if not dragDetector thenwarn(string.format("Đã cố gắng khôi phục thanh trượt %s nhưng không tìm thấy UIDragDetector", object:GetFullName()))endlocal self = setmetatable({handle = handle,innerFill = innerFill,dragDetector = dragDetector,value = initialValue or 0.5,onChanged = onChanged,}, SliderController)-- Đặt giá trị ban đầuself:setValue(self.value)-- Kết nối detector với sự thao tác của người chơiself.dragConnection = dragDetector.DragContinue:Connect(function()self:setValue(handle.Position.X.Scale)end)return selfendfunction SliderController:setValue(value: Value)local clampedValue = math.clamp(value, 0, 1)self.value = clampedValue-- Cập nhật vị trí tay cầm và kích thước khung bên trong để khớpself.handle.Position = UDim2.fromScale(clampedValue, 0.5)self.innerFill.Size = UDim2.fromScale(clampedValue, 1)-- Chạy hàm callback của người dùng với giá trị mới nhấtlocal changeSuccess, changeResult = pcall(self.onChanged, clampedValue)if not changeSuccess thenwarn("Lỗi trong hàm callback thanh trượt:", changeResult)endendreturn SliderControllerGiải thích mãDòng Mục đích 7‑46 Hàm được sử dụng để khởi tạo và gán hành vi cho một phần tử thanh trượt, chấp nhận Frame của thanh trượt làm object, một hàm callback onChanged để xử lý các thay đổi của thanh trượt, và một giá trị ban đầu cho vị trí tay cầm của thanh trượt. Bên trong hàm, các dòng 14‑27 xác nhận rằng các phần tử tay cầm, lấp đầy bên trong, và các phần tử UIDragDetector tồn tại. Dòng 38 gọi hàm setValue() của module và các dòng 41‑43 kết nối sự kiện DragContinue của detector với hàm setValue() của module (xem dòng tiếp theo để biết chi tiết). 48‑61 Hàm được sử dụng để thiết lập giá trị của thanh trượt. Bên trong hàm, các dòng 53‑54 đảm bảo vị trí tay cầm và kích thước khung lấp đầy bên trong được đồng bộ, và dòng 57 chuyển giá trị đã thay đổi đến hàm callback để bạn có thể sử dụng giá trị khi cần thiết.
Tạo script cài đặt
Với nút cài đặt và menu cài đặt đã hoàn thiện, bạn có thể kết nối mọi thứ với nhau bằng một script duy nhất sử dụng các module điều khiển.
Chèn một LocalScript mới vào HUDContainer và đổi tên thành SettingsScript để mô tả mục đích của nó. Lưu ý rằng script này nên ở cùng cấp (cùng nhánh) với SettingsMenu và SettingsButton, các đối tượng UI cấp cao mà nó sẽ quản lý.

Dán đoạn mã sau vào trong script:
SettingsScriptlocal ReplicatedStorage = game:GetService("ReplicatedStorage")local SoundService = game:GetService("SoundService")local SliderController = require(ReplicatedStorage.SliderController)local StatefulObjectController = require(ReplicatedStorage.StatefulObjectController)local HUDContainer = script.Parent-- Khởi tạo nút cài đặtlocal settingsButton = StatefulObjectController.hydrate({object = HUDContainer:FindFirstChild("SettingsButton"),states = {menuOpen = {transition = TweenInfo.new(0.5, Enum.EasingStyle.Exponential, Enum.EasingDirection.Out),properties = {Rotation = 45,},},menuClosed = {transition = TweenInfo.new(0.5, Enum.EasingStyle.Exponential, Enum.EasingDirection.Out),properties = {Rotation = 0,},},},initialStateName = "menuClosed"})-- Khởi tạo khung menu cài đặtlocal settingsMenu = StatefulObjectController.hydrate({object = HUDContainer:FindFirstChild("SettingsMenu"),states = {menuOpen = {transition = TweenInfo.new(0.5, Enum.EasingStyle.Bounce, Enum.EasingDirection.Out),properties = {Position = UDim2.fromScale(0.5, 0.5),Visible = true,},},menuClosed = {transition = TweenInfo.new(0),properties = {Position = UDim2.fromScale(0.5, 0.4),Visible = false,},},},initialStateName = "menuClosed"})-- Khởi tạo thanh trượt âm lượng hiệu ứnglocal effectsAudio = SoundService:FindFirstChild("Effects")local effectsVolumeSlider = SliderController.hydrate({object = HUDContainer:FindFirstChild("EffectsVolumeSlider", true),initialValue = effectsAudio and effectsAudio.Volume or 0.5,onChanged = function(value: SliderController.Value)if effectsAudio theneffectsAudio.Volume = valueendend,})-- Khởi tạo thanh trượt âm lượng nềnlocal backgroundAudio = SoundService:FindFirstChild("Background")local backgroundVolumeSlider = SliderController.hydrate({object = HUDContainer:FindFirstChild("BackgroundVolumeSlider", true),initialValue = backgroundAudio and backgroundAudio.Volume or 0.5,onChanged = function(value: SliderController.Value)if backgroundAudio thenbackgroundAudio.Volume = valueendend,})-- Kết nối các nút với sự tương tác của người chơiHUDContainer:FindFirstChild("SettingsButton").Activated:Connect(function()local targetState = if settingsButton.currentStateName == "menuClosed"then "menuOpen"else "menuClosed"settingsButton:setState(targetState)settingsMenu:setState(targetState)end)HUDContainer:FindFirstChild("CloseButton", true).Activated:Connect(function()settingsButton:setState("menuClosed")settingsMenu:setState("menuClosed")end)Tham khảo các phần sau để tìm hiểu cách script này sử dụng các module điều khiển.
Nút cài đặtCác dòng 10-27 khởi tạo và gán hành vi cho nút cài đặt như sau:
Dòng Mục đích 10‑11 Một tham chiếu cục bộ settingsButton được khai báo và khôi phục bằng hàm StatefulObjectController.hydrate(), và nút SettingsButton bên trong HUDContainer được tham chiếu là đối tượng để khôi phục. 12‑25 Một bảng states được truyền đi với hai trạng thái duy nhất, menuOpen và menuClosed. Mỗi trạng thái chứa một chuyển tiếp để khai báo (thông qua một TweenInfo) cách mà trạng thái cụ thể sẽ đạt được các thuộc tính mục tiêu được xác định trong bảng properties. Trong trường hợp này, menuOpen khai báo một sự thay đổi trạng thái đến 45° cho Rotation của nút với một tween Exponential trong nửa giây, trong khi menuClosed khôi phục nút về xoay mặc định của nó là 0. 26 Trạng thái ban đầu của nút được đặt thành menuClosed (điều này có thể được đặt thành bất kỳ tên trạng thái nào trong bảng states). Phía dưới trong script (các dòng 76-86), các nút được kết nối với sự tương tác của người chơi để kích hoạt các thay đổi trạng thái UI:
Dòng Mục đích 76‑82 SettingsButton được kết nối với sự kiện Activated của lớp GuiButton để gọi một hàm ẩn danh. Bên trong hàm ẩn danh, các dòng 77‑79 chuyển đổi biến targetState giữa menuOpen và menuClosed. Dòng 80 sau đó gọi setState() để đặt trạng thái của nút thành targetState và dòng 81 thực hiện điều tương tự cho SettingsMenu để nó bật hoặc tắt với việc kích hoạt SettingsButton. 83‑86 CloseButton được kết nối với sự kiện Activated để gọi một hàm ẩn danh, mà thiết lập trạng thái của cả SettingsButton và SettingsMenu thành menuClosed. Menu cài đặtCác dòng 30-49 khởi tạo và gán hành vi cho menu cài đặt như sau:
Dòng Mục đích 30‑31 Một tham chiếu cục bộ settingsMenu được khai báo và khôi phục bằng hàm StatefulObjectController.hydrate() và khung SettingsMenu bên trong HUDContainer được tham chiếu là đối tượng để khôi phục. 32‑46 Tương tự như nút cài đặt, một bảng states được truyền đi với hai trạng thái duy nhất, menuOpen và menuClosed. Ở đây, menuOpen khai báo một sự thay đổi trạng thái đến UDim2.fromScale(0.5, 0.5) cho Position của khung với một tween Bounce trong nửa giây, cũng như một trạng thái Visible của true. Ngược lại, menuClosed khai báo một thay đổi trạng thái vị trí đến UDim2.fromScale(0.5, 0.4) và Visible thành false, nhưng TweenInfo với thời gian 0 trong dòng 41 thực tế làm cho sự thay đổi trạng thái xảy ra ngay lập tức. 48 Trạng thái ban đầu của khung được đặt thành menuClosed. Phía dưới trong script (các dòng 76-86), menu cài đặt được liên kết với sự tương tác của người chơi:
Dòng Mục đích 81 Thiết lập trạng thái targetState cho SettingsMenu đồng bộ với việc kích hoạt SettingsButton. 85 Đặt trạng thái của SettingsMenu thành menuClosed khi CloseButton được kích hoạt. Thanh trượt âm lượngCác dòng 51-73 khởi tạo và gán hành vi cho EffectsVolumeSlider và BackgroundVolumeSlider như sau:
Dòng Mục đích 52 Thiết lập một tham chiếu cục bộ đến SoundGroup Effects bên trong SoundService. Một SoundGroup cho phép bạn liên kết nhiều Sounds và điều khiển âm lượng của mọi âm thanh trong nhóm thông qua thuộc tính Volume của nhóm. 53‑54 Một tham chiếu cục bộ effectsVolumeSlider được khai báo và khôi phục bằng hàm SliderController.hydrate() và khung EffectsVolumeSlider bên trong HUDContainer ⟩ SettingsMenu được tham chiếu là đối tượng để khôi phục. Lưu ý rằng tham số thứ hai của true cho FindFirstChild() cho biết phương thức tìm kiếm đệ quy xuống nhánh HUDContainer để tìm EffectsVolumeSlider. 55 Giá trị ban đầu của thanh trượt được đặt thành âm lượng hiện tại của nhóm âm thanh Effects, hoặc 0.5 làm giá trị dự phòng. 56‑60 Xác lập hàm callback để áp dụng các thay đổi giá trị thanh trượt cho âm lượng của nhóm âm thanh Effects. 64‑73 Về cơ bản giống như các dòng 52-61, nhưng là để khôi phục khung BackgroundVolumeSlider bên trong HUDContainer ⟩ SettingsMenu và áp dụng các thay đổi giá trị thanh trượt cho âm lượng của nhóm âm thanh Background.
Với SettingsScript đã được đặt, trải nghiệm bây giờ cung cấp một ví dụ UI tương tác hoàn toàn hoạt động liên kết một tập hợp các đối tượng UI có liên quan với sự tương tác của người chơi.






