🔌

【Roblox】プラグイン作成

2024/10/22に公開

はじめに

Robloxではプラグイン機能を使ってUnityのエディタ拡張機能と同じようなものを作ることができます。
今回はRobloxでプラグインの使い方について紹介していきたいと思います。

Robloxバージョン:0.638.1.6380615

プラグイン

Robloxのプラグインは通常のスクリプトと同様にScriptにプログラムを書いて作成します。

作成したプラグインはローカルに保存して同じPC上のすべてのプロジェクトで使えるようにするなら「ローカルのプラグインとして保存」、Roblox上に公開してほかのPCからも使えるようにするなら「プラグインとして公開」を選択するとプラグインとして使用できるようになります。

機能紹介

①ツールバーのプラグインタブにボタンを追加する

ツールバーにボタンを追加して、押したときに特定の処理を実行することができます。この機能はのちに紹介する機能のON・OFFを切り替えることにも使えるのでプラグインを作成する場合のほぼ必須機能になります。

-- 新しいツールバーセクションを作成します。
local toolbar = plugin:CreateToolbar("Custom Script Tools")

-- 作成したツールバーセクションにボタンを追加します。
-- 第1引数:ボタンと一緒に表示される機能名
-- 第2引数:カーソルを合わせたときの説明文
-- 第3引数:ボタンに表示されるImageのID
local PluginButton = toolbar:CreateButton("Sample Plugin", "This is SamplePlugin!", "rbxassetid://1234567890")

PluginButton.ClickableWhenViewportHidden = true

local function onPluginButtonClickid()
    -- ボタンを押したときに実行する処理
end

-- ボタンのクリックに処理を接続する
PluginButton.Click:Connect(onPluginButtonClicked)

②ウィジェットGUIを作成する

Studio上にプラグイン用のウィンドウを作成して、そこにGuiObjectsを埋め込むことができます。この機能を使ってより詳細な設定が必要になるような処理を実装することができます。

-- ウィジェットGUIを作成するための情報を設定
local widgetInfo = DockWidgetPluginGuiInfo.new(
    Enum.InitialDockState.Float,  -- Widget will be initialized in floating panel
    false,   -- 初期有効化状態
    false,  -- 前回のセッション状態に上書きするか
    200,    -- Float時のデフォルトの幅
    300,    -- Float時のデフォルトの高さ
    150,    -- ウィンドウの最小の幅
    150     -- ウィンドウの最小の高さ
)

-- ウィジェットGUIを作成する
-- 第1引数:pluginGuiId
-- 第2引数:WidgetPluginGuiInfo
local WidgetGui = plugin:CreateDockWidgetPluginGui("SampleWidget", widgetInfo)
WidgetGui.Title = "Sample Widget"

上の方法だけでウィジェットGUIを作成することはできますが、このままだとStudioを開いたときに最初に表示されて閉じてしまうともう一度表示する手段がありません。
なので①の方法と合わせることでウィジェットGUIの表示を自分の任意のタイミングで切り替えることができます。

local toolbar = plugin:CreateToolbar("Custom Script Tools")
local PluginButton = toolbar:CreateButton("Sample Plugin", "This is SamplePlugin!", "rbxassetid://1234567890")

PluginButton.ClickableWhenViewportHidden = true

--[[=========================
ここに上記のスクリプトを記載
===========================]]

local function onPluginButtonClickid()
    WidgetGui.Enabled = not WidgetGui.Enabled
end

local function onWidgetCloseButtonClicked()
    --そのままだとボタンが押されたままの状態になっているので手動で切り替える
    PluginButton.Enabled = true
end

PluginButton.Click:Connect(onPluginButtonClicked)

-- ウィジェットGUIの閉じるボタンを押したときの処理をオーバーライドする
WidgetGui:BindToClose(onWidgetCloseButtonClicked)

これでウィジェットGUIの表示切替ができるようになりました。
GuiObjectsを配置するにはここで作成したウィジェットGUIの子にGuiObjectを配置するだけです。

③コンテキストメニューを作成する

Studioで指定のアクションをした際にコンテキストメニューを表示することができます。

-- ActionIDはプラグインごとに1つしか同じものが使えないので新しく作成する
local plugin = plugin or getfenv().PluginManager():CreatePlugin()
plugin.Name = "Plugin"
plugin.Parent = workspace

-- プラグインタブのボタンを押したときにコンテキストを表示するようにしたいためボタンを作成
local toolbar = plugin:CreateToolbar("Custom Script Tools")
local PluginButton = toolbar:CreateButton("Sample Plugin", "This is SamplePlugin!", "rbxassetid://1234567890")

PluginButton.ClickableWhenViewportHidden = true

-- プラグインメニューを作成する(メインメニュー)
-- 第1引数:メニューの一意のID
-- 第2引数:サブメニューとして使用したときに表示されるテキスト
-- (第3引数:サブメニューとして使用するときに表示されるアイコン)
local pluginMenu = plugin:CreatePluginMenu(math.random(), "Test Menu")
pluginMenu.Name = "Test Menu"

-- メインメニューに新規アクションを追加する
-- 第1引数:一意のアクションID
-- 第2引数:メニューに表示されるテキスト
-- 第3引数:メニューに表示されるアイコン
-- 戻り値:作成されたプラグインアクション
local actionA = pluginMenu:AddNewAction("ActionA", "A", "rbxassetid://1234567890")
local actionB = pluginMenu:AddNewAction("ActionB", "B", "rbxassetid://1234567890")

