⌨️

【Roblox】ProximityPromptのカスタムUI(2)~タップ入力とボタンアイコン~

に公開

1. はじめに

前回の記事で、ProximityPromptのカスタムUIの表示の仕組みを作成しました。
まだ前回の記事を読んでいない方は、先にこちらをご覧ください。
https://zenn.dev/landho_roblox/articles/9b4aebed5f2fce
前回までの形だとまだ表示非表示の切り替えしかできていないため、今回はその続きとして、各ProximityPromptInputTypeへの対応を行います。
ProximityPromptInputTypeとはユーザーの操作の入力方式のことで、Touchはスマートデバイスのタッチ操作、Gamepadはゲームパッド(いわゆるコントローラ)、Keyboardはそのままキーボードを示しています。
Robloxはマルチプラットフォームなのでこのようにさまざまな入力方式があるため、汎用的なUIを作る場合、入力の受付はもちろん、UIに表示する操作ガイドのボタンアイコンも、現在のProximityPromptInputTypeとProximityPromptの設定を元に適切なものが表示されるようにする必要があるわけです。

ちなみに、今後はPromptUIの作業中の一時的な配置変更については省略しますので、構築作業の際は確認用Partの子に配置、実行時の動作確認の際はPromptUIFolderの子に配置して行うものとして、適宜対応してください。

バージョン:0.672.0.6720706

2. クリック、タップを可能にする

まずは、PromptUIにクリックやタップでアクセスできるようにしましょう。
PromptUIの子にTextButtonを追加してください。
ただの判定用ボタンなので、名前はそのままで大丈夫です。
BackgroundTransparency1Textは空の状態にして、何も表示されなくします。
SizeScaleで(1, 1)に設定し、PromptUIのサイズと同じになるようにします。
この状態になったら、TextButtonをPromptUIControllerの子に移動してください。

前回少し触れましたが、クリックできるかどうかはデバイスやProximityPromptの設定によって変動するため、TextButtonを付けるかどうかもScriptで処理します。
PromptUIControllerのCreateメソッドに以下のコードを追加してください。

PromptUIController
 -- UIに対して動作を設定し、破棄関数を返します.
function promptUIController:Create(prompt: ProximityPrompt, promptUI: BillboardGui, inputType: Enum.ProximityPromptInputType)

 	----------- 省略 -----------

 	-- BaseFrameの設定.
	local baseFrame = promptUI:WaitForChild("BaseFrame")::Frame
	do
 		------- 省略 -------
	end

+	-- ProximityPromptInputTypeがタッチの場合か、ProximityPromptがクリック可能設定である場合、入力受付用のボタンを設定する.
+	if inputType == Enum.ProximityPromptInputType.Touch or prompt.ClickablePrompt then
+		-- TextButtonを複製.
+		local button = script:WaitForChild("TextButton"):Clone()::TextButton
+		button.Parent = promptUI
+
+		local buttonDown = false	-- 入力状態を示すフラグ.
+
+		-- 入力時の処理.
+		button.InputBegan:Connect(function(input: InputObject)
+			if (input.UserInputType == Enum.UserInputType.Touch
+				or input.UserInputType == Enum.UserInputType.MouseButton1)
+				and input.UserInputState ~= Enum.UserInputState.Change
+			then
+				-- 入力の開始をProximityPromptに伝える.
+				prompt:InputHoldBegin()
+				buttonDown = true
+			end
+		end)
+
+		-- 入力終了時の処理.
+		button.InputEnded:Connect(function(input: InputObject)
+			if input.UserInputType == Enum.UserInputType.Touch
+				or input.UserInputType == Enum.UserInputType.MouseButton1
+			then
+				if buttonDown then
+					-- 入力の終了をProximityPromptに伝える.
+					buttonDown = false
+					prompt:InputHoldEnd()
+				end
+			end
+		end)
+
+		-- BillboardGuiが入力を受付けるようにActiveをtrueに.
+		promptUI.Active = true
+	end
+

 	-- イベントを接続.
	local connections = {}

	----------- 省略 -----------

end

入力を受け付けるために必要な処理を追加しました。
入力可能になる条件は、現在のProximityPromptInputTypeがTouchである場合か、ProximityPrompt.ClickablePromptがtrueの場合としています。
ClickablePromptは、ProximityPromptのUIをクリックでの入力を受け付けるかどうかを設定するプロパティなのですが、カスタムUIの場合、こういったプロパティの反映も自分で行う必要があります。

入力可能にする処理では、TextButtonの複製だけではなく、GuiObject.InputBeganGuiObject.InputEndedに接続し、入力された際の処理も追加しています。
コールバック関数の中では、ProximityPrompt:InputHoldBeginProximityPrompt:InputHoldEndを実行しています。

InputHoldBegin, InputHoldEnd

これらのメソッドは、通常ProximityPrompt側で処理している入力に関する情報を、ScriptからProximityPromptに通知できるメソッドです。
InputHoldBeginは入力が開始されたことをProximityPromptに通知して、InputHoldEndが実行されるまでは入力され続けているものとして処理します。
そのままHoldDurationの分時間が経過すれば(0なら即時)Triggerdが発火します。
基本的には、今回のようにカスタムUI上のボタンを押したときの制御に用いるメソッドです。

さて、それではさっそく実行して確認してみましょう。

これで、タップやクリックでの入力が可能になりました!

ちなみに、この後の作成するボタンアイコンにも関係するのですが、UIが表示されている時にProximityPromptInputTypeが変わった場合、PromptHiddenとPromptShownが発火するようになっているため、新しいProximityPromptInputTypeに応じたUIが作り直されることになります。
よって、動的にボタンアイコンやタップでの入力の可否を更新する必要はありません。

3. ボタンアイコンを追加する

では次に、入力すべき操作をユーザーに示すボタンアイコンを表示しましょう。
このアイコンはなかなか手間のかかる部分で、その時のユーザーのProximityPromptInputTypeによって表示を変える必要がある上、キーボードやGamepadの場合、ProximityPromptに設定しているキーやボタンに応じた表示にも対応しなければなりません。
ひとつずつ作っていきましょう。
画像については、Defaultと同じ、公式に用意されているものを使用します。

ボタンアイコンのベースを作る

まずはボタンアイコン用のFrameを追加します。
BaseFrameの子にFrameを一つ追加してください。
名前は「InputFrame」としておきます。

InputFrameのSizeContextRelativeYYにしてください。
SizeContextとは、SizeをScaleで設定する際に、親のどのSizeを基準にするかを決めるプロパティで、RelativeYYにすると、XもYもどちらも親のYを基準にするようになります。
この状態でSizeScaleで(1, 1)にしてください。
親のYを基準にした正方形になるはずです。
そして、このFrameはあくまで枠として使うだけですので、BackgroundTransparency1にして透明にしてください。

