roblox-studio-emulation について
roblox-studio-emulationは、Roblox Studioでモバイル、マルチプレイヤー、境界条件を検証するためのテストスキルです。Studioのメニュー操作、リモートイベントを使ったテスト、検証結果の限界の報告方法を説明します。
主な機能
- 01タッチやデバイス条件のエミュレーション
- 02AppleScriptによるStudioのTestメニュー操作
- 03実際のリモートを使ったマルチプレイヤーのテスト
- 04クライアント属性によるシナリオ設定
- 05実動作・ロジック・未検証の区別
活用シーン
- 01タッチ専用UIの動作確認
- 022人目のクライアントなしでパーティー状態を確認
- 03合成入力で分岐をテスト
MCP 対応
このスキルはRoblox Studio MCPツールと併用します。エミュレーター切り替え後の再接続には、`list_roblox_studios`、`set_active_studio`、`start_stop_play`を参照しています。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ flatbedj/ roblox-skills --skill roblox-studio-emulationインストール後、Claude Code で/roblox-studio-emulationを入力して読み込まれたか確認します。
詳細
ソロプレイテストでは再現できない状況をエミュレートする
MCP ツールだけではテストが不可能に見える種類のテストが 2 つあり、ユーザーに任せたくなるかもしれません。どちらも自分で実施できます。何かのテストにユーザーが必要だと言う前に、ここを確認してください。
通常の開発ループについては roblox-studio-workflow を、MCP ツールボックスについては roblox-studio-mcp を参照してください。
1. デバイスエミュレーション(モバイル/タブレット/タッチ)— Studio のメニューバーから
Studio は macOS のアクセシビリティ機能にウィンドウを公開していないため(count windows → 0)、サイズを変更できません。
しかし、メニューバー全体は公開しているため、Test メニューを操作できます。
# one-time: the user must grant Terminal (or your host app) Accessibility permission
# System Settings → Privacy & Security → Accessibility
# symptom if missing: "osascript is not allowed assistive access. (-1719)"
osascript -e 'tell application "Roblox Studio" to activate'
osascript -e 'tell application "System Events" to tell process "RobloxStudio" \
to click menu item "Device Emulator" of menu 1 of menu bar item "Test" of menu bar 1'
プロセス名は RobloxStudio です(「Roblox Studio」でも「RobloxStudioBeta」でもありません)。
実際に反映されたかどうかを確認してください。決めつけずに、Play を開始して実際の値を読み取ります。
local cam = workspace.CurrentCamera
local UIS = game:GetService("UserInputService")
-- desktop: ~1920x1080, TouchEnabled=false
-- emulated: device-sized viewport, TouchEnabled=TRUE
return cam.ViewportSize, UIS.TouchEnabled
TouchEnabled = true は、タッチ専用のコードパス(サムスティックの配置スペース、タッチレイアウトの分岐、POS_SHOWN オフセット)が有効になったことを示します。
Test メニュー全体(便利な項目):
| 項目 | 備考 |
|---|---|
Device Emulator | エミュレーションの切り替え |
Controller Emulator / Player Emulator | ゲームパッド/プレイヤー状態のエミュレーション |
Start Test Session | Add Clients の前に必要 |
Add Clients | Play Solo では無効。Test Session 内では有効 |
Toggle Client View、Pause Client、End Session | マルチクライアントセッションの操作 |
クリックする前に項目を列挙し、有効状態を確認します。
osascript -e 'tell application "System Events" to tell process "RobloxStudio" \
to get name of every menu item of menu 1 of menu bar item "Test" of menu bar 1'
osascript -e 'tell application "System Events" to tell process "RobloxStudio" \
to get enabled of menu item "Add Clients" of menu 1 of menu bar item "Test" of menu bar 1'
制限事項
- 特定のデバイス(iPhone と iPad など)を選ぶ操作には、ウィンドウ内のドロップダウンを使用しますが、これは AX では公開されていません。現在選択されているデバイスが使われます。特定の幅が必要な場合は、一度ユーザーに設定を依頼してください。レイアウトが実際に崩れるのは、幅の狭いスマートフォンです。
- エミュレーターを切り替えると、現在の Play セッションが終了し、MCP 接続が切れることがあります。
list_roblox_studios→set_active_studioで再接続してから、start_stop_playを実行してください。 - 終了したらエミュレーターを OFF に戻してください。そうしないと、その後のスクリーンショットがすべてタッチデバイスのものになります。
2. 2 つ目のクライアントを使わずにマルチプレイヤー機能をテストする — 実際のリモートを呼び出す
通常、マルチクライアントセッションを起動するよりも速く、正確です。 Server VM と Client VM は DataModel を共有しているため、Server VM からの FireClient は実際のクライアントモジュールに届き、その後の処理(シグナルハンドラー、キャッシュ状態、UI の更新)を実行します。
-- datamodel_type = "Server"
local Remotes = require(game:GetService("ReplicatedStorage"):WaitForChild("Shared").Remotes)
local plr = game.Players:GetPlayers()[1]
-- pretend the server told us we joined someone's party
Remotes.event("PartyUpdated"):FireClient(plr, {
ownerUserId = 99999999, ownerName = "TestHost",
members = { { userId = plr.UserId, name = plr.DisplayName } },
})
次に、Client VM から観測可能なインスタンス状態を検証します(モジュール内部は使わないでください。別の VM であり、require は複製されたモジュールを返します)。
-- datamodel_type = "Client"
local on = 0
for _, p in game:GetService("CollectionService"):GetTagged("Plot") do
for _, d in p:GetDescendants() do
if d:IsA("ProximityPrompt") and d.Name == "PartyPrompt" and d.Enabled then on += 1 end
end
end
return on
必ず往復をテストしてください。「参加」状態を発火して検証し、次に「退出」状態を発火して(FireClient(plr, nil))、元に戻ることを検証します。一方向のテストでは、状態が戻らなくなる問題が隠れてしまいます。これはより深刻なバグです(プロンプトが非表示のままになると、そのプレイヤーにとって機能が使えなくなります)。
前提条件を偽装する
クライアント側の属性書き込みだけでシナリオを設定できます。クライアントコードは同じ属性を読み取り、GetAttributeChangedSignal はローカルで発火するためです。
-- datamodel_type = "Client" — pretend another player owns this plot
workspace.Map.Plots.Plot1:SetAttribute("OwnerUserId", 99999999)
-- ... run the test ...
workspace.Map.Plots.Plot1:SetAttribute("OwnerUserId", nil) -- ALWAYS clean up
これが機能しない場合
偽装したユーザーが**実際にオンラインの Player**であることを必要とするコードは、この方法では偽装できません。例:Players:GetPlayerByUserId(owner) を実行し、このチェックに失敗したものを削除するポーリングループは、設定を直ちに取り消します。症状として、作成したものが 1 秒以内に消えます。
次のいずれかに切り替えてください。
Test ▸ Start Test Session ▸ Add Clientsを使って、本物の 2 人目のプレイヤーを追加する、または- Client VM で合成入力を使って純粋なロジックをユニットテストする。出荷時のガード/分岐をそのまま再現し、テーブルテストを行います。
local cases = {
{ "nil payload", nil, true },
{ "empty string", "", true },
{ "garbage base64", "!!!not-base64!!!", true },
{ "short buffer", Base64.encode(short),true },
{ "valid buffer", Base64.encode(full), false },
}
-- assert blocked == expected for each; report N/N
これは実際に報告できる結果です。ただし、検証したのはロジックであり、描画を含む完全な経路ではないことを明示してください。
正確な報告
毎回、次の 3 つを区別してください。
- 実環境で検証済み — 操作を実行し、その効果を確認した。
- ロジックを検証済み — 合成入力で分岐をテーブルテストした。完全な経路は未テスト。
- 未テスト — その旨を伝え、何が必要かを正確に説明する。
「コンパイルできて見た目も正しい」を、検証済みとして報告してはいけません。