ワール ドに「スイッチを押したら雰囲気が変わる」を作りたくなります。照明が落ちて、BGMが止まって、閉店の札が出る。
ところが3つを別々に書くと、すぐにずれます。ライトは消えたのに音が鳴り続ける。もう一度押したら、今度は札だけが残る。押すたびに、どれかが取り残されます。
この記事では、1つの状態から3つの反応をそろえて切り替える 書き方を解説します。
この記事でわかること
- 「状態は1つ、反応は複数」という書き方
- 起動時に見た目をそろえておく理由
- ボタン自身を消してしまう失敗の避け方
Light.enabledとSetActiveの使い分け
変数と参照 を試した状態から始めます。
状態は1つ、反応は3つ
ずれる原因は、いつも同じです。それぞれの反応が、自分の状態を持ってしまう からです。
ライトが「点いているか」を覚え、音が「鳴っているか」を覚え、札が「出ているか」を覚える。3つの記憶が別々にあると、どこかで食い違います。
解決は単純です。覚えるのは1つだけにします。

isOn という真偽の値を1つ持ち、その値を見て3つの見た目を作ります。押されたら isOn を反転して、また3つを作り直します。
こうすると、どこから何度呼んでも結果が同じになります。「いまの状態を、見た目に反映するだけ」の処理だからです。

この形は、このあと何度も出てきます。同期を入れるときも、途中参加の人に状態を伝えるときも、土台はこれと同じです。
実践:照明・音・表示をまとめて切り替える
ボタンを1回押すと、ライトが点き、BGMが流れ、壁の札が現れます。もう一度押すと、3つとも同時に消えます。
1. ボタンと3つの対象を置く
床のあるシーンに4つ置きます。
| 名前 | 作り方と設定 |
|---|---|
RoomButton | Cube。Position (0, 1, 1)、Scale (0.4, 0.4, 0.4) |
RoomLight | Light → Point Light。Position (0, 2.5, 0)、Range 6、Intensity 2、Mode「Realtime」 |
RoomSign | Cube。Position (0, 1.8, 2.8)、Scale (1.2, 0.5, 0.05) |
RoomAudio | Create Empty に Audio Source を追加。Position (0, 1, 0) |

RoomAudio の Audio Source には、ループする短い音を入れます。Play On Awake は必ずオフ にしてください。オンのままだと、ワールドに入った瞬間から鳴り始めて、ボタンの意味がなくなります。Loopはオンにします。
ライトのModeは Realtime にします。Bakedだと、実行中に消しても焼き込んだ光が残ります。
2. コードを書く
Assets/Scripts で「Create → U# Script」を選び、LocalRoomToggle という名前で作ります。
using UdonSharp;
using UnityEngine;
[UdonBehaviourSyncMode(BehaviourSyncMode.None)]
public class LocalRoomToggle : UdonSharpBehaviour
{
[SerializeField] private Light targetLight; // 照明
[SerializeField] private GameObject targetVisual; // 表示する札
[SerializeField] private AudioSource targetAudio; // BGM
[SerializeField] private bool startOn; // 起動時にONにするか
private bool isOn; // 覚えるのはこの1つだけ
private bool ready;
private void Start()
{
ready = targetLight != null && targetVisual != null && targetAudio != null;
if (!ready)
{
Debug.LogWarning("[LocalRoomToggle] 3つの対象を指定してください。");
return;
}
// ボタン自身や親を消すと、押せなくなってしまう
if (targetVisual == gameObject || transform.IsChildOf(targetVisual.transform))
{
ready = false;
Debug.LogWarning("[LocalRoomToggle] 表示対象にボタン自身や親を指定しないでください。");
return;
}
isOn = startOn;
ApplyState(); // 起動時に、見た目を状態へそろえる
}
public override void Interact()
{
if (!ready) return;
isOn = !isOn;
ApplyState();
Debug.Log("[LocalRoomToggle] isOn=" + isOn);
}
// 状態を見て、3つの見た目を作る。何度呼んでも同じ結果になる
private void ApplyState()
{
targetLight.enabled = isOn;
targetVisual.SetActive(isOn);
if (isOn)
{
if (!targetAudio.isPlaying) targetAudio.Play();
}
else
{
targetAudio.Stop();
}
}
}
見どころが3つあります。
ApplyState() に反応をまとめています。 ここには「切り替える」処理を書きません。いまの isOn を見て、そのとおりの見た目を作る だけです。だから起動時にも押されたときにも、同じ関数を呼べます。
音は isPlaying を確認してから鳴らします。 すでに鳴っているときに Play() を呼ぶと、頭から鳴り直してしまいます。ループするBGMでは、これがぶつ切りに聞こえます。
ボタン自身を対象にしていないか確認しています。 targetVisual にボタン自身を入れると、押した瞬間に自分が消えて、二度と押せなくなります。この確認があると、設定ミスのときに理由がConsoleに出ます。
3. 参照を割り当てる
RoomButton に Udon Behaviour を追加し、Program Source に LocalRoomToggle を指定します。
| 欄 | 指定するもの |
|---|---|
| Target Light | Hierarchyの RoomLight |
| Target Visual | Hierarchyの RoomSign |
| Target Audio | Hierarchyの RoomAudio |
| Start On | オフ(消えた状態から始める) |
| Interaction Text | 部屋の設定を切り替える |