次に、InputFrameの子にもう一つFrameを追加してください。
名前は「RoundFrame」としておきます。
これは、ボタンアイコンの背景になるグレーの円になります。
SizeOffsetで(48, 48)にしてください。
そして、AnchorPointPositionScaleで全て0.5に設定して中央に来るようにしたら、BackgroundColor3グレー163, 162, 165)、BackgroundTransparency0.5に設定してください。

最後に、RoundFrameの子にUICornerを追加し、CornerRadiusScaleで1に設定してください。

これで、グレーの円になったはずです。
以下の画像のようになっていればOKです。

続いて、このグレーの円の動作を設定します。
PromptUIControllerのCreateメソッドに以下内容を追記してください。

PromptUIController
 -- UIに対して動作を設定し、破棄関数を返します.
function promptUIController:Create(prompt: ProximityPrompt, promptUI: BillboardGui, inputType: Enum.ProximityPromptInputType)

 	------- 省略 -------

 	-- BaseFrameの設定.
	local baseFrame = promptUI:WaitForChild("BaseFrame")::Frame
	do
 		------- 省略 -------
	end

+	-- RoundFrameのUIの表示と終了のTweenを作成しリストに追加.
+	do
+		local roundFrame = baseFrame:WaitForChild("InputFrame"):WaitForChild("RoundFrame")::Frame
+		table.insert(tweensForFadeOut, TweenService:Create(roundFrame, tweenInfoFast, { BackgroundTransparency = 1 }))
+		table.insert(tweensForFadeIn, TweenService:Create(roundFrame, tweenInfoFast, { BackgroundTransparency = roundFrame.BackgroundTransparency }))
+
+		-- 初期の透明度を1に.
+		roundFrame.BackgroundTransparency = 1
+	end
+
	-- ProximityPromptInputTypeがタッチの場合か、ProximityPromptがクリック可能設定である場合、入力受付用のボタンを設定する.
	if inputType == Enum.ProximityPromptInputType.Touch or prompt.ClickablePrompt then

 	------- 省略 -------

end

BaseFrameと同様、Fadeを設定しました。
これでボタンアイコンのベースの作成は完了です。

タッチ用アイコン

さて、ここから実際に表示するアイコンを作っていきます。
まずは、タッチ操作の場合のアイコンです。

InputFrameの子にImageLabelを一つ追加します。
名前は「TouchButtonImage」としておきます。
RoundFrameと同様にAnchorPointPositionを中央に来るようにしたら、BackgroundTransparency1にして背景を透過してください。
そして、Imageを「rbxasset://textures/ui/Controls/TouchTapIcon.png」に設定してください。
これは、Defaultの場合にタップ時のアイコンに使われているものです。
最後に、Sizeをこの画像に合わせてOffsetで(25, 31)に設定します。
以下の画像のようになっていればOKです。

これで、タップ用のアイコンのImageLabelができました。

ただし、実際にどのアイコンを付けるかはScriptで動的に処理することになります。
適切なボタンアイコンを選択する処理は、他のカスタムUIを作成した際にも流用できそうですので、ModuleScriptとして切り出そうと思います。
というわけで、PromptUIFolderにModuleScriptを追加してください。
名前は「ButtonImageProvider」としておきます。
そして、先ほど作成したTouchButtonImageをButtonImageProviderの子に移動します。

そして、ButtonImageProviderに以下のコードを追加してください。

ButtonImageProvider
--!strict

-- ProximityPromptとProximityPromptInputTypeの情報から、適切なボタンアイコンを複製して、
-- FadeのTweenと一緒に返すメソッドを持つモジュールです.

-- Service
local TweenService = game:GetService("TweenService")
local UserInputService = game:GetService("UserInputService")

local ButtonIconProvider = {}

export type ButtonImageTable = {
	ButtonImage: ImageLabel,
	TweensForFadeIn: {Tween},
	TweensForFadeOut: {Tween}
}

-- 適切なボタンアイコンを複製し、FadeのTweenと一緒に返すメソッド.
function ButtonIconProvider:GetButtonImage(prompt: ProximityPrompt, inputType: Enum.ProximityPromptInputType, tweenInfo: TweenInfo): ButtonImageTable?
	local buttonImage
	local tweensForFadeOut = {}
	local tweensForFadeIn = {}

	-- ボタンアイコンの生成.
	-- タッチ入力の場合.
	if inputType == Enum.ProximityPromptInputType.Touch then
		-- タッチ入力用のボタンアイコンを作成.
		buttonImage = script:WaitForChild("TouchButtonImage"):Clone()

	end

	-- ボタンアイコン用の表示と終了のTweenを作成.
	if buttonImage then
		table.insert(tweensForFadeOut, TweenService:Create(buttonImage, tweenInfo, { ImageTransparency = 1 }))
		table.insert(tweensForFadeIn, TweenService:Create(buttonImage, tweenInfo, { ImageTransparency = buttonImage.ImageTransparency }))

		-- 初期の透明度を1に.
		buttonImage.ImageTransparency = 1

	else
		-- ボタンアイコンを生成できていなかったら終了.
		return nil
	end

	-- tableにして返す.
	local result = {
		ButtonImage = buttonImage,
		TweensForFadeIn = tweensForFadeIn,
		TweensForFadeOut = tweensForFadeOut
	}::ButtonImageTable

	return result
end

return ButtonIconProvider

さらに、PromptUIControllerに以下のコードを追加してください。

PromptUIController
 --!strict
 -- PromptUIの動作を設定するModule

local TweenService = game:GetService("TweenService")
local RunService = game:GetService("RunService")
local Folder = script.Parent

+local ButtonImageProvider = require(Folder:WaitForChild("ButtonImageProvider"))

local promptUIController = {}

 ------- 省略 -------

 -- UIに対して動作を設定し、破棄関数を返します.
function promptUIController:Create(prompt: ProximityPrompt, promptUI: BillboardGui, inputType: Enum.ProximityPromptInputType)

 	------- 省略 -------

 	-- RoundFrameのUIの表示と終了のTweenを作成しリストに追加.
	do
 		------- 省略 -------
	end

