Một HUD (Heads-Up Display) trong trò chơi của bạn thường hiển thị các yếu tố UI không tương tác như đồng hồ sức khỏe được trình bày trong Tạo đồng hồ HUD. Ngoài ra, hầu hết mọi trò chơi đề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 Cơ bản về UI - Đồng hồ HUD làm điểm khởi đầu và Cơ bản về UI - Tương tác làm tài liệu tham khảo hoàn chỉnh, 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 tương tác.
- Sử dụng một ModuleScripts để tạo ra 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 có 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 để điều chỉnh âm lượng của hiệu ứng âm thanh và âm thanh nền một cách riêng biệt.
Kích hoạt Trình giả lập Thiết bị
Như đã đề cập 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 yếu tố UI của bạn phải vừa vặn trên các màn hình nhỏ hơn và rõ ràng với người chơi. Nếu bạn chưa làm như vậy, hãy kích hoạt Trình giả lập Thiết bị trong Studio:
Mở mẫu Cơ bản về UI - Đồng hồ HUD trong Studio.
Từ menu Test của Studio, bật Trình giả lập Thiết bị.
Từ thanh ngay 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 xem 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 yếu tố giao diện người dùng tương tác với chức năng tích hợp sẵn như sự kiện đa nền tảng Activated được kích hoạt khi nút được nhấp hoặc chạm. 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 "bánh răng" để 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ệ khung hình được ràng buộc)
- Image = rbxassetid://104919049969988 (ID tài sản của biểu tượng "bánh răng")
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 menu cài đặt
Nút cài đặt mới sẽ được lập trình để bật và tắt menu cài đặt, cung cấp cho người chơi quyền truy cập theo yêu cầu vào các cài đặt hoặc thông tin khác. 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ư đã đề cập trong Tạo đồng hồ HUD, một Frame phục vụ 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 dễ dàng quản lý như một đối tượng có trạng thái phản ứng với đầu vào khác nhau tùy thuộc vào 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ờ)
- 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/cao của khu vực màn hình bên trong)

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.


Để ràng buộc chiều rộng và chiều cao pixel của SettingsMenu, chèn một UISizeConstraint. Đặt MaxSize của nó thành 800, inf và MinSize của nó thành 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 có thể kéo được điều khiển bởi UIDragDetector, một đối tượng tiện lợi giúp tương tác với các yếu 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à một 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 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 (6% khoảng cách giữa các yếu tố trong danh sách)
- FillDirection = Horizontal (sắp xếp các yếu tố từ trái sang phải)
- HorizontalFlex = Fill (thay đổi kích thước các yếu 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 chỉnh các yếu tố theo chiều dọc ở giữa)
Biểu tượng thanh trượt
Một biểu tượng đơn giản bao gồm một nốt âm thanh và biểu tượng "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 khía cạnh nào của trò chơi 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ệ khung hình được ràng buộc)
- Image = rbxassetid://90019827067389 (ID tài sản của nốt âm thanh và biểu tượng "bùng nổ")
Chèn một UIAspectRatioConstraint vào Icon và để các thuộc tính của nó ở 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 UIListLayout.

Với SliderFrame được chọn, đặt các thuộc tính sau:
- BackgroundColor3 = 0 (màu đen)
- BackgroundTransparency = 0.75 (25% mờ)
- LayoutOrder = 1
- Size = 1, 0, 1, 0 (100% chiều rộng/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 đặt thành 0, Thickness thành 3, và Transparency thành 0.25 (75% mờ viền đen)

Tay cầm tương tác
Với container thanh trượt đã được xây dựng, bạn có thể tạo một tay cầm có thể kéo cho người chơi tương tác trong quá trình 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ệ khung hình được ràng buộc)
- ZIndex = 3 (đặt tay cầm ở phía trước các yếu 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 giúp dễ dàng và khuyến khích tương tác với các yếu 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 các giá trị tỷ lệ của vị trí cha của detector)
Để đảm bảo phạm vi kéo tuyến tính của tay cầm được giới hạn trong container của nó, liên kết thuộc tính BoundingUI của nó với container SliderFrame:
- Chọn UIDragDetector.
- Nhấp vào 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 của tay cầm SliderFrame.

Liên kết thuộc tính BoundingUI giờ đây phản ánh container SliderFrame:

Nếu bạn thử nghiệm trò chơi 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ó:
Điền bên trong
Để chỉ ra rõ ràng 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 phần điền bên trong vào 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 (cạnh trái và giữa theo chiều dọc)
- BackgroundColor3 = [0, 150, 255] (màu xanh phù hợp với tay cầm của thanh trượt)
- BackgroundTransparency = 0.35 (65% mờ)
- 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 (đặt ở phía trước phần điền/viền của khung cha)
Để phù hợp với hình dạng "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 một biểu tượng của các 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ó xuống dưới thanh trượt đầu tiên.

Mở rộng nhánh cấp cao của BackgroundVolumeSlider, chọn nhãn hình Icon, và thay đổi thuộc tính Image của nó thành rbxassetid://101125859760167 (ID tài sản của biểu tượng với các 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 hồng giống như ([255, 0, 125]).



