ObserveOnMainThread
1. 概要
ObserveOnMainThread は、上流からの通知(OnNext / OnErrorResume / OnCompleted)を Unity のメインスレッドに切り替えて配信する R3.Unity のオペレーターです。内部的には ObserveOn(UnityFrameProvider.Update) と同義です。
Unity の GameObject、Component、uGUI などは基本的にメインスレッドで操作する必要があります。バックグラウンド処理や別スレッドから値が流れてくる可能性がある場合、ObserveOnMainThread を挟むことで購読側の処理を Unity の Update タイミングへ戻せます。
2. シグネチャ
public static Observable<T> ObserveOnMainThread<T>(
this Observable<T> source)3. マーブルダイアグラム
値自体は変更されません。通知の配信先だけが Unity のメインスレッド(Update)へ切り替わります。
4. サンプルコード
Observable
.FromAsync(async ct => await LoadScoreAsync(ct))
.ObserveOnMainThread()
.Subscribe(score => scoreText.text = score.ToString())
.AddTo(this);5. 補足
SubscribeOnMainThread との違い
ObserveOnMainThread は通知の配信先を変えるだけで、ソースへの購読処理をどこで開始するかは変更しません。購読処理自体を Unity メインスレッドで実行したい場合は SubscribeOnMainThread を使用します。
| 観点 | ObserveOnMainThread | SubscribeOnMainThread |
|---|---|---|
| 変更するもの | OnNext / OnErrorResume / OnCompleted の実行場所 | ソースへの Subscribe と Dispose の実行場所 |
| 主な用途 | GameObject / Component / UI の更新を Unity メインスレッドで行う | 購読開始時に Unity API を触る処理を Unity メインスレッドで開始する |
Subscribe(...) 内のラムダに効くか | 効く | 直接は効かない |
| Unity オブジェクト更新に必要か | 多くの場合こちら | これだけでは不十分 |
特に混同しやすい点は、SubscribeOnMainThread が Subscribe(x => ...) のラムダをメインスレッドで実行するための API ではないことです。Subscribe(x => transform.position = ...) や Subscribe(x => text.text = ...) を Unity メインスレッドで実行したい場合は ObserveOnMainThread を使います。
// GameObject や UI を更新したい場合
observable
.ObserveOnMainThread()
.Subscribe(x => transform.position = x)
.AddTo(this);次のように SubscribeOnMainThread だけを書いても、通知を Unity メインスレッドで受け取る意味にはなりません。
// 誤解しやすい例: Subscribe ラムダの実行場所を変える目的ではない
observable
.SubscribeOnMainThread()
.Subscribe(x => transform.position = x)
.AddTo(this);ObserveOnMainThread は、置いた位置より下流の通知処理に影響します。重い計算はメインスレッドに戻す前に済ませ、Unity オブジェクトに触る直前で ObserveOnMainThread を置くのが基本です。
observable
.Select(x => HeavyComputation(x)) // ここはメインスレッドへ戻す前に実行
.ObserveOnMainThread()
.Subscribe(result => resultText.text = result.ToString())
.AddTo(this);反対に、ObserveOnMainThread の後に重い Select などを書くと、その処理も Unity メインスレッド上で実行され、フレーム落ちの原因になります。
observable
.ObserveOnMainThread()
.Select(x => HeavyComputation(x)) // Unity メインスレッドを止めやすい
.Subscribe(result => resultText.text = result.ToString())
.AddTo(this);購読開始処理も Unity メインスレッドに寄せたい場合は、SubscribeOnMainThread と組み合わせます。
observable
.SubscribeOnMainThread() // 購読開始を Unity メインスレッドへ
.ObserveOnMainThread() // 通知の受け取りも Unity メインスレッドへ
.Subscribe(x => UpdateGameObject(x))
.AddTo(this);