【Unity】UniRxで.Subscribeしているオブジェクトがシーン遷移でエラーになるときのエラー解決方法[MissingReferenceException: The object of type XXX has been destroyed but you are still trying to access it.]


概要

UniRxで.Subscribeしたオブジェクトがシーン遷移してから戻って呼び出したときに以下のエラーを出していて何のことか分からないので解決方法と原因をまとめました

MissingReferenceException: The object of type 'PlayerIcon' has been destroyed but you are still trying to access it.
Your script should either check if it is null or you should not destroy the object.
UnityEngine.Component.GetComponent[T] () (at <127e81e1cb3441cc97d26b1910daae77>:0)
PlayerIcon.<Start>b__2_1 (System.String playerIcon) (at Assets/Scripts/UI/PlayerIcon.cs:17)
UniRx.Observer+Subscribe`1[T].OnNext (T value) (at Assets/Plugins/UniRx/Scripts/Observer.cs:165)

解決方法

.Subscribeしているところに購読終了の.AddTo(gameObject)を付与し、そのオブジェクトが破棄されたときに自動で購読終了させる

public static ReactiveProperty<string> rpPlayerIcon = new ReactiveProperty<string>("");

void Start()
{
  // .Subscribeしたオブジェクトに.Add(gameObject)を付与すれば解決
  rpPlayerIcon.ObserveEveryValueChanged(_ => _.Value)
      .Subscribe(playerIcon => GetComponent<Image>().sprite = GetPlayerIcon(playerIcon));
      .AddTo(gameObject); //指定のgameObjectが破棄されたらDisposeする
}

原因

別シーンに遷移したときにこれが付与されているオブジェクトが破棄されているのに、.Subscribeしているためエラーになっていたようです。

こちらの記事を参考にさせていただきました! → AddToが必要な理由

このスクリプトをプレイヤーのアイコンを取得するために使用していたのですが、別シーンに遷移してから元のシーンに戻り、画像を変更したときにエラーになりました。

using UnityEngine;
using UnityEngine.UI;
using UniRx;
using System.Linq;

// プレイヤーが自分のアイコンを変更したときに使用
[RequireComponent(typeof(Image))]
public class PlayerIcon : MonoBehaviour
{
    public static ReactiveProperty<string> rpPlayerIcon = new ReactiveProperty<string>("");
    private Sprite[] icons;

    void Start()
    {
        icons = Resources.LoadAll<Sprite>("PlayerIcons/");

        rpPlayerIcon.ObserveEveryValueChanged(_ => _.Value)
            .Subscribe(playerIcon => GetComponent<Image>().sprite = GetPlayerIcon(playerIcon));
            // ↑この.Subscribeがオブジェクトが破棄された後も続いていた
    }

    private Sprite GetPlayerIcon(string playerIcon)
    {
        if (icons.Any(icon => icon.name == playerIcon))
        {
            return Resources.Load<Sprite>("PlayerIcons/" + playerIcon);
        }
        else
        {
            return Resources.Load<Sprite>("PlayerIcons/icon_0");
        }
    }
}

なので解決方法でも記載しましたが、.AddTo(gameObject)そのオブジェクトが破棄されたときに購読停止すればエラーが無くなりました!

破棄されているのに購読していたらエラーになりますよねw
エラー文のままでしたが気づきませんでした。。。

MissingReferenceException: The object of type 'PlayerIcon' has been destroyed but you are still trying to access it.
Your script should either check if it is null or you should not destroy the object.
UnityEngine.Component.GetComponent[T] () (at <127e81e1cb3441cc97d26b1910daae77>:0)
PlayerIcon.<Start>b__2_1 (System.String playerIcon) (at Assets/Scripts/UI/PlayerIcon.cs:17)
UniRx.Observer+Subscribe`1[T].OnNext (T value) (at Assets/Plugins/UniRx/Scripts/Observer.cs:165)

まとめ

.Subscribeした後のオブジェクトの挙動を忘れていたことが原因でした。でも.Add(gameObject)で解決できたので被害は少なかったです。
UniRxはやっぱり難しいですね。エラーにたくさん出会いますが解決して習得していきたいですね。