今日だけ頑張るんだ

主に軽めの開発記録

自作アプリでUniRxからR3に乗り換えるときにコンパイルが通るまでにやったこと

世間ではUniRxがarchiveされR3への移行が行われ始めているそうです。

github.com

I have started distributing an evolved version of UniRx in Cysharp/R3, please use it instead of UniRx.

github.com

自分は仕事でも個人開発でもUniRxにはお世話になっていました。自作アプリの開発をしていたのでR3に関する情報は傍目に見るだけでしたが、リリースしてひと段落したタイミングでR3への移行を行いました。

naninunenoy.hatenablog.com

所要時間

自作アプリでも 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.ValueR3.ReadOnlyReactiveProperty.CurrentValue となります。

5. IObservable を変える

System.IObservable のinterfaceを使うことが多かったと思いますが、R3ではクラスの Observable を使います。 変数名が置換対象ならないよう、型名のみ置換されるように "IObservable<" を "Observable<" に置換しました。

6. TakeUntillDestroy を消す

オブジェクトの破棄時に購読を止める際、有名なのは .AddTo(this) ですが、プロジェクトによっては .TakeUntillDestroy(this) を推奨している場合もあると思います。 R3では TakeUntillDestroy はないようなので、AddToRegisterTo に移行します。 筆者は RegisterTo を使いましたが、渡すのが thisgameObject でなく destroyCancellationToken になります。

7. ToUniTaskFirstAsync に変える

筆者のプロジェクトでは IObservable<T> の最初の値を待ち受けるために

await observable.ToUniTask(true, cancellationToken: ct);

のように書いていました。同様の処理は FirstAsync で書けるようです

await observable.FirstAsync(cancellationToken: ct).AsUniTask();

8. EveryFixedUpdate を変える

Observable.EveryFixedUpdate() は直接呼べなくなっています。Observable.EveryUpdateUnityFrameProvider.FixedUpdate を渡してやることで FixedUpdate を購読できます。また、フレーム数をとるには Index() を購読する必要があるようでした。

-Observable.EveryFixedUpdate()
+Observable.EveryUpdate(UnityFrameProvider.FixedUpdate)
+  .Index()
   .Subscribe(i => {

9. ObserveEveryValueChanged を変える

文章では説明しずらいですが ObserveEveryValueChanged の書き方が変わりました。 これはとり先生の説明を見てもらえればわかるかと思います

qiita.com

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) =>