--サブメニューを作成する
local subMenu = plugin:CreatePluginMenu(math.random(), "C", "rbxassetid://1234567890")
subMenu.Name = "Sub Menu"
--サブメニューに新規アクションを追加する
local actionD = subMenu:AddNewAction("ActionD", "D", "rbxassetid://1234567890")
--サブメニューをメインメニューに追加
pluginMenu:AddMenu(subMenu)

--メインメニューにセパレータを追加
pluginMenu:AddSeparator()
local actionE = pluginMenu:AddNewAction("ActionE", "E", "rbxassetid://1234567890")

-- プラグインタブのボタンを押したときの処理
local function onPluginButtonClicked()
    -- メインメニューを表示して選択されたアクションを取得
    local selectedAction = pluginMenu:ShowAsync()
    if selectedAction then
        -- 選択されたアクション毎の処理
        if selectedAction == actionA then
            print("ActionA")
        elseif selectedAction == actionB then
            print("ActionB")
        elseif selectedAction == actionD then
            print("ActionD")
        elseif selectedAction == actionE then
            print("ActionE")
        end
    else
        print("User did not select an action!")
    end
end

PluginButton.Click:Connect(onPluginButtonClicked)

これでコンテキストメニューを表示して処理を実装することができました。
選択されたアクションの判別のところでActionIdを使えばいいのではと思われるかもしれませんが、なぜかプラグインアクションを作る際に指定したActionIdとプラグインアクションの中身のActionIdが別のものになってしまうので、今回はプラグインアクションインスタンス自体を比較して判別しています。
例:AcitonEのActionIdがcloud_1234567890_ActionEのようになっている。
おそらく保存場所についての要素が追加されているとは思われますが、わざわざsplitしたり、findで文字列検索をおこなったりしないとActionIdでの判別はできないのと精度がすこし落ちるかと感じます。

④データを保存する

DataStoreのようにRobloxのプラグイン上でデータを保持したいときに使用できるものが用意されています。

local SETTING_KEY = "SampleKey"
-- 保存されているデータを取得する
local Data = plugin:GetSetting(SETTING_KEY)

-- データを保存する
-- データはJSON形式で保存されます。
plugin:SetSetting(SETTING_KEY, Data)

⑤Pluginでよく使う機能

プラグインを作成するうえでよく使用する機能の中から使用頻度が高そうなものを抜粋して紹介します。

1.Selection

Studio上で選択しているインスタンスを制御することができます。

local Selection = game:GetService("Selection")

-- Studio上で選択しているインスタンスを取得します。
local selectedObjects = Selection:Get()

-- 引数に渡したインスタンスの配列を選択状態にします。直前の選択状態を上書きします。
Selection:Set({Instance})

-- 引数に渡したインスタンスを選択状態に追加します。
Selection:Add(Instance)

-- 引数に渡したインスタンスの配列の選択状態を解除します。
Selection:Remove({Instance})

2.ChangeHistoryService

プラグインがプロジェクトに加えた変更を記録し、変更前の状態に戻すことができます。

local ChangeHistoryService = game:GetService("ChangeHistoryService")

-- 元に戻すポイントまたはやり直しポイントとして使用できる新しいウェイポイントを設定します。
-- 引数は任意のウェイポイント名
ChangeHistoryService:SetWaypoint("Test Waypoint")

-- ウェイポイントが存在する最後のアクションまで元に戻します。
ChangeHistoryService:Undo()

-- 最後に元に戻されたアクションを実行します。
ChangeHistoryService:Redo()

--元に戻せるアクションがあるかを返し、ある場合は最後のアクションを返します。
local action = ChangeHistoryService:GetCanUndo()

--やり直し可能なアクションがあるかを返し、ある場合は最後のアクションを返します。
local action = ChangeHistoryService:GetCanRedo()

-- 履歴をクリアし、ウェイポイントをすべて削除します
ChangeHistoryService:ResetWaypoints()

3.StudioService

RobloxStudioの設定へのアクセスを提供します。

local StudioService = game:GetService("StudioService")

-- エクスプローラーウィンドウに表示されるアイコンを取得します。
local icon = StudioService:GetClassIcon(ClassName)
local iconImageID = icon.Image
local iconRectOffset = icon.ImageRectOffset
local iconRectSize = icon.ImageRectSize

-- StudioにログインしているユーザーのIDを取得します
local loggedInUserId = StudioService:GetUserId()

-- Studioを使用しているユーザーに1つのファイルを選択するように要求し、そのファイルを読み込みます。
-- 第1引数:ファイルタイプフィルター。指定したファイルタイプのもののみ選択できるようになります。
-- 戻り値:インポートされたファイル
local importFile = StudioService:PromptImportFile({"png", "jpg"})

-- Studioを使用しているユーザーに1つ以上のファイルを選択するように要求し、そのファイルを読み込みます。
-- 第1引数:ファイルタイプフィルター。指定したファイルタイプのもののみ選択できるようになります。
--戻り値:インポートされたファイルの配列
local importFile = StudioService:PromptImportFiles({"png", "jpg"})

まとめ

今回はRobloxでプラグインを作成するための方法を紹介しました。RobloxではUnityやUnrealEngineと比べ制約が多く、また使い勝手も悪いところが多いので自作でプラグインを作成するなどして効率よく開発できる環境を整えるのが大切だと思います。

お読みいただきありがとうございました。

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

Discussion