Adaptation and use of PrefabImagePairManager script

Hello.

I posted before about wanting to have a prefab and it’s relevant UI loaded/deloaded when a reference image is scanned. I have now adapted the PrefabImagePairManager script to try and solve this and have linked it to a singleton that I have developed. In the PrefabImagePairManager script, the function OnTrackedImagesChanged has been adapted as follows:

void OnTrackedImagesChanged(ARTrackedImagesChangedEventArgs eventArgs)
{
    foreach (var trackedImage in eventArgs.added)
    {
        // Give the initial image a reasonable default scale
        var minLocalScalar = Mathf.Min(trackedImage.size.x, trackedImage.size.y) / 2;
        trackedImage.transform.localScale = new Vector3(minLocalScalar, minLocalScalar, minLocalScalar);
        AssignPrefab(trackedImage);
    }
    foreach (var trackedImage in eventArgs.updated)
    {
        if (m_Instantiated[trackedImage.referenceImage.guid])
        {
            if (trackedImage.trackingState == TrackingState.Limited) //TrackingState.Limited is called to test if an image is outside of the camera's point of view
            {
                Debug.Log("Image lost");
                if (ARManager.instance.footballSpawned == true)
                {
                    ARManager.instance.RemoveFootballContainer();
                }
                else if (ARManager.instance.cathedralSpawned == true)
                {
                    ARManager.instance.RemoveCathedralContainer();
                }
                else if (ARManager.instance.laptopSpawned == true)
                {
                    ARManager.instance.RemoveLaptopContainer();
                }
            }
            break;
        }
       
        // Give the initial image a reasonable default scale
        var minLocalScalar = Mathf.Min(trackedImage.size.x, trackedImage.size.y) / 2;
        trackedImage.transform.localScale = new Vector3(minLocalScalar, minLocalScalar, minLocalScalar);
        AssignPrefab(trackedImage);
    }
}

Equally, the ARManager script that it refers to is detailed here:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.XR.ARFoundation.samples;

public class ARManager : MonoBehaviour
{
    public GameObject cathedralImage;
    public GameObject laptopImage;
    public GameObject footballImage;
    public GameObject cathedralContainer;
    public GameObject laptopContainer;
    public GameObject footballContainer;
    public bool footballSpawned;
    public bool laptopSpawned;
    public bool cathedralSpawned;
    public Button cathedralButton;
    public Button laptopButton;
    public Button footballButton;
    public PrefabImagePairManager prefabImagePairManager;
    public static ARManager instance;
    private void Awake()
    {
        if(instance != null && instance != this)
        {
            Destroy(this); //already have one in scene
        }
        else
        {
            instance = this;
        }
    }
    private void Start()
    {
        footballSpawned = false;
        laptopSpawned = false;
        cathedralSpawned = false;
        //footballButton.onClick.AddListener(() => RemoveFootballContainer());
        //laptopButton.onClick.AddListener(() => RemoveLaptopContainer());
        //cathedralButton.onClick.AddListener(() => RemoveCathedralContainer());
    }
    private void Update()
    {
        if (footballSpawned)
        {
            footballContainer.SetActive(true);
        }
        else if(laptopSpawned)
        {
            laptopContainer.SetActive(true);
        }
        else if(cathedralSpawned)
        {
            cathedralContainer.SetActive(true);
        }
    }

    public void RemoveFootballContainer()
    {
        footballSpawned = false;
        Debug.Log(footballSpawned);
        foreach(var gb in prefabImagePairManager.m_Instantiated)
        {
            if (gb.Value.gameObject.tag.Equals("Football"))
            {
                Destroy(gb.Value.gameObject);
            }
        }
    }
    public void RemoveLaptopContainer()
    {
        laptopSpawned = false;
        Debug.Log(laptopSpawned);
        foreach (var gb in prefabImagePairManager.m_Instantiated)
        {
            if (gb.Value.gameObject.tag.Equals("Laptop"))
            {
                Destroy(gb.Value.gameObject);
            }
        }
    }
    public void RemoveCathedralContainer()
    {
        cathedralSpawned = false;
        Debug.Log(cathedralSpawned);
        foreach (var gb in prefabImagePairManager.m_Instantiated)
        {
            if (gb.Value.gameObject.tag.Equals("Angel"))
            {
                Destroy(gb.Value.gameObject);
            }
        }
    }
}

The plan for this code is, when scanning a marker image, display the relevant marker prefab, as well as the text and button associated with the prefab as well (as each prefab has text related to it). Then, when this marker is scanned again, ideally the same prefab should spawn with the same information again. My current issue however is that it seems like I am getting a nullexceptionreference after scanning the object for the first time and then removing it from the scene. I am checking what prefab is assign and what prefab is deleted, and it seems to be that the prefab assigned in the inspector is the one assigning the prefab and the prefab deleted is a clone of this prefab. I’m not sure why I’m getting this error so I was hoping someone might be able to explain what is happening and potentially offer a better solution.

We have a sample scene for this that you could use as reference: GitHub - Unity-Technologies/arfoundation-samples: Example content for Unity projects based on AR Foundation