Tạo nút đóng
Yếu 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 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ệ khung hình được ràng buộc)
- Image = rbxassetid://5577404210 (ID tài sản của biểu tượng nút đóng)
- ImageTransparency = 0.25 (75% mờ)
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 thiết lập 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 trong mỗi đối tượng. ModuleScripts tạo điều kiện cho chức năng 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 ở hai bên của ranh giới client-server hoặc cùng một bên của ranh giới.
Bộ điều khiển đối tượng có trạng thái
Module bộ điều khiển đối tượng có trạng thái sau 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 mã sau vào bên 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 tweens để tái sử dụng nhằm tránh việc tạo tweens mới 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 các tweens khác không xung độtfor _, tween in self.tweens dotween:Cancel()endstateTween:Play()endreturn StatefulObjectControllerGiải thích mãDòng Mục đích 6‑10 Định nghĩa và xuất các loại Luau để cải thiện tự động hoàn thành 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 để đặt/thay đổi trạng thái của một đối tượng có trạng thái. Bên trong hàm, các dòng 42 đặt currentStateName của đối tượng thành trạng thái đang được đặt, các dòng 45‑47 hủy bỏ bất kỳ tweens đang chạy để ngăn chặn xung đột, và dòng 49 thực thi tween của trạng thái.
Bộ đ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 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ò chơi.
Chèn một ModuleScript vào container ReplicatedStorage và đổi tên thành SliderController.

Dán mã sau vào bên 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 tạo thanh trượt %s nhưng không thể tìm thấy Handle", object:GetFullName()))endlocal innerFill = object:FindFirstChild("InnerFill", true)if not innerFill thenwarn(string.format("Đã cố gắng khởi tạo thanh trượt %s nhưng không thể tìm thấy InnerFill", object:GetFullName()))endlocal dragDetector = handle:FindFirstChildWhichIsA("UIDragDetector")if not dragDetector thenwarn(string.format("Đã cố gắng khởi tạo thanh trượt %s nhưng không thể 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 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 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 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 yếu tố thanh trượt, chấp nhận Frame cha 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 yếu tố tay cầm, điền bên trong, và 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 thêm chi tiết). 48‑61 Hàm được sử dụng để đặt giá trị của thanh trượt. Bên trong hàm, các dòng 53‑54 đảm bảo rằng vị trí tay cầm và kích thước khung bên trong được đồng bộ, và dòng 57 truyền giá trị đã thay đổi đến hàm callback để bạn có thể sử dụng giá trị theo nhu cầu.
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ứ lại 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 (anh em) với SettingsMenu và SettingsButton, các đối tượng UI cấp cao mà nó sẽ quản lý.

Dán mã sau vào bên 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 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 để khám phá cách script 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 tạo với hàm StatefulObjectController.hydrate(), và nút SettingsButton bên trong HUDContainer được tham chiếu làm đối tượng để khởi tạo. 12‑25 Một bảng states được truyền vào 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 định nghĩa trong bảng properties. Trong trường hợp này, menuOpen khai báo một thay đổi trạng thái thành 45° cho Rotation của nút với một tween Exponential trong ½ giây, trong khi menuClosed hiệu quả khôi phục nút về độ xoay mặc định 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 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 một 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 làm điều tương tự với SettingsMenu để nó mở hoặc đóng cùng 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à đặt 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 tạo với hàm StatefulObjectController.hydrate() và khung SettingsMenu bên trong HUDContainer được tham chiếu làm đối tượng để khởi tạo. 32‑46 Tương tự như nút cài đặt, một bảng states được truyền vào với hai trạng thái duy nhất, menuOpen và menuClosed. Ở đây, menuOpen khai báo một thay đổi trạng thái thành UDim2.fromScale(0.5, 0.5) cho Position của khung với một tween Bounce trong ½ giây, cũng như một trạng thái Visible là true. Ngược lại, menuClosed khai báo một thay đổi trạng thái vị trí thành UDim2.fromScale(0.5, 0.4) và Visible thành false, nhưng TweenInfo với thời gian 0 ở dòng 41 hiệu quả làm cho 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 tương tác của người chơi:
Dòng Mục đích 81 Đặt cùng một 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 Đặt 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 tạo với hàm SliderController.hydrate() và khung EffectsVolumeSlider bên trong HUDContainer ⟩ SettingsMenu được tham chiếu làm đối tượng để khởi tạo. Lưu ý rằng tham số thứ hai của true cho FindFirstChild() cho biết phương thức này sẽ tìm kiếm đệ quy xuống nhánh HUDContainer để xác định EffectsVolumeSlider. 55 Giá trị ban đầu của thanh trượt được đặt thành Volume hiện tại của nhóm Effects, hoặc 0.5 như một phương án dự phòng. 56‑60 Thiết 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, ngoại trừ việc khởi tạo 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 thiết lập, trò chơi giờ đây 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 liên quan với tương tác của người chơi.









