世間ではUniRxがarchiveされR3への移行が行われ始めているそうです。
I have started distributing an evolved version of UniRx in Cysharp/R3, please use it instead of UniRx.
自分は仕事でも個人開発でもUniRxにはお世話になっていました。自作アプリの開発をしていたのでR3に関する情報は傍目に見るだけでしたが、リリースしてひと段落したタイミングでR3への移行を行いました。
所要時間
自作アプリでも System.IObservable<T>
や UniRx.IReadonlyProperty<T>
は結構使ってますし、MessagePipe
も使ってます。
そんなプロジェクトですが、4時間程の作業でUniRxからR3へ移行できました。
しかし、とりあえずコンパイルエラーが通ってEditor実行で動いている風なのを確認しただけで、ちゃんと動作確認するとまだ修正点があるかもしれません。
やったこと
やったことを順序だてて解説します。説明の都合で実際の手順と異なる場合があります。
作業当時の環境は以下でした
- Unity: 2022.3.17f1
- UniRx: 7.1.0
- R3: 1.1.7
- MessagePiep: 1.7.4
1. R3のインストール
こちらの記事を参考にインストールします。 zenn.dev
そしてインストールが自分的に一番の落とし穴だったのですが、ちゃんと記事の通りにインストールしましょう。 上記記事には
以下のスクショのようにUnity NuGet内の R3 (NuGet) とOpenUPM内の R3 からインストールします。
とあります。自分はUnity NuGetからだけR3をインストールして満足しており、後の作業で using R3.Triggers;
ができずに30分ぐらい悩んでました。
R3.Triggers
はUnity依存の実装であり、OpenUPMの方のR3から取得する必要があるようです。また、プロジェクトでasmdefを切っている場合は R3.Unity
への参照を追加する必要があります。
なお、R3本体の方はdllなのでasmdefに参照を追加する必要はなく、勝手に参照できるようになります。
また、このときMessagePipeをUnity NuGetの方に乗り換えようとしたのですが、VContainerとの参照でコンパイルエラーが出たので止めました。 (GitHub経由のインストールのままとしました)
2. UniRxをR3に置換する
"using UniRx" を一気に "using R3" に置換してしまいます。当然コンパイルエラーが出ますがこれを一つずつ直していくことになります。
3. UniRxを消す
UniRxをpackage manager からremoveします。UniRxとR3は同名の拡張メソッドを多数持つので早めにかぶりを無くしてしまった方が良いでしょう。 プロジェクトのasmdefに追加したUniRxの参照もMissingになるので削除しておきましょう。 同時に代わりではないですがasmdefの参照に R3.Unity を追加しておきましょう。 これでいくらかコンパイルエラーが減るかと思います。
4. IReadOnlyReactiveProperty
を変える
筆者は UniRx.IReadOnlyReactiveProperty
を公開用に多用してました。R3ではinterfaceは消えてしまったので R3.ReadOnlyReactiveProperty
を使います。
これも "IReadOnlyReactiveProperty" を "ReadOnlyReactiveProperty" に置換してしまっていいでしょう。
ただ、UniRx.IReadOnlyReactiveProperty.Value
は R3.ReadOnlyReactiveProperty.CurrentValue
となります。
5. IObservable
を変える
System.IObservable
のinterfaceを使うことが多かったと思いますが、R3ではクラスの Observable
を使います。
変数名が置換対象ならないよう、型名のみ置換されるように "IObservable<" を "Observable<" に置換しました。
6. TakeUntillDestroy
を消す
オブジェクトの破棄時に購読を止める際、有名なのは .AddTo(this)
ですが、プロジェクトによっては .TakeUntillDestroy(this)
を推奨している場合もあると思います。
R3では TakeUntillDestroy
はないようなので、AddTo
や RegisterTo
に移行します。
筆者は RegisterTo
を使いましたが、渡すのが this
や gameObject
でなく destroyCancellationToken
になります。
7. ToUniTask
を FirstAsync
に変える
筆者のプロジェクトでは IObservable<T>
の最初の値を待ち受けるために
await observable.ToUniTask(true, cancellationToken: ct);
のように書いていました。同様の処理は FirstAsync
で書けるようです
await observable.FirstAsync(cancellationToken: ct).AsUniTask();
8. EveryFixedUpdate
を変える
Observable.EveryFixedUpdate()
は直接呼べなくなっています。Observable.EveryUpdate
に UnityFrameProvider.FixedUpdate
を渡してやることで FixedUpdate
を購読できます。また、フレーム数をとるには Index()
を購読する必要があるようでした。
-Observable.EveryFixedUpdate() +Observable.EveryUpdate(UnityFrameProvider.FixedUpdate) + .Index() .Subscribe(i => {
9. ObserveEveryValueChanged
を変える
文章では説明しずらいですが ObserveEveryValueChanged
の書き方が変わりました。
これはとり先生の説明を見てもらえればわかるかと思います
10. MessagePipe.ISubscriber<T>
の変換
先ほどのとり先生の記事には
(この記事の執筆時点では MessagePipe -> R3.Observable の変換メソッドは存在していませんでした。将来的に実装される可能性があります)
とありましたが、下記のように .AsObservable()ToObservable()
を挟むと一応 MessagePipe.ISubscriber
に関しては R3.Observable
に変換でき、Select
などを挟むことが出来るようになりました。
using MessagePipe; using R3; using UnityEngine; using VContainer; public class SubscriberToR3 : MonoBehaviour { [Inject] ISubscriber<int> _subscriber = default; void Start() { _subscriber .AsObservable() .ToObservable() .Select(x => x * 2) .Subscribe(x => Debug.Log(x)) .RegisterTo(destroyCancellationToken); } }
あとやること
自分のプロジェクトでは上記でコンパイルエラーはなくなりました。
ただ、Prefabやシーンのオブジェクトに UniRx.ObservableEventTrigger
をアタッチしていた場合は先のUniRxの削除により Missing になっているはずなので、R3.ObservableEventTrigger
に差し替えてやりましょう。
後は折角R3にアップデートしたので新しい機能を使いましょう。とりあえず SubscribeAwait
を積極的に使いたいと思います
-.Subscribe(_ => UniTask.Void(async () => +.SubscribeAwait(async (_, ct) =>