BGMの音量 を訪問者が調整できるようにしたい。照明の明るさを好みで変えられるようにしたい。
こういう設定パネルを作るには、ワールドの中に浮かぶUIが必要です。ただ、Unityの手順どおりに作ると、エディタでは押せるのにVRChatでは反応しない ことがよくあります。
この記事では、VRChatで確実に押せるパネル を作り、ボタンとスライダーからUdonの処理を呼ぶところまでを解説します。
この記事でわかること
- VRChatでUIを押せるようにする4つの部品
- ボタンからUdonのメソッドを呼ぶ手順
- スライダーの値を受け取る方法
- 押せないときの原因を頻度順に
メソッドとカスタムイベント を試した状態から始めます。
押せるようにするには4つの部品が要る
Unityで作ったUIは、エディタのPlayではマウスで押せます。ところがVRChatに上げると押せません。
VRChatのレーザーポインタを、UnityのUIにつなぐ部品が足りない からです。

必要なのはこの4つです。
| 部品 | 役割 | どこに付けるか |
|---|---|---|
| Canvas(World Space) | UIを置く板 | 自分で作る |
| Graphic Raycaster | ポインタが当たったかを判定する | Canvasと同じ(自動で付く) |
| VRC Ui Shape | VRChatの指し棒をUnityのUIにつなぐ | Canvasと同じ(自分で追加) |
| EventSystem | クリックを配る受け皿 | シーンに1つだけ |
いちばん多い失敗が、VRC Ui Shape の付け忘れ です。これがないと、エディタでは押せてVRChatでは押せない、という分かりにくい状態になります。
追加すると Box Collider が自動で付きます。この Collider が「触れる面」になるので、自分でColliderを足す必要はありません。
もう1つ、レイヤーは Default のままにします。 UI レイヤーにしてはいけません。VRChatクライアント自身のメニューが UI レイヤーを使っているので、そこに置くとワールドのポインタが当たらなくなります。
EventSystemはシーンに1つだけ です。UIを最初に作ったときに自動で追加されます。複数あると動かなくなるので、増えていないか確認してください。
実践:音量と照明の設定パネルを作る
BGMの音量をスライダーで、照明のオン・オフをボタンで操作できるパネルを作ります。
1. パネルを置く
「UI → Canvas」を作り、ControlPanel と名付けます。
| 項目 | 設定 |
|---|---|
| Render Mode | World Space |
| Position | (0, 1.4, 2.8) |
| Rotation | (0, 180, 0) |
| Width / Height | 400 / 300 |
| Scale | (0.005, 0.005, 0.005) |
| Layer | Default |

Scaleを小さくしているのは、World SpaceのCanvasでは1単位が1メートルとして扱われる からです。Widthが400のままだと400mの板になります。0.005倍にすると、幅2m・高さ1.5mの現実的な大きさになります。
「Add Component」で VRC Ui Shape を追加します。これで押せるようになります。
Hierarchyに EventSystem があることも確認してください。なければ、UI要素を作ったときに自動で追加されます。
2. スライダーとボタンを置く
ControlPanel を右クリックして、次を追加します。
- 「UI → Slider」を追加して
VolumeSliderと名付け、パネルの上半分に配置する - 「UI → Button - TextMeshPro」を追加して
LightButtonと名付け、下半分に配置する
Sliderの Min Value を 0、Max Value を 1、Value を 0.12 にします。BGMの音量として使うためです。
ボタンのテキストは「照明を切り替える」にしておきます。
3. 操作を受け取るコードを書く
「Create Empty」で PanelController を作り、(0, 0, 0) に置きます。
Assets/Scripts で「Create → U# Script」を選び、RoomControlPanel を作ります。
using UdonSharp;
using UnityEngine;
using UnityEngine.UI;
[UdonBehaviourSyncMode(BehaviourSyncMode.None)]
public class RoomControlPanel : UdonSharpBehaviour
{
[SerializeField] private AudioSource bgm;
[SerializeField] private Light roomLight;
[SerializeField] private Slider volumeSlider;
private bool lightOn = true;
private void Start()
{
ApplyVolume();
ApplyLight();
}
// スライダーから呼ばれる。引数は取れないので、値は自分で読む
public void OnVolumeChanged()
{
ApplyVolume();
}
// ボタンから呼ばれる
public void ToggleLight()
{
lightOn = !lightOn;
ApplyLight();
Debug.Log("[ControlPanel] light=" + lightOn);
}
private void ApplyVolume()
{
if (bgm == null || volumeSlider == null) return;
bgm.volume = volumeSlider.value;
}
private void ApplyLight()
{
if (roomLight != null) roomLight.enabled = lightOn;
}
}
大事なのは、UIから呼ばれるメソッドが public で引数を取らない ことです。
スライダーの値は引数では渡ってきません。呼ばれた側が volumeSlider.value を 自分で読みに行きます。 そのためにスライダーへの参照を持っています。
4. UIとUdonをつなぐ
PanelController に Udon Behaviour を追加し、RoomControlPanel を指定します。BGM・Room Light・Volume Sliderをそれぞれ割り当てます。
次に、UI側から呼ぶ設定をします。