+	-- ボタンアイコンの生成.
+	do
+		local buttonTable = ButtonImageProvider:GetButtonImage(prompt, inputType, tweenInfoFast)
+
+		if buttonTable then
+			-- ボタンアイコンをセット.
+			buttonTable.ButtonImage.Parent = inputFrame
+
+			-- FadeのTweenを配列に追加.
+			table.move(buttonTable.TweensForFadeIn, 1, #buttonTable.TweensForFadeIn, #tweensForFadeIn + 1, tweensForFadeIn)
+			table.move(buttonTable.TweensForFadeOut, 1, #buttonTable.TweensForFadeOut, #tweensForFadeOut + 1, tweensForFadeOut)
+		end
+	end
+
 	-- ProximityPromptInputTypeがタッチの場合か、ProximityPromptがクリック可能設定である場合、入力受付用のボタンを設定する.
	if inputType == Enum.ProximityPromptInputType.Touch or prompt.ClickablePrompt then

 	------- 省略 -------

end

ButtonImageProviderでは、まずProximityPromptInputTypeを見てボタンアイコンを生成しています。
タッチ用の場合は単純にCloneしているだけです。
ここに各ProximityPromptInputTypeやProximityPromptの設定に応じたボタンアイコンの生成処理が追加されていくイメージです。

生成したらTweenを設定して、テーブルにして返しています。
タプルで返してもよいのですが、Tween配列のどちらがFadeInでどちらがFadeOutなのかが明確になるため、テーブルにしてメンバ名を付けた上で返しています。

PromptUIControllerの方では、ButtonImageProviderをRequireしてメソッドを使用し、返ってきたテーブルからボタンアイコンを設定し、Tweenを配列に追加しています。

これで、タッチ用のボタンアイコンが表示できるようになりました。

ゲームパッド用アイコン

次はゲームパッド(コントローラ)用のアイコンです。
ただし、どのようなコントローラが接続されているかを確認する方法は今のところ提供されていません。
一応、ボタン入力があった際のキーコードから、PS系かXbox系かを判定する方法もなくはないようなのですが、今回はXbox配置を想定した表示にのみ対応することにします。

まずはImageLabelを作成します。
といっても、ほぼタッチ用の流用でOKです。
先ほど作ったTouchButtonImageをコピーし、InputFrameの子に追加して、名前を「GamepadButtonImage」に変更してください。
次に、SizeOffsetで(24, 24)に変更してください。
そしてImageはScriptで設定するため空白でよいのですが、見え方の確認のため、一旦「rbxasset://textures/ui/Controls/xboxX.png」を付けてみます。
これも、Defaultの場合に使われている画像です。以降も、アイコン画像にはDefaultの画像を用います。
以下の画像のようになっていればOKです。

確認できたら、Image空白にして、先ほどと同様ButtonImageProviderの子に移動します。

そして、ButtonImageProviderに以下のソースコードを追加してください。

ButtonImageProvider
 --!strict

 -- ProximityPromptとProximityPromptInputTypeの情報から、適切なボタンアイコンを複製して、
 -- FadeのTweenと一緒に返すメソッドを持つモジュールです.

 -- Service
local TweenService = game:GetService("TweenService")
local UserInputService = game:GetService("UserInputService")

+-- ボタンアイコン画像.
+local GamepadButtonImage = {
+	[Enum.KeyCode.ButtonX] = "rbxasset://textures/ui/Controls/xboxX.png",
+	[Enum.KeyCode.ButtonY] = "rbxasset://textures/ui/Controls/xboxY.png",
+	[Enum.KeyCode.ButtonA] = "rbxasset://textures/ui/Controls/xboxA.png",
+	[Enum.KeyCode.ButtonB] = "rbxasset://textures/ui/Controls/xboxB.png",
+	[Enum.KeyCode.DPadLeft] = "rbxasset://textures/ui/Controls/dpadLeft.png",
+	[Enum.KeyCode.DPadRight] = "rbxasset://textures/ui/Controls/dpadRight.png",
+	[Enum.KeyCode.DPadUp] = "rbxasset://textures/ui/Controls/dpadUp.png",
+	[Enum.KeyCode.DPadDown] = "rbxasset://textures/ui/Controls/dpadDown.png",
+	[Enum.KeyCode.ButtonSelect] = "rbxasset://textures/ui/Controls/xboxView.png",
+	[Enum.KeyCode.ButtonStart] = "rbxasset://textures/ui/Controls/xboxmenu.png",
+	[Enum.KeyCode.ButtonL1] = "rbxasset://textures/ui/Controls/xboxLB.png",
+	[Enum.KeyCode.ButtonR1] = "rbxasset://textures/ui/Controls/xboxRB.png",
+	[Enum.KeyCode.ButtonL2] = "rbxasset://textures/ui/Controls/xboxLT.png",
+	[Enum.KeyCode.ButtonR2] = "rbxasset://textures/ui/Controls/xboxRT.png",
+	[Enum.KeyCode.ButtonL3] = "rbxasset://textures/ui/Controls/xboxLS.png",
+	[Enum.KeyCode.ButtonR3] = "rbxasset://textures/ui/Controls/xboxRS.png",
+	[Enum.KeyCode.Thumbstick1] = "rbxasset://textures/ui/Controls/xboxLSDirectional.png",
+	[Enum.KeyCode.Thumbstick2] = "rbxasset://textures/ui/Controls/xboxRSDirectional.png",
+}
+
local ButtonIconProvider = {}

export type ButtonImageTable = {
	ButtonImage: ImageLabel,
	TweensForFadeIn: {Tween},
	TweensForFadeOut: {Tween}
}

 -- 適切なボタンアイコンを複製し、FadeのTweenと一緒に返すメソッド.
function ButtonIconProvider:GetButtonImage(prompt: ProximityPrompt, inputType: Enum.ProximityPromptInputType, tweenInfo: TweenInfo): ButtonImageTable?
	local buttonImage
	local tweensForFadeOut = {}
	local tweensForFadeIn = {}

 	-- ボタンアイコンの生成.
 	-- タッチ入力の場合.
	if inputType == Enum.ProximityPromptInputType.Touch then
 		-- タッチ入力用のボタンアイコンを作成.
		buttonImage = script:WaitForChild("TouchButtonImage"):Clone()

+	-- ゲームパッド入力の場合.
+	elseif inputType == Enum.ProximityPromptInputType.Gamepad then
+		-- ProximityPromptに紐づけられているGamepadのKeyCodeに応じた画像のボタンアイコンを作成.
+		if GamepadButtonImage[prompt.GamepadKeyCode] then
+			buttonImage = script:WaitForChild("GamepadButtonImage"):Clone()
+			buttonImage.Image = GamepadButtonImage[prompt.GamepadKeyCode]
+		end
+
	end

 	------- 省略 -------

end

return ButtonIconProvider

最初にGamepad用のアイコン画像のリストが追加され、生成の時にProximityPrompt.GamepadKeyCodeから画像が選択されるようになっています。

これで、ゲームパッド用のボタンアイコンが表示できるようになりました。

キーボード用アイコン

最後に、キーボード用のアイコンです。
他とはちょっと構成が変わり、一番手間のかかるところなのですが、順番にやっていきましょう。

ベースのImageLabel

まずはベースのImageLabelを作ります。
この部分は先ほどと同様にほぼ流用でOKです。
TouchButtonImageをコピーして、InputFrameの子に追加し、名前を「KeyButtonImage」に変更してください。
次に、SizeOffsetで(28, 30)に変更してください。
そしてImageは「rbxasset://textures/ui/Controls/key_single.png」に設定してください。
これがキーボードのアイコンの土台になります。この画像の上にキー名などを表示するわけです。
以下の画像のようになっていればOKです。

専用の画像を用いるキーのImageLabel

続いて、専用の画像を用いるキーのためのImageLabelを作ります。
KeyButtonImageをコピーして、KeyButtonImageの子に追加し、名前を「KeyIconImage」に変更してください。
次に、SizeOffsetで(36, 36)に変更してください。
Imageはゲームパッドのものと同様Scriptで設定するのですが、見え方の確認のため、一旦「rbxasset://textures/ui/Controls/return.png」を付けてみます。
これはエンターキーの場合の画像です。
以下の画像のようになっていればOKです。

完成したら、Image空白にして、KeyIconImageだけをButtonImageProviderの子に移動してください。

テキストを用いるキーのTextLabel

さらに、画像ではなくテキストで表示する場合のためのTextLabelを作ります。
多くの一般的なキーはこちらで表示することになります。
TextLabelをKeyButtonImageの子に追加してください。
名前は「KeyIconText」としておきます。
BackgroundTransparency1SizeScaleで(1, 1)にしてください。
そして、位置の微調整のため、PositionOffsetで(0, -1)にします。
TextColor3(255, 255, 255)にしてください。
FontFaceは、Defaultと同じものにするならBuilder Sansなのですが、こちらのフォントは最初はインストールされていないフォントになっています。
とはいえRobloxから配布されているため、追加する場合は、ツールボックスを開きフォントを選択してBuilder Sansで検索すればインストールできるかと思います。

最後に、TextはやはりScriptで設定するのですが、確認のため「E」などのアルファベットを一文字入れてみましょう。
以下の画像のようになっていればOKです。

完成したら、Text空白にして、KeyIconTextKeyButtonImage両方ButtonImageProviderの子に移動します。
配置に注意してください。

そして、ButtonImageProviderに以下のコードを追加してください。

ButtonImageProvider
 --!strict

 -- ProximityPromptとProximityPromptInputTypeの情報から、適切なボタンアイコンを複製して、
 -- FadeのTweenと一緒に返すメソッドを持つモジュールです.

 -- Service
local TweenService = game:GetService("TweenService")
local UserInputService = game:GetService("UserInputService")

 -- ボタンアイコン画像.
local GamepadButtonImage = {

 	------- 省略 -------

}

+-- キーボードアイコン画像.
+local KeyboardButtonImage = {
+	[Enum.KeyCode.Backspace] = "rbxasset://textures/ui/Controls/backspace.png",
+	[Enum.KeyCode.Return] = "rbxasset://textures/ui/Controls/return.png",
+	[Enum.KeyCode.LeftShift] = "rbxasset://textures/ui/Controls/shift.png",
+	[Enum.KeyCode.RightShift] = "rbxasset://textures/ui/Controls/shift.png",
+	[Enum.KeyCode.Tab] = "rbxasset://textures/ui/Controls/tab.png",
+}
+
+-- キーボード特殊文字アイコン画像.
+local KeyboardButtonIconMapping = {
+	["'"] = "rbxasset://textures/ui/Controls/apostrophe.png",
+	[","] = "rbxasset://textures/ui/Controls/comma.png",
+	["`"] = "rbxasset://textures/ui/Controls/graveaccent.png",
+	["."] = "rbxasset://textures/ui/Controls/period.png",
+	[" "] = "rbxasset://textures/ui/Controls/spacebar.png",
+}
+
+-- 一部キーコードとテキストのマッピング.
+local KeyCodeToTextMapping = {
+	[Enum.KeyCode.LeftControl] = "Ctrl",
+	[Enum.KeyCode.RightControl] = "Ctrl",
+	[Enum.KeyCode.LeftAlt] = "Alt",
+	[Enum.KeyCode.RightAlt] = "Alt",
+	[Enum.KeyCode.F1] = "F1",
+	[Enum.KeyCode.F2] = "F2",
+	[Enum.KeyCode.F3] = "F3",
+	[Enum.KeyCode.F4] = "F4",
+	[Enum.KeyCode.F5] = "F5",
+	[Enum.KeyCode.F6] = "F6",
+	[Enum.KeyCode.F7] = "F7",
+	[Enum.KeyCode.F8] = "F8",
+	[Enum.KeyCode.F9] = "F9",
+	[Enum.KeyCode.F10] = "F10",
+	[Enum.KeyCode.F11] = "F11",
+	[Enum.KeyCode.F12] = "F12",
+}
+
local ButtonIconProvider = {}

export type ButtonImageTable = {
	ButtonImage: ImageLabel,
	TweensForFadeIn: {Tween},
	TweensForFadeOut: {Tween}
}

 -- 適切なボタンアイコンを複製し、FadeのTweenと一緒に返すメソッド.
function ButtonIconProvider:GetButtonImage(prompt: ProximityPrompt, inputType: Enum.ProximityPromptInputType, tweenInfo: TweenInfo): ButtonImageTable?
	local buttonImage
	local tweensForFadeOut = {}
	local tweensForFadeIn = {}

 	-- ボタンアイコンの生成.
 	-- タッチ入力の場合.
	if inputType == Enum.ProximityPromptInputType.Touch then
 		-- タッチ入力用のボタンアイコンを作成.
		buttonImage = script:WaitForChild("TouchButtonImage"):Clone()

 	-- ゲームパッド入力の場合.
	elseif inputType == Enum.ProximityPromptInputType.Gamepad then
		-- ProximityPromptに紐づけられているGamepadのKeyCodeに応じた画像のボタンアイコンを作成.
		if GamepadButtonImage[prompt.GamepadKeyCode] then
			buttonImage = script:WaitForChild("GamepadButtonImage"):Clone()
			buttonImage.Image = GamepadButtonImage[prompt.GamepadKeyCode]
		end

+	-- それ以外(キーボード入力)の場合.
+	else
+		-- キーボード入力用のボタンアイコンのベースを作成.
+		buttonImage = script:WaitForChild("KeyButtonImage"):Clone()		
+
+		-- ProximityPromptに紐づけられているKeyCodeを文字列で取得.
+		local buttonTextString = UserInputService:GetStringForKeyCode(prompt.KeyboardKeyCode)
+
+		-- 専用画像を用いるキーボード用ボタンアイコン画像を取得.
+		local buttonTextImage = KeyboardButtonImage[prompt.KeyboardKeyCode]
+
+		-- 取得できなかった場合、特殊文字用のボタンアイコン画像を取得.
+		if buttonTextImage == nil then
+			buttonTextImage = KeyboardButtonIconMapping[buttonTextString]
+		end
+
+		-- さらに取得できなかった場合、専用画像を用いないキーとしての処理を行う.
+		if buttonTextImage == nil then
+			-- Ctrlなどの一部のキーコード用の文字列を取得しなおす.
+			if KeyCodeToTextMapping[prompt.KeyboardKeyCode] then
+				buttonTextString = KeyCodeToTextMapping[prompt.KeyboardKeyCode]
+			end
+		end
+
+		-- この時点で画像が取得できている(専用画像を用いるキーコードだった)場合.
+		if buttonTextImage then
+			local icon = script:WaitForChild("KeyIconImage"):Clone()
+			icon.Image = buttonTextImage
+
+			-- アイコン部分用の表示と終了のTweenを作成.
+			table.insert(tweensForFadeOut, TweenService:Create(icon, tweenInfo, { ImageTransparency = 1 }))
+			table.insert(tweensForFadeIn, TweenService:Create(icon, tweenInfo, { ImageTransparency = icon.ImageTransparency }))
+
+			-- 初期の透明度を1に.
+			icon.ImageTransparency = 1
+
+			-- ButtonImageの子にする.
+			icon.Parent = buttonImage
+
+		-- 専用画像を用いないキーで、文字列が取得できている場合.
+		elseif buttonTextString ~= nil and buttonTextString ~= "" then
+			local buttonText = script:WaitForChild("KeyIconText"):Clone()
+			buttonText.Text = buttonTextString
+
+			-- 二文字以上ならTextSizeを12に下げる.
+			if string.len(buttonTextString) > 2 then
+				buttonText.TextSize = 12
+			end
+
+			-- アイコン部分用の表示と終了のTweenを作成.
+			table.insert(tweensForFadeOut, TweenService:Create(buttonText, tweenInfo, { TextTransparency = 1 }))
+			table.insert(tweensForFadeIn, TweenService:Create(buttonText, tweenInfo, { TextTransparency = buttonText.TextTransparency }))
+
+			-- 初期の透明度を1に.
+			buttonText.TextTransparency = 1
+
+			-- ButtonImageの子にする.
+			buttonText.Parent = buttonImage
+
+		else
+			-- 非対応のキーコードなので破棄して終了.
+			buttonImage:Destroy()
+			return nil
+		end
	end

 	------- 省略 -------

end

return ButtonIconProvider

最初に、専用画像を用いるキーの画像リスト、一部の特殊な文字の画像リスト、一部のキーコードとテキストをマップしたリストの三つが追加されています。

処理の部分では、ボタンアイコンのベースを複製した後、ProximityPrompt.KeyboardKeyCodeUserInputService:GetStringForKeyCodeに渡して、KeyCodeを表示用のstringで取得します。
さらに、KeyboardButtonImageのリストから専用画像を用いるボタンアイコン画像を取得します。
この時、リストにキーが無く取得したのがnilだったなら、専用画像を用いるものではないとして、次にKeyboardButtonIconMappingのリストから一部の特殊文字用のボタンアイコン画像を取得します。
それでも取得できなかった場合、画像を使うものではないということになりますが、一部のキーは表示用にテキストを調整したいものがあるため、KeyCodeToTextMappingのリストにあるものはstringを取得しなおします。
これで、「表示する画像のID」または「表示するstring」が取得できました。
その上で、「使用する画像のID」がnilかどうかで、画像とstringどちらを表示するのかを区別できます。

その後は、画像であればKeyIconImageを、stringであればKeyIconTextを生成し、設定を行っています。
stringの場合、文字数が二文字以上ならTextLabel.TextSize12にしています。
デフォルトでは14なので、Textが長くなる分、少し小さくしている感じです。

最後に、一応ここまでの間で画像もstringも取得できていなかった場合、非対応のキーコードとして終了してnilを返しています。
この場合、エラーを出すようにしてもよいでしょう。

動作確認

ようやく各ボタンアイコンの作成が終わったので、さっそく実行して確認してみましょう。
実行する際は、タッチ入力を検証するため、テストタブからデバイスの仮想化をONにし、iPhone 14 Proなどのスマートデバイスを選択してください。
仮想的に選択したデバイスで実行している状態になります。

この状態で実行すると、マウスクリックが仮想的にタッチ入力として扱われ、ProximityPromptInputTypeはTouchになります。
このままでもキーボードで操作するとProximityPromptInputTypeはKeyboardになるので、そちらも検証可能です。
さらに、同じくテストタブからコントローラの仮想化をONにすると、キーボード入力が仮想的にコントローラ入力として扱われ、ProximityPromptInputTypeはGamepadになります。


これらの仮想化機能を使って、各ProximityPromptInputTypeでの表示を確認してみます。

これで、ボタンアイコンの表示ができました!
確認する際は、ProximityPromptのKeyboardKeyCodeやGamepadKeyCodeを色々と変えて、きちんと対応できているかを確かめておきましょう。

4. サンプルコード

今回作成した最終的なサンプルコードを掲載しておきます。

PromptUIDispatcher
PromptUIDispatcher
--!strict

-- Service
local ProximityPromptService = game:GetService("ProximityPromptService")
local Players = game:GetService("Players")

-- Variable
local LocalPlayer = Players.LocalPlayer

local PlayerGui = LocalPlayer:WaitForChild("PlayerGui")
local Folder = game:GetService("ReplicatedStorage"):WaitForChild("PromptUIFolder")

-- プロンプト用のScreenGuiを取得する関数.
local function getScreenGui()
	-- プロンプト用のScreenGui取得.
	local screenGui = PlayerGui:FindFirstChild("ProximityPrompts")

	-- 存在しなかったら作成.
	if screenGui == nil then
		screenGui = Instance.new("ScreenGui")
		screenGui.Name = "ProximityPrompts"
		screenGui.ResetOnSpawn = false
		screenGui.Parent = PlayerGui
	end

	-- 取得ないし作成したScreenGuiを返す.
	return screenGui
end

-- PromptのUIを作成し、破棄関数を返します.
local function createPrompt(prompt: ProximityPrompt, inputType: Enum.ProximityPromptInputType, gui: ScreenGui)
	-- UIを複製.
	local promptUI = Folder:WaitForChild("PromptUI"):Clone()

	-- BillboardGuiのターゲットと親を設定.
	promptUI.Adornee = prompt.Parent
	promptUI.Parent = gui

	-- PromptUIControllerをrequire
	local promptController = require(Folder:WaitForChild("PromptUIController"))

	-- 動作を設定して破棄関数を返す.
	return promptController:Create(prompt, promptUI, inputType)
end

-- ProximityPromptService.PromptShownに、カスタムUIの表示を接続します.
local function onLoad()
	-- ProximityPromptService.PromptShownに接続.
	ProximityPromptService.PromptShown:Connect(function(prompt: ProximityPrompt, inputType: Enum.ProximityPromptInputType)
		-- デフォルトだったら(カスタムじゃなかったら)処理しない.
		if prompt.Style == Enum.ProximityPromptStyle.Default then
			return
		end

		-- プロンプト用のScreenGuiを取得.
		local gui = getScreenGui()

		-- カスタムUI作成、戻り値で破棄関数が渡される.
		local cleanupFunction = createPrompt(prompt, inputType, gui)

		-- PromptHidden待ち.
		-- PromptHiddenはProximityPromptから離れた時等だけではなく、InputTypeが切り替わった時にも発火する.
		prompt.PromptHidden:Wait()

		-- 破棄関数を実行.
		cleanupFunction()
	end)
end

-- 実行.
onLoad()
PromptUIController
PromptUIController
--!strict
-- PromptUIの動作を設定するModule

local TweenService = game:GetService("TweenService")
local RunService = game:GetService("RunService")
local Folder = script.Parent

local ButtonImageProvider = require(Folder:WaitForChild("ButtonImageProvider"))

local promptUIController = {}

-- Tweenの配列を全てPlayする関数.
local function tweenPlayAll(tweens: {Tween})
	for _, v in ipairs(tweens) do
		v:Play()
	end
end

-- UIに対して動作を設定し、破棄関数を返します.
function promptUIController:Create(prompt: ProximityPrompt, promptUI: BillboardGui, inputType: Enum.ProximityPromptInputType)
	-- 各Tweenを一斉実行するための配列.
	local tweensForButtonHoldBegin = {}
	local tweensForButtonHoldEnd = {}
	local tweensForFadeOut = {}
	local tweensForFadeIn = {}

	local fadeDuration = 0.2
	local tweenInfoFast = TweenInfo.new(fadeDuration)


	-- BaseFrameの設定.
	local baseFrame = promptUI:FindFirstChild("BaseFrame")::Frame
	do
		-- Tweenの作成.
		local fadeOut = TweenService:Create(baseFrame, tweenInfoFast, {BackgroundTransparency = 1})
		local fadeIn = TweenService:Create(baseFrame, tweenInfoFast, {BackgroundTransparency = baseFrame.BackgroundTransparency})

		-- リストに追加.
		table.insert(tweensForButtonHoldBegin, fadeOut)
		table.insert(tweensForButtonHoldEnd, fadeIn)
		table.insert(tweensForFadeOut, fadeOut)
		table.insert(tweensForFadeIn, fadeIn)

		-- 初期の透明度を1に.
		baseFrame.BackgroundTransparency = 1
	end

	-- RoundFrameのUIの表示と終了のTweenを作成しリストに追加.
	local inputFrame = baseFrame:FindFirstChild("InputFrame")::Frame
	do
		local roundFrame = inputFrame:FindFirstChild("RoundFrame")::Frame
		table.insert(tweensForFadeOut, TweenService:Create(roundFrame, tweenInfoFast, { BackgroundTransparency = 1 }))
		table.insert(tweensForFadeIn, TweenService:Create(roundFrame, tweenInfoFast, { BackgroundTransparency = roundFrame.BackgroundTransparency }))

		-- 初期の透明度を1に.
		roundFrame.BackgroundTransparency = 1
	end

	-- ボタンアイコンの生成.
	do
		-- ボタンアイコンとそのTweenを取得.
		local buttonTable = ButtonImageProvider:GetButtonImage(prompt, inputType, tweenInfoFast)

		if buttonTable then
			-- ボタンアイコンの親をInputFrameに.
			buttonTable.ButtonImage.Parent = inputFrame

			-- FadeのTweenを配列に追加.
			table.move(buttonTable.TweensForFadeIn, 1, #buttonTable.TweensForFadeIn, #tweensForFadeIn + 1, tweensForFadeIn)
			table.move(buttonTable.TweensForFadeOut, 1, #buttonTable.TweensForFadeOut, #tweensForFadeOut + 1, tweensForFadeOut)
		end
	end

	-- InputTypeがタッチの場合か、ProximityPromptがクリック可能設定である場合、入力受付用のボタンを設定する.
	if inputType == Enum.ProximityPromptInputType.Touch or prompt.ClickablePrompt then
		-- TextButtonを複製.
		local button = script:WaitForChild("TextButton"):Clone()::TextButton
		button.Parent = promptUI

		local buttonDown = false	-- 入力状態を示すフラグ.

		-- 入力時の処理.
		button.InputBegan:Connect(function(input: InputObject)
			if (input.UserInputType == Enum.UserInputType.Touch
				or input.UserInputType == Enum.UserInputType.MouseButton1)
				and input.UserInputState ~= Enum.UserInputState.Change
			then
				-- 入力の開始をProximityPromptに伝える.
				prompt:InputHoldBegin()
				buttonDown = true
			end
		end)

		-- 入力終了時の処理.
		button.InputEnded:Connect(function(input: InputObject)
			if input.UserInputType == Enum.UserInputType.Touch
				or input.UserInputType == Enum.UserInputType.MouseButton1
			then
				if buttonDown then
					-- 入力の終了をProximityPromptに伝える.
					buttonDown = false
					prompt:InputHoldEnd()
				end
			end
		end)

		-- BillboardGuiが入力を受付けるようにActiveをtrueに.
		promptUI.Active = true
	end


	-- イベントを接続.
	local connections = {}

	-- Holdが必要なProximityPromptの場合.
	if prompt.HoldDuration > 0 then
		table.insert(connections, prompt.PromptButtonHoldBegan:Connect(function(playerWhoTriggered: Player)
			tweenPlayAll(tweensForButtonHoldBegin)
		end))
		table.insert(connections, prompt.PromptButtonHoldEnded:Connect(function(playerWhoTriggered: Player)
			tweenPlayAll(tweensForButtonHoldEnd)
		end))
	end

	table.insert(connections, prompt.Triggered:Connect(function(playerWhoTriggered: Player)
		tweenPlayAll(tweensForFadeOut)
	end))
	table.insert(connections, prompt.TriggerEnded:Connect(function(playerWhoTriggered: Player)
		tweenPlayAll(tweensForFadeIn)
	end))


	-- ProximityPromptの情報を元にUIを更新する関数を定義.
	local function updateUIFromPrompt()
		-- AutomaticSizeが反映されるのはRenderStepped直前のため、次のRenderSteppedまで待機する.
		RunService.RenderStepped:Wait()

		-- Size.XはBaseFrameのXに合わせる.
		promptUI.Size = UDim2.fromOffset(baseFrame.AbsoluteSize.X, promptUI.Size.Y.Offset)

		-- Prompt.UIOffsetを反映.
		-- ProximityPrompt.UIOffsetはピクセル単位の値だが、BillboardGui.SizeOffsetはBillboardGui自体の大きさを基準としたScaleの値なので、BillboardGuiのサイズで割ることで変換している.
		promptUI.SizeOffset = Vector2.new(prompt.UIOffset.X / promptUI.Size.X.Offset, prompt.UIOffset.Y / promptUI.Size.Y.Offset)
	end

	-- Changedイベントに接続し、ProximityPromptプロパティが変更された際にUIを更新.
	table.insert(connections, prompt.Changed:Connect(updateUIFromPrompt))

	-- 一度実行しておき、UIを更新.
	updateUIFromPrompt()


	-- 開幕のFadeInを実行.
	tweenPlayAll(tweensForFadeIn)


	-- 後始末を行う関数を定義.
	local function cleanup()
		-- イベント接続を破棄.
		for _, v in ipairs(connections) do
			v:Disconnect()
		end

		-- FadeOut.
		tweenPlayAll(tweensForFadeOut)
		task.wait(fadeDuration)

		-- 破棄.
		promptUI:Destroy()
	end

	-- 破棄関数を返す.
	return cleanup
end

return promptUIController
ButtonImageProvider
ButtonImageProvider
--!strict

-- ProximityPromptとProximityPromptInputTypeの情報から、適切なボタンアイコンを複製して、
-- FadeのTweenと一緒に返すメソッドを持つモジュールです.

-- Service
local TweenService = game:GetService("TweenService")
local UserInputService = game:GetService("UserInputService")

-- ボタンアイコン画像.
local GamepadButtonImage = {
	[Enum.KeyCode.ButtonX] = "rbxasset://textures/ui/Controls/xboxX.png",
	[Enum.KeyCode.ButtonY] = "rbxasset://textures/ui/Controls/xboxY.png",
	[Enum.KeyCode.ButtonA] = "rbxasset://textures/ui/Controls/xboxA.png",
	[Enum.KeyCode.ButtonB] = "rbxasset://textures/ui/Controls/xboxB.png",
	[Enum.KeyCode.DPadLeft] = "rbxasset://textures/ui/Controls/dpadLeft.png",
	[Enum.KeyCode.DPadRight] = "rbxasset://textures/ui/Controls/dpadRight.png",
	[Enum.KeyCode.DPadUp] = "rbxasset://textures/ui/Controls/dpadUp.png",
	[Enum.KeyCode.DPadDown] = "rbxasset://textures/ui/Controls/dpadDown.png",
	[Enum.KeyCode.ButtonSelect] = "rbxasset://textures/ui/Controls/xboxView.png",
	[Enum.KeyCode.ButtonStart] = "rbxasset://textures/ui/Controls/xboxmenu.png",
	[Enum.KeyCode.ButtonL1] = "rbxasset://textures/ui/Controls/xboxLB.png",
	[Enum.KeyCode.ButtonR1] = "rbxasset://textures/ui/Controls/xboxRB.png",
	[Enum.KeyCode.ButtonL2] = "rbxasset://textures/ui/Controls/xboxLT.png",
	[Enum.KeyCode.ButtonR2] = "rbxasset://textures/ui/Controls/xboxRT.png",
	[Enum.KeyCode.ButtonL3] = "rbxasset://textures/ui/Controls/xboxLS.png",
	[Enum.KeyCode.ButtonR3] = "rbxasset://textures/ui/Controls/xboxRS.png",
	[Enum.KeyCode.Thumbstick1] = "rbxasset://textures/ui/Controls/xboxLSDirectional.png",
	[Enum.KeyCode.Thumbstick2] = "rbxasset://textures/ui/Controls/xboxRSDirectional.png",
}

-- キーボードアイコン画像.
local KeyboardButtonImage = {
	[Enum.KeyCode.Backspace] = "rbxasset://textures/ui/Controls/backspace.png",
	[Enum.KeyCode.Return] = "rbxasset://textures/ui/Controls/return.png",
	[Enum.KeyCode.LeftShift] = "rbxasset://textures/ui/Controls/shift.png",
	[Enum.KeyCode.RightShift] = "rbxasset://textures/ui/Controls/shift.png",
	[Enum.KeyCode.Tab] = "rbxasset://textures/ui/Controls/tab.png",
}

-- キーボード特殊文字アイコン画像.
local KeyboardButtonIconMapping = {
	["'"] = "rbxasset://textures/ui/Controls/apostrophe.png",
	[","] = "rbxasset://textures/ui/Controls/comma.png",
	["`"] = "rbxasset://textures/ui/Controls/graveaccent.png",
	["."] = "rbxasset://textures/ui/Controls/period.png",
	[" "] = "rbxasset://textures/ui/Controls/spacebar.png",
}

-- 一部キーコードとテキストのマッピング.
local KeyCodeToTextMapping = {
	[Enum.KeyCode.LeftControl] = "Ctrl",
	[Enum.KeyCode.RightControl] = "Ctrl",
	[Enum.KeyCode.LeftAlt] = "Alt",
	[Enum.KeyCode.RightAlt] = "Alt",
	[Enum.KeyCode.F1] = "F1",
	[Enum.KeyCode.F2] = "F2",
	[Enum.KeyCode.F3] = "F3",
	[Enum.KeyCode.F4] = "F4",
	[Enum.KeyCode.F5] = "F5",
	[Enum.KeyCode.F6] = "F6",
	[Enum.KeyCode.F7] = "F7",
	[Enum.KeyCode.F8] = "F8",
	[Enum.KeyCode.F9] = "F9",
	[Enum.KeyCode.F10] = "F10",
	[Enum.KeyCode.F11] = "F11",
	[Enum.KeyCode.F12] = "F12",
}

local ButtonIconProvider = {}

export type ButtonImageTable = {
	ButtonImage: ImageLabel,
	TweensForFadeIn: {Tween},
	TweensForFadeOut: {Tween}
}

-- 適切なボタンアイコンを複製し、FadeのTweenと一緒に返すメソッド.
function ButtonIconProvider:GetButtonImage(prompt: ProximityPrompt, inputType: Enum.ProximityPromptInputType, tweenInfo: TweenInfo): ButtonImageTable?
	local buttonImage
	local tweensForFadeOut = {}
	local tweensForFadeIn = {}

	-- ボタンアイコンの生成.
	-- タッチ入力の場合.
	if inputType == Enum.ProximityPromptInputType.Touch then
		-- タッチ入力用のボタンアイコンを作成.
		buttonImage = script:WaitForChild("TouchButtonImage"):Clone()

	-- ゲームパッド入力の場合.
	elseif inputType == Enum.ProximityPromptInputType.Gamepad then
		-- ProximityPromptに紐づけられているGamepadのKeyCodeに応じた画像のボタンアイコンを作成.
		if GamepadButtonImage[prompt.GamepadKeyCode] then
			buttonImage = script:WaitForChild("GamepadButtonImage"):Clone()
			buttonImage.Image = GamepadButtonImage[prompt.GamepadKeyCode]
		end

	-- それ以外(キーボード入力)の場合.
	else
		-- キーボード入力用のボタンアイコンのベースを作成.
		buttonImage = script:WaitForChild("KeyButtonImage"):Clone()		

		-- ProximityPromptに紐づけられているKeyCodeを文字列で取得.
		local buttonTextString = UserInputService:GetStringForKeyCode(prompt.KeyboardKeyCode)

		-- 専用画像を用いるキーボード用ボタンアイコン画像を取得.
		local buttonTextImage = KeyboardButtonImage[prompt.KeyboardKeyCode]

		-- 取得できなかった場合、特殊文字用のボタンアイコン画像を取得.
		if buttonTextImage == nil then
			buttonTextImage = KeyboardButtonIconMapping[buttonTextString]
		end

		-- さらに取得できなかった場合、専用画像を用いないキーとしての処理を行う.
		if buttonTextImage == nil then
			-- Ctrlなどの一部のキーコード用の文字列を取得しなおす.
			if KeyCodeToTextMapping[prompt.KeyboardKeyCode] then
				buttonTextString = KeyCodeToTextMapping[prompt.KeyboardKeyCode]
			end
		end

		-- この時点で画像が取得できている(専用画像を用いるキーコードだった)場合.
		if buttonTextImage then
			local icon = script:WaitForChild("KeyIconImage"):Clone()
			icon.Image = buttonTextImage

			-- アイコン部分用の表示と終了のTweenを作成.
			table.insert(tweensForFadeOut, TweenService:Create(icon, tweenInfo, { ImageTransparency = 1 }))
			table.insert(tweensForFadeIn, TweenService:Create(icon, tweenInfo, { ImageTransparency = icon.ImageTransparency }))

			-- 初期の透明度を1に.
			icon.ImageTransparency = 1

			-- ButtonImageの子にする.
			icon.Parent = buttonImage

		-- 専用画像を用いないキーで、文字列が取得できている場合.
		elseif buttonTextString ~= nil and buttonTextString ~= "" then
			local buttonText = script:WaitForChild("KeyIconText"):Clone()
			buttonText.Text = buttonTextString

			-- 二文字以上ならTextSizeを12に下げる.
			if string.len(buttonTextString) > 2 then
				buttonText.TextSize = 12
			end

			-- アイコン部分用の表示と終了のTweenを作成.
			table.insert(tweensForFadeOut, TweenService:Create(buttonText, tweenInfo, { TextTransparency = 1 }))
			table.insert(tweensForFadeIn, TweenService:Create(buttonText, tweenInfo, { TextTransparency = buttonText.TextTransparency }))

			-- 初期の透明度を1に.
			buttonText.TextTransparency = 1

			-- ButtonImageの子にする.
			buttonText.Parent = buttonImage

		else
			-- 非対応のキーコードなので破棄して終了.
			buttonImage:Destroy()
			return nil
		end
	end

	-- ボタンアイコン用の表示と終了のTweenを作成.
	if buttonImage then
		table.insert(tweensForFadeOut, TweenService:Create(buttonImage, tweenInfo, { ImageTransparency = 1 }))
		table.insert(tweensForFadeIn, TweenService:Create(buttonImage, tweenInfo, { ImageTransparency = buttonImage.ImageTransparency }))

		-- 初期の透明度を1に.
		buttonImage.ImageTransparency = 1

	else
		-- ボタンアイコンを生成できていなかったら終了.
		return nil
	end

	-- tableにして返す.
	local result = {
		ButtonImage = buttonImage,
		TweensForFadeIn = tweensForFadeIn,
		TweensForFadeOut = tweensForFadeOut
	}::ButtonImageTable

	return result
end

return ButtonIconProvider

5. まとめ

  • ProximityPromptInputTypeは、Touch, Gamepad, Keyboardの三種類がある
  • ProximityPrompt:InputHoldBegin, InputHoldEndで、入力があったこと、離されたことをProximityPromptに通知することができる
  • Touch用のアイコンは一種類でOK
  • Gamepad用のアイコンは、ProximityPrompt.GamepadKeyCodeに応じて選ぶ
  • Keyboard用のアイコンは、ProximityPrompt.KeyboardKeyCodeに応じて選び、物によって画像とテキストを使い分ける
  • Roblox Studioの仮想化機能を使う事で、仮想的に特定のデバイスで実行している状態で動作確認ができる
  • コントローラの仮想化をONにすると、キーボード入力を仮想的にコントローラ入力に変換した状態で動作確認ができる

今回は、各ProximityPromptInputTypeへの対応を行いました。
なかなかの物量で大変だったかと思いますが、これでさまざまな入力方式に汎用的に対応できるようになりました。
次回以降にご紹介する機能についても同様なのですが、実際に制作する際は、そのUIで利用したい機能さえあればよいので、ProximityPromptInputTypeの三種類にだけ対応して、表示するキーはProximityPromptに設定しているもののみを用意するなど、取捨選択して必要最低限の機能のものを作るのが良いかと思います。
この記事を参考に、ゲームに合わせたUI制作を行ってみてください!
そして次回は、ObjectTextとActionTextの表示を実装します。
そちらもぜひご覧ください!
最後までお読みいただき、ありがとうございました!

6. 参考

https://create.roblox.com/docs/reference/engine/classes/ProximityPrompt
https://create.roblox.com/docs/reference/engine/classes/ProximityPromptService
https://create.roblox.com/docs/reference/engine/enums/ProximityPromptStyle
https://create.roblox.com/docs/ui/size-modifiers#automatic-sizing
https://create.roblox.com/docs/reference/engine/classes/UserInputService#GetStringForKeyCode

ランド・ホー Roblox開発チーム

Discussion