게임의 HUD (Heads-Up Display)는 일반적으로 HUD 미터 만들기에서 보여준 건강 미터와 같은 비대화형 UI 요소를 표시합니다. 이와 함께 거의 모든 게임은 플레이어의 활성화에 반응하고, 활성화될 때 애니메이션을 실행하며, 다른 대화형 컨트롤과 함께 메뉴를 트윈 인/아웃하는 버튼과 같은 대화형 UI를 필요로 합니다.

UI 기초 - HUD 미터를 시작점으로 하고 UI 기초 - 대화형을 완성된 참조로 사용하여, 이 튜토리얼에서는 다음을 보여줍니다:
- 화면 상단 가장자리에 설정 버튼을 배치하는 방법.
- 대화형 드래그 가능한 슬라이더가 포함된 설정 메뉴 디자인.
- 상태가 있는 선언으로 UI 객체를 확장 가능하게 제어하기 위한 "컨트롤러" 모듈을 형성하기 위해 ModuleScripts를 사용하는 방법.
- 버튼을 플레이어 활성화에 연결하여 설정 메뉴를 트글 인/아웃하는 방법.
- 드래그 가능한 UI 슬라이더를 연결하여 효과음과 배경 음향의 볼륨을 별도로 조정하는 방법.
장치 에뮬레이터 활성화
HUD 미터 만들기에서 언급했듯이, 전화기와 태블릿은 화면 공간이 가장 적기 때문에 UI 요소가 더 작은 화면에 맞고 플레이어에게 명확하게 보이도록 하는 것이 중요합니다. 아직 하지 않았다면, Studio에서 장치 에뮬레이터를 활성화하세요:
Studio에서 UI 기초 - HUD 미터 템플릿을 엽니다.
Studio의 테스트 메뉴에서 장치 에뮬레이터를 켭니다.
메인 뷰포트 바로 위의 바에서 iPhone X 또는 Samsung Galaxy A51과 같은 전화 에뮬레이션을 선택합니다. 그런 다음, 창에 맞추기로 보기 크기를 설정하여 Studio에서 최대 공간을 활용합니다.

설정 버튼 만들기
GuiButtons는 클릭되거나 탭될 때 발생하는 멀티 플랫폼 Activated 이벤트와 같은 내장 기능을 가진 대화형 사용자 인터페이스 요소입니다. GuiButton 클래스는 TextButton과 ImageButton의 두 가지 변형으로 확장되며, 이 튜토리얼에서는 설정 메뉴를 열거나 닫기 위해 "기어" 모양의 ImageButton을 사용합니다.

설정 버튼을 구성하려면:
Explorer 창에서 StarterGui 내의 HUDContainer를 찾습니다.

새 버튼을 선택한 상태에서 속성 창에서 다음을 설정합니다:
- AnchorPoint = 0.5, 0.25 (수평 중앙 및 수직 상단 1/4)
- BackgroundTransparency = 1 (완전히 투명)
- Position = 0.5, 0, 0, 0 (상단 중앙 위치)
- Size = 0.1, 0, 0.1, 0 (비율이 제한된 후 화면 높이의 10%)
- Image = rbxassetid://104919049969988 (기어 기호의 자산 ID)
기본 속성으로 버튼에 UIAspectRatioConstraint를 삽입합니다 (1:1 너비 대 높이).

설정 메뉴 만들기
새로운 설정 버튼은 설정 메뉴를 열고 닫는 스크립트가 작성될 것이며, 플레이어에게 설정이나 기타 정보에 대한 즉각적인 접근을 제공합니다. 이 튜토리얼에서는 메뉴에 배경 오디오와 효과음의 볼륨 레벨을 독립적으로 조정할 수 있는 대화형 슬라이더가 포함됩니다.

부모 프레임 만들기
HUD 미터 만들기에서 언급했듯이, Frame는 다른 UI 객체의 컨테이너 역할을 합니다. 전체 설정 메뉴는 단일 부모 프레임으로 구성되어, 현재 상태에 따라 입력에 다르게 반응하는 상태가 있는 객체로 관리하기 쉽게 만듭니다.
HUDContainer에 Frame을 삽입하고 이름을 SettingsMenu로 변경합니다.

SettingsMenu가 선택된 상태에서 다음 속성을 설정합니다:
- AnchorPoint = 0.5 (중앙 앵커)
- BackgroundColor3 = 30, 30, 60 (네이비 블루)
- BackgroundTransparency = 0.25 (75% 불투명)
- Position = 0.5, 0, 0.5, 0 (화면 중앙)
- Size = 0.75, 0, 0.75, 0 (화면 내 삽입 영역의 75% 너비/높이)

SettingsMenu에 UIAspectRatioConstraint를 삽입하고 AspectRatio 속성을 2.5로 설정합니다. (너비 대 높이 비율 2.5:1).


SettingsMenu의 픽셀 너비와 높이를 제한하기 위해 UISizeConstraint를 삽입합니다. MaxSize를 800, inf로 설정하고 MinSize를 350, 0로 설정합니다.



슬라이더 구성
플레이어가 볼륨 레벨을 조정할 수 있도록 설정 메뉴에는 UIDragDetector로 구동되는 두 개의 드래그 가능한 슬라이더 위젯이 포함됩니다. 이는 2D 사용자 인터페이스 요소와의 상호작용을 용이하게 하는 편리한 객체입니다.
첫 번째 슬라이더의 부모 컨테이너를 만들려면:
SettingsMenu 컨테이너에 새로운 Frame을 삽입하고 이름을 EffectsVolumeSlider로 변경합니다.

EffectsVolumeSlider 프레임이 선택된 상태에서 다음 속성을 설정합니다:
AnchorPoint = 0.5 (중앙 앵커)
BackgroundTransparency = 1 (완전히 투명)
Position = 0.5, 0, 0.35, 0 (수평 중앙 및 컨테이너의 상단에서 35%)
Size = 0.8, 0, 0.1, 0 (컨테이너 너비의 80% 및 높이의 10%)

EffectsVolumeSlider에 새로운 UIListLayout을 삽입합니다. 이 레이아웃 수정자는 형제 GuiObjects를 부모 컨테이너 내에서 수평 행 또는 수직 열로 자동 정렬하는 강력한 방법으로, 플렉스 개념을 적용할 수 있습니다.

새로운 UIListLayout에 대해 다음 속성을 설정합니다:
- Padding = 0.06, 0 (목록 요소 간 6% 패딩)
- FillDirection = Horizontal (왼쪽에서 오른쪽으로 요소 배열)
- HorizontalFlex = Fill (정의된 너비를 무시하고 부모 컨테이너를 가득 채우도록 요소 크기 조정)
- VerticalAlignment = Center (요소를 수직 중앙에 정렬)
슬라이더 아이콘
간단한 아이콘에는 오디오 노트와 "버스트" 기호가 포함되어 있어 플레이어가 설정 메뉴를 열 때 슬라이더의 목적을 식별하는 데 도움이 됩니다.

EffectsVolumeSlider 프레임에 ImageLabel을 삽입하고 이름을 Icon으로 변경합니다. 이 아이콘은 플레이어가 슬라이더가 제어하는 게임 측면을 이해하는 데 도움이 됩니다.

Icon이 선택된 상태에서 다음 속성을 설정합니다:
- BackgroundTransparency = 1 (완전히 투명)
- Size = 2.5, 0, 2.5, 0 (비율이 제한된 후 부모 프레임의 250% 높이)
- Image = rbxassetid://90019827067389 (오디오 노트와 "버스트" 기호의 자산 ID)
범위 프레임
아이콘 바로 오른쪽에, 슬라이더의 대화형 부분은 또 다른 Frame 내에 포함되어야 합니다.

EffectsVolumeSlider 프레임에 새로운 Frame을 삽입하고 이름을 SliderFrame으로 변경합니다. 이는 UIListLayout의 직접 형제여야 합니다.

SliderFrame이 선택된 상태에서 다음 속성을 설정합니다:
- BackgroundColor3 = 0 (검정색)
- BackgroundTransparency = 0.75 (25% 불투명)
- LayoutOrder = 1
- Size = 1, 0, 1, 0 (부모 프레임의 100% 너비/높이)
SliderFrame에 다음 수정자를 삽입합니다:

대화형 핸들
슬라이더 컨테이너가 구성되었으므로, 이제 플레이어가 게임 중에 상호작용할 수 있는 드래그 가능한 핸들을 만들 수 있습니다.

SliderFrame 컨테이너에 새로운 Frame을 삽입하고 이름을 Handle로 변경합니다.

Handle이 선택된 상태에서 다음 속성을 설정합니다:
- AnchorPoint = 0.5 (중앙 앵커)
- Position = 0.5, 0, 0.5, 0 (부모 프레임의 수평 및 수직 중앙)
- Size = 1.2, 0, 1.2, 0 (비율이 제한된 후 부모 프레임의 120% 높이)
- ZIndex = 3 (다른 슬라이더 요소 앞에 시각적으로 레이어링)
Handle에 다음 수정자를 삽입합니다:
- UIAspectRatioConstraint와 그 기본 속성 (1:1 너비 대 높이)
Handle에 UIDragDetector 객체를 삽입합니다. 이 편리한 객체는 2D 사용자 인터페이스 요소와의 상호작용을 용이하게 하고 장려합니다.

새로운 UIDragDetector에 대해 다음 속성을 설정합니다:
- ResponseStyle = Scale (탐지기의 부모 위치의 스케일 값으로 이동)
핸들의 선형 드래그 범위가 컨테이너로 제한되도록 BoundingUI 속성을 SliderFrame 컨테이너에 연결합니다:
- UIDragDetector를 선택합니다.
- 속성 창에서 BoundingUI 속성을 클릭합니다.
- Explorer 창에서 핸들의 부모 SliderFrame을 클릭합니다.

이제 BoundingUI 속성 링크가 SliderFrame 컨테이너를 반영합니다:

이제 게임을 플레이 테스트하면 핸들을 부모 컨테이너 내에서 왼쪽과 오른쪽으로 드래그할 수 있습니다:
내부 채우기
슬라이더가 0%에서 100% 범위를 제어한다는 것을 더 명확하게 나타내기 위해, 핸들의 가변 위치와 동기화되는 내부 채우기를 컨테이너의 왼쪽에 추가할 수 있습니다.

SliderFrame 컨테이너에 새로운 Frame을 삽입하고 이름을 InnerFill로 변경합니다.

InnerFill 프레임에 대해 다음 속성을 설정합니다:
- AnchorPoint = 0, 0.5 (왼쪽 가장자리 및 수직 중앙)
- BackgroundColor3 = [0, 150, 255] (슬라이더 핸들과 일치하는 파란색)
- BackgroundTransparency = 0.35 (65% 불투명)
- Position = 0, 0, 0.5, 0
- Size = 0.5, 0, 1, 0 (부모 프레임의 50% 너비 및 100% 높이)
- ZIndex = 2 (부모 프레임의 채우기/윤곽선 앞에 시각적으로 레이어링)
슬라이더 복제
첫 번째 슬라이더가 구성되었으므로, 이를 쉽게 복제하고 다른 목적을 나타내기 위해 일부 시각적 측면을 수정할 수 있습니다. 이 경우, 음악 노트 아이콘으로 상징되는 배경 오디오의 볼륨 레벨입니다.

완료된 EffectsVolumeSlider 객체를 선택합니다.

이를 복제합니다 (CtrlD 또는 ⌘D) 그리고 복제된 이름을 BackgroundVolumeSlider로 변경합니다.

복제된 Position을 0.5, 0, 0.7, 0로 변경하여 첫 번째 슬라이더 아래로 이동합니다.

BackgroundVolumeSlider의 최상위 브랜치를 확장하고 Icon 이미지 레이블을 선택한 후, Image 속성을 rbxassetid://101125859760167 (음악 노트가 있는 기호의 자산 ID)로 변경합니다.



닫기 버튼 만들기
설정 메뉴의 마지막 요소는 메뉴를 닫기 위한 추가 입력을 제공하는 닫기 버튼입니다 (상단 중앙의 SettingsButton도 동일한 목적을 수행합니다).

CloseButton이 선택된 상태에서 다음 속성을 설정합니다:
- AnchorPoint = 1, 0 (상단 오른쪽 모서리)
- BackgroundTransparency = 1 (완전히 투명)
- Position = 1, -10, 0, 10 (상단 오른쪽 모서리에서 10픽셀 안쪽)
- Size = 0.15, 0, 0.15, 0 (비율이 제한된 후 프레임 높이의 15%)
- Image = rbxassetid://5577404210 (닫기 버튼 기호의 자산 ID)
- ImageTransparency = 0.25 (75% 불투명)
제어 모듈 만들기
확장 가능한 제어 모듈 설정은 각 객체에 개별 스크립트를 배치하는 것보다 대화형 UI 관리를 더 간소화합니다. ModuleScripts는 클라이언트-서버 경계의 서로 다른 측면 또는 경계의 동일한 측면에서 스크립트 간에 코드를 재사용할 수 있도록 하여 이 확장 가능한 기능을 촉진합니다.
상태가 있는 객체 컨트롤러
다음 상태가 있는 객체 컨트롤러 모듈은 SettingsButton 및 SettingsMenu와 같은 UI 객체에 동작을 부착하고 다양한 상태 간에 쉽게 전환/트윈할 수 있게 해줍니다. 모듈을 만들려면:
ReplicatedStorage 컨테이너에 ModuleScript를 삽입하고 이름을 StatefulObjectController로 변경합니다.

모듈 내에 다음 코드를 붙여넣습니다:
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)-- Create tweens for reuse to avoid making new tweens every time state is changedfor 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("Attempted to set %s to unknown state '%s'", self.object:GetFullName(), stateName))returnendself.currentStateName = stateName-- Make sure other tweens aren't conflictingfor _, tween in self.tweens dotween:Cancel()endstateTween:Play()endreturn StatefulObjectController코드 설명라인 목적 6‑10 이 모듈을 사용할 때 자동 완성과 린팅을 개선하기 위해 Luau 유형을 정의하고 내보냅니다. 12‑33 상태가 있는 UI 객체에 동작을 초기화하고 부착하는 데 사용되는 함수로, GuiObject를 object로, 상태와 해당 TweenInfo 데이터를 포함하는 테이블을 수용하며, 초기 상태 이름을 수용합니다. 이 함수 내에서 26‑28 라인은 각 상태에 대한 Tweens를 구성하고, 30 라인은 모듈의 setState() 함수를 호출하여 객체를 초기 상태로 설정합니다. 35‑50 상태가 있는 객체의 상태를 설정/변경하는 데 사용되는 함수입니다. 42 라인은 객체의 currentStateName을 설정하려는 상태로 설정하고, 45‑47 라인은 충돌을 방지하기 위해 실행 중인 모든 트윈을 취소하며, 49 라인은 상태의 트윈을 실행합니다.
슬라이더 컨트롤러
추가 모듈은 두 개의 볼륨 슬라이더를 초기화하고 제어합니다. 또한 각 슬라이더에 콜백 함수를 연결하여 플레이어가 슬라이더와 상호작용하는 것을 감지하고 게임에서 원하는 변경을 적용할 수 있습니다.
ReplicatedStorage 컨테이너에 ModuleScript를 삽입하고 이름을 SliderController로 변경합니다.

모듈 내에 다음 코드를 붙여넣습니다:
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("Attempted to hydrate slider %s but couldn't find Handle", object:GetFullName()))endlocal innerFill = object:FindFirstChild("InnerFill", true)if not innerFill thenwarn(string.format("Attempted to hydrate slider %s but couldn't find InnerFill", object:GetFullName()))endlocal dragDetector = handle:FindFirstChildWhichIsA("UIDragDetector")if not dragDetector thenwarn(string.format("Attempted to hydrate slider %s but couldn't find UIDragDetector", object:GetFullName()))endlocal self = setmetatable({handle = handle,innerFill = innerFill,dragDetector = dragDetector,value = initialValue or 0.5,onChanged = onChanged,}, SliderController)-- Set initial valueself:setValue(self.value)-- Connect detector to player manipulationself.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-- Update the handle position and inner frame size to matchself.handle.Position = UDim2.fromScale(clampedValue, 0.5)self.innerFill.Size = UDim2.fromScale(clampedValue, 1)-- Run the user's callback with the latest valuelocal changeSuccess, changeResult = pcall(self.onChanged, clampedValue)if not changeSuccess thenwarn("Error in slider callback:", changeResult)endendreturn SliderController코드 설명라인 목적 7‑46 슬라이더 요소에 동작을 초기화하고 부착하는 데 사용되는 함수로, 슬라이더의 부모 Frame을 object로, 슬라이더 변경을 처리할 onChanged 콜백 함수를 수용하며, 슬라이더 핸들 위치의 초기 값을 수용합니다. 함수 내에서 14‑27 라인은 슬라이더의 핸들, 내부 채우기, 및 UIDragDetector 요소가 존재하는지 확인합니다. 38 라인은 모듈의 setValue() 함수를 호출하고, 41‑43 라인은 드래그 탐지기의 DragContinue 이벤트를 모듈의 setValue() 함수에 연결합니다 (다음 행의 세부 사항 참조). 48‑61 슬라이더의 값을 설정하는 데 사용되는 함수입니다. 함수 내에서 53‑54 라인은 핸들 위치와 내부 채우기 크기가 동기화되도록 보장하며, 57 라인은 변경된 값을 콜백 함수에 전달하여 필요에 따라 값을 활용할 수 있도록 합니다.
설정 스크립트 만들기
설정 버튼과 설정 메뉴가 완료되었으므로, 제어 모듈을 활용하여 모든 것을 연결하는 단일 스크립트를 작성할 수 있습니다.
HUDContainer에 새로운 LocalScript를 삽입하고 이름을 SettingsScript로 변경하여 그 목적을 설명합니다. 이 스크립트는 SettingsMenu 및 SettingsButton과 같은 최상위 UI 객체와 동일한 수준(형제)에 있어야 합니다.

