仕掛けを10個ほど作ったあたりから、ワールドが重くなってきました。飾りは増やしていないのに、フレームレートだけが落ちています。
Udonのコードは、見た目はふつうのC#です。ただ、実行のされ方が違います。 翻訳された命令を1つずつ解釈しながら動くので、1行のコストが想像より高くつきます。
だからこそ効くのは、書き方を凝ることではありません。何回呼ぶかを減らすこと です。この記事では、毎フレームの処理をイベントに置き換えます。
この記事でわかること
- 毎フレーム確かめる処理をイベントに置き換える
- 取得した結果を覚えておく効果
- 同期の送信回数を減らす考え方
- 直したあと、測って確かめる方法
メソッドとカスタムイベント と 重さを測る を読んだ状態から始めます。
毎秒60回、同じ質問をしない
重いUdonのコードには、共通の形があります。
private void Update()
{
// 近づいたか? を毎フレーム確かめる
float d = Vector3.Distance(player.GetPosition(), transform.position);
doorAnimator.SetBool("IsOpen", d < 3f);
}
書いてあることは正しいのですが、1秒に60回、同じ質問をしています。 実際に答えが変わるのは、人が近づいた瞬間と離れた瞬間の2回だけです。

置き換え先は、たいていすでに用意されています。
| 毎フレーム確かめていること | 置き換えるイベント |
|---|---|
| 範囲に人がいるか | OnPlayerTriggerEnter / OnPlayerTriggerExit |
| 掴まれているか | OnPickup / OnDrop |
| ボタンが押されたか | Interact |
| 同期の値が変わったか | OnDeserialization |
| 誰かが入ってきたか | OnPlayerJoined / OnPlayerLeft |
「誰かが触った」「誰かが入った」「値が届いた」。 この3種類で、ワールドのUpdateの大半は消せます。
もう1つ、「いまは待って、あとで1回だけ」という処理もあります。これはUpdateで数えるのではなく、予約で書きます。
// 3秒後に1回だけ閉める
SendCustomEventDelayedSeconds(nameof(CloseDoor), 3f);
Updateで経過時間を足していくより短く、負荷もかかりません。詳しくは 時間差で処理を呼ぶ を参照してください。
Updateを使ってよい場面もあります。 回り続ける看板、揺れ続ける水面のように、毎フレーム本当に変わり続けるものです。避けたいのは「変わったかどうかを確かめる」使い方だけです。
同じものを何度も探さない
もう1つ、Udonで特に高くつくものがあります。探す処理 です。
private void Update()
{
// 毎フレーム探している
GetComponent<AudioSource>().volume = 0.5f;
}
GetComponent は、そのオブジェクトの部品を1つずつ見て回る処理です。ネイティブのC#でも避けたいところですが、Udonでは特に高くつきます。

答えは単純で、Start() で1回取って、フィールドに覚えておきます。
private AudioSource cachedAudio;
private void Start()
{
cachedAudio = GetComponent<AudioSource>(); // 1回だけ
}
覚えておきたいのは、この3つです。
| 高くつくもの | どうするか |
|---|---|
GetComponent | Start() で取って覚える |
GameObject.Find 系 | 使わない。インスペクタで割り当てる |
| 文字列をつなげる処理 | 表示が変わるときだけ作る |
インスペクタで割り当てられるものは、コードで探さない。 これが基本の姿勢です。
実践:距離を測るドアを、イベントに置き換える
毎フレーム距離を測る自動ドアを作って、測って、イベントに置き換えて、また測ります。