ボタンの場合:
LightButtonを選び、Inspectorの On Click () の+を押す- 空いた欄に、Hierarchyの
PanelControllerをドラッグする - 右のドロップダウンから UdonBehaviour → SendCustomEvent を選ぶ
- 下のテキスト欄に
ToggleLightと入力する
スライダーの場合:
VolumeSliderを選び、On Value Changed (Single) の+を押すPanelControllerをドラッグする- UdonBehaviour → SendCustomEvent を選ぶ
- テキスト欄に
OnVolumeChangedと入力する
メソッド名は文字で入力するので、つづりを間違えると何も起きません。 コードのメソッド名と1文字ずつ照らし合わせてください。
5. 確かめる
Playして、パネルを操作します。
| 操作 | 期待する結果 |
|---|---|
| ボタンを押す | 照明が消える。Consoleに light=False |
| もう一度押す | 点く |
| スライダーを動かす | BGMの音量が変わる |
| パネルから離れて操作する | 離れすぎると届かない |
ClientSimで押せても油断しないでください。 VRC Ui Shapeの付け忘れは、実際のVRChatで初めて分かります。Build & Testでも確認します。
押せないときの原因
頻度の高い順に並べます。

- VRC Ui Shape が付いていない → Canvasと同じオブジェクトに追加します。エディタで押せてVRChatで押せない場合は、まずここです
- レイヤーが
UIになっている →Defaultに戻します - EventSystemがない、または複数ある → シーンに1つだけにします
- Graphic Raycaster が消えている → Canvasと同じオブジェクトにあるか確認します
- メソッド名のつづりが違う → SendCustomEventに入れた文字とコードを照合します
- Canvasのスケールが極端 → 巨大すぎると当たり判定がずれます。0.005前後に します
Canvasの大きさを後から変えた場合、VRC Ui Shapeが付けたColliderが古いサイズのまま残ることがあります。そのときは、一度外して付け直します。
おまけ:先に知っておくと良いこと
- VRとデスクトップで操作が違う: VRはコントローラのレーザー、デスクトップは画面中央のポインタです。どちらでも押せる大きさにしておきます。ボタンは小さくしすぎないほうが親切です
- これはローカルの処理です: 音量も照明も、操作した人の画面だけが変わります。音量は各自で決められるほうが親切なので、これで正解です。全員に共有したいものは ネットワーク同期入門 へ
- 設定を次回も覚えさせられる: 音量の設定を、次に来たときも保つことができます。PlayerDataで音量を保存する で扱います
- パネルの高さに注意: 座って遊ぶ人は目線が1.1m前後まで下がります。パネルを高く置きすぎると、座ったまま操作できません
- 使いどころはいろいろ: 交流ワールドの音量調整、ゲームの難易度選択、展示ワールドの言語切り替え、イベント会場の照明操作。どれも同じ形で作れます
まとめ
ワールド内UIは、4つの部品がそろって初めて 動きます。
- Canvas・Graphic Raycaster・VRC Ui Shape・EventSystem
- レイヤーは
Default。UIにしない - World Spaceでは1単位が1メートル。Scaleで調整する
- ボタンからは
SendCustomEventでメソッド名を呼ぶ。引数は渡せない
作ったあとの問いかけは、「VRChatでも押せたか」 です。エディタで押せることは、その証明になりません。
設定を次回も覚えさせるなら PlayerDataで音量を保存する、端末ごとの使いやすさを整えるなら 使いやすい設定パネル へ進んでください。