ゲーム内のHUD(ヘッドアップディスプレイ)は、一般的に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の2つのバリエーションに拡張され、このチュートリアルでは設定メニューを開閉するために「ギア」形状のImageButtonを使用します。

設定ボタンを構築するには:
Explorerウィンドウで、StarterGui内のHUDContainerを見つけます。

新しいボタンを選択した状態で、プロパティウィンドウで以下を設定します:
- AnchorPoint = 0.5, 0.25(水平中央および垂直上四分の一)
- 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にUICorner修飾子を挿入し、そのCornerRadiusを0.1, 0に設定します。


スライダーを構築する
プレイヤーが音量レベルを調整できるようにするために、設定メニューにはUIDragDetectorによって駆動される2つのドラッグ可能なスライダーウィジェットが含まれます。この便利なオブジェクトは、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(要素を垂直方向の中央に整列)
スライダーアイコン
オーディオノートと「バースト」シンボルを含むシンプルなアイコンは、プレイヤーが設定メニューを開いたときにスライダーの目的を特定するのに役立ちます。

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は状態のトゥイーンを実行します。
スライダーコントローラー
追加のモジュールは、2つの音量スライダーを初期化して制御します。また、各スライダーにコールバック関数を接続して、プレイヤーのスライダーとのインタラクションを検出し、ゲーム内での望ましい変更を適用できるようにします。
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に変更して目的を説明します。このスクリプトは、管理するトップレベルのUIオブジェクトであるSettingsMenuとSettingsButtonと同じレベル(兄弟)にある必要があります。

スクリプト内に以下のコードを貼り付けます:
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の2つのユニークな状態を持つstatesテーブルが渡されます。各状態には、特定の状態がpropertiesテーブルで定義されたターゲットプロパティに到達する方法を宣言するためのトランジションが含まれます。この場合、menuOpenはボタンのRotationを45°に変更し、Exponentialトゥイーンで0.5秒かけて変化させ、menuClosedはボタンをデフォルトの回転である0に戻します。 26 ボタンの初期状態がmenuClosedに設定されます(これはstatesテーブル内の任意の状態名に設定できます)。 スクリプトの下部(行76-86)では、ボタンがプレイヤーのインタラクションに接続されてUIの状態変更をトリガーします:
行 目的 76‑82 SettingsButtonはActivatedイベントに接続され、匿名関数を呼び出します。匿名関数内で、行77‑79はtargetState変数をmenuOpenとmenuClosedの間でトグルします。行80はボタンの状態をtargetStateに設定するためにsetState()を呼び出し、行81はSettingsMenuにも同様に呼び出して、SettingsButtonのアクティベーションに応じて開閉します。 83‑86 CloseButtonはActivatedイベントに接続され、匿名関数を呼び出して、SettingsButtonとSettingsMenuの両方の状態をmenuClosedに設定します。 設定メニュー行30-49は、設定メニューに動作を初期化して追加します:
行 目的 30‑31 ローカル参照settingsMenuが宣言され、StatefulObjectController.hydrate()関数で水分補給され、HUDContainer内のSettingsMenuフレームが水分補給するオブジェクトとして参照されます。 32‑46 設定ボタンと同様に、menuOpenとmenuClosedの2つのユニークな状態を持つstatesテーブルが渡されます。ここで、menuOpenはフレームのPositionをUDim2.fromScale(0.5, 0.5)に変更し、Bounceトゥイーンで0.5秒かけて変化させ、Visibleの状態をtrueにします。対照的に、menuClosedは位置状態をUDim2.fromScale(0.5, 0.4)に変更し、Visibleをfalseにしますが、行41の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という2番目のパラメータは、FindFirstChild()メソッドにHUDContainerブランチを再帰的に検索してEffectsVolumeSliderを見つけるよう指示します。 55 スライダーの初期値は、Effectsサウンドグループの現在のVolumeに設定され、フォールバックとして0.5が使用されます。 56‑60 スライダーの値の変更をEffectsサウンドグループの音量に適用するためのコールバック関数を確立します。 64‑73 基本的には行52-61と同じですが、HUDContainer ⟩ SettingsMenu内のBackgroundVolumeSliderフレームを水和し、スライダーの値の変更をBackgroundサウンドグループの音量に適用します。
SettingsScriptが配置されると、ゲームはプレイヤーのインタラクションに関連する一連のUIオブジェクトをリンクする完全に機能するインタラクティブUIの例を提供します。