스크립트 내에 다음 코드를 붙여넣습니다:
SettingsScriptlocal ReplicatedStorage = game:GetService("ReplicatedStorage")local SoundService = game:GetService("SoundService")local SliderController = require(ReplicatedStorage.SliderController)local StatefulObjectController = require(ReplicatedStorage.StatefulObjectController)local HUDContainer = script.Parent-- Initialize settings buttonlocal 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"})-- Initialize settings menu framelocal 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"})-- Initialize effects volume sliderlocal 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,})-- Initialize background volume sliderlocal 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,})-- Connect buttons to player interactionHUDContainer: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)다음 섹션을 참조하여 스크립트가 제어 모듈을 어떻게 활용하는지 탐색합니다.
설정 버튼10-27 라인은 설정 버튼에 동작을 초기화하고 부착합니다:
라인 목적 10‑11 로컬 참조 settingsButton이 선언되고 StatefulObjectController.hydrate() 함수로 수화되며, HUDContainer 내의 SettingsButton 버튼이 수화할 객체로 참조됩니다. 12‑25 menuOpen 및 menuClosed라는 두 개의 고유 상태가 있는 states 테이블이 전달됩니다. 각 상태는 특정 상태가 properties 테이블에 정의된 목표 속성에 도달하는 방법을 선언하기 위해 TweenInfo를 통해 전환을 포함합니다. 이 경우, menuOpen은 버튼의 Rotation을 45°로 변경하고, Exponential 트윈을 0.5초 동안 적용하며, menuClosed는 버튼을 기본 회전인 0으로 복원합니다. 26 버튼의 초기 상태가 menuClosed로 설정됩니다 (이는 states 테이블 내의 어떤 상태 이름으로도 설정할 수 있습니다). 스크립트의 아래쪽 (라인 76-86)에서 버튼은 플레이어 상호작용에 연결되어 UI 상태 변경을 트리거합니다:
라인 목적 76‑82 SettingsButton은 Activated 이벤트에 연결되어 익명 함수를 호출합니다. 익명 함수 내에서 77‑79 라인은 targetState 변수를 menuOpen과 menuClosed 사이에서 전환합니다. 80 라인은 setState()를 호출하여 버튼의 상태를 targetState로 설정하고, 81 라인은 SettingsMenu에 대해서도 동일하게 수행하여 SettingsButton의 활성화와 함께 열리거나 닫히도록 합니다. 83‑86 CloseButton은 Activated 이벤트에 연결되어 익명 함수를 호출하며, 이 함수는 SettingsButton과 SettingsMenu의 상태를 menuClosed로 설정합니다. 설정 메뉴30-49 라인은 설정 메뉴에 동작을 초기화하고 부착합니다:
라인 목적 30‑31 로컬 참조 settingsMenu가 선언되고 StatefulObjectController.hydrate() 함수로 수화되며, HUDContainer 내의 SettingsMenu 프레임이 수화할 객체로 참조됩니다. 32‑46 설정 버튼과 유사하게, menuOpen 및 menuClosed라는 두 개의 고유 상태가 있는 states 테이블이 전달됩니다. 여기서 menuOpen은 프레임의 Position을 UDim2.fromScale(0.5, 0.5)로 변경하고, Bounce 트윈을 0.5초 동안 적용하며, Visible 상태를 true로 설정합니다. 반면, menuClosed는 위치 상태를 UDim2.fromScale(0.5, 0.4)로 변경하고 Visible을 false로 설정하지만, TweenInfo가 0 지속 시간을 가지므로 상태 변경이 즉시 발생합니다. 48 프레임의 초기 상태가 menuClosed로 설정됩니다. 스크립트의 아래쪽 (라인 76-86)에서 설정 메뉴는 플레이어 상호작용에 연결됩니다:
라인 목적 81 SettingsMenu의 동일한 targetState를 설정하여 SettingsButton의 활성화와 동기화합니다. 85 CloseButton이 활성화될 때 SettingsMenu의 상태를 menuClosed로 설정합니다. 볼륨 슬라이더51-73 라인은 EffectsVolumeSlider 및 BackgroundVolumeSlider에 동작을 초기화하고 부착합니다:
라인 목적 52 로컬 참조를 Effects SoundGroup에 설정합니다. SoundGroup은 여러 Sounds를 상호 연관시키고 그룹의 Volume 속성을 통해 모든 소리의 볼륨을 제어할 수 있게 해줍니다. 53‑54 로컬 참조 effectsVolumeSlider가 선언되고 SliderController.hydrate() 함수로 수화되며, HUDContainer ⟩ SettingsMenu 내의 EffectsVolumeSlider 프레임이 수화할 객체로 참조됩니다. true라는 두 번째 매개변수는 FindFirstChild()가 HUDContainer 브랜치 아래로 재귀적으로 EffectsVolumeSlider를 찾도록 지시합니다. 55 슬라이더의 초기 값이 Effects 사운드 그룹의 현재 Volume으로 설정되거나, 기본값으로 0.5가 설정됩니다. 56‑60 슬라이더 값 변경을 Effects 사운드 그룹의 볼륨에 적용하는 콜백 함수를 설정합니다. 64‑73 기본적으로 52-61과 동일하지만, HUDContainer ⟩ SettingsMenu 내의 BackgroundVolumeSlider 프레임을 수화하고 슬라이더 값 변경을 Background 사운드 그룹의 볼륨에 적용합니다.
SettingsScript가 설정되면, 게임은 관련 UI 객체 세트를 플레이어 상호작용에 연결하는 완전한 대화형 UI 예제를 제공합니다.



















