Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question AR Foundation Multiple Image Tracking - How to detect one at a time

Discussion in 'AR' started by SAKDT, Feb 20, 2023.

  1. SAKDT

    SAKDT

    Joined:
    Aug 8, 2022
    Posts:
    4
    Hello, I am using AR foundation 4.2.7 with Unity 2021.3.7f1 for Image tracking.
    I have added Prefab Image Pair Manager to the AR session origin to detect multiple images with different prefabs. When I run the app, the paired prefab will spawn accordingly for each image. The problem for my case is that once an image is detected, the spawned prefab will still stay in the scene. I would like to track one image at a time instead of having multiple prefabs all at once. So when a new image is detected, the previous spawned prefab should be removed in the scene. Could I get help on what to adjust to make it work like this? Thank you.
     
  2. DevDunk

    DevDunk

    Joined:
    Feb 13, 2020
    Posts:
    4,261
    Whenever a prefab is spawned just destroy the previously placed one?
    What did you already try? This doesn't sound too complicated
     
  3. SAKDT

    SAKDT

    Joined:
    Aug 8, 2022
    Posts:
    4
    For now I have a button to reset the ARsession to clear the scene. Could you guide me where in the script I should look into and what I should add? It will be super helpful to understand. Thank you for the quick reply.
     
  4. DevDunk

    DevDunk

    Joined:
    Feb 13, 2020
    Posts:
    4,261
    Easiest is to have 1 singleton manager class with a list of gameobjects) (or just 1 gameobject if that's all ya need).
    Then every time you spawn an object add it to the list via a method (maybe in the Start() of a script on the prefab).
    Then when you call the method to add destroy the old gameobject and add the new one to the list instead.

    If just one, simply find object by tag and destoy in start (and don't destroy the object itself haha). Not the cleanest but gets the job done
     
  5. SAKDT

    SAKDT

    Joined:
    Aug 8, 2022
    Posts:
    4
    Thank you for the reply. Instead, I modified the Prefab Image Pair Manager script and now the app can detect one prefab at a time. But now the issue is, when I want to scan something again, the prefab will not show anymore. So this works for detecting prefabs only once. What should I adjust to keep the ability to view the prefabs no matter if it is the first time or not? For example, I have three images and corresponding prefabs. I can scan each image and see one prefab at a time, but once I try to scan any of the three images again, it wont show anymore. Here is the part that I adjusted.

    Code (CSharp):
    1.     void AssignPrefab(ARTrackedImage trackedImage)
    2.         {
    3.            
    4.                 //destroy previous prefab if there was one
    5.  
    6.                 if (m_CurrentPrefab != null)
    7.                 {
    8.                     Destroy(m_CurrentPrefab);
    9.                     m_CurrentPrefab = null;
    10.                 }
    11.  
    12.                 //initiate the new prefab
    13.                 if (m_PrefabsDictionary.TryGetValue(trackedImage.referenceImage.guid, out var prefab))
    14.                     //   m_Instantiated[trackedImage.referenceImage.guid] = Instantiate(prefab, trackedImage.transform);
    15.                     m_CurrentPrefab = Instantiate(prefab, trackedImage.transform);
    16.            
    17.  
    18.            
    19.         }

    and the full modified Prefab Image Pair Manager script:

    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. #if UNITY_EDITOR
    4. using UnityEditor;
    5. #endif
    6. using UnityEngine;
    7. using UnityEngine.UI;
    8. using UnityEngine.XR.ARSubsystems;
    9. using UnityEngine.XR.ARFoundation;
    10.  
    11. namespace UnityEngine.XR.ARFoundation.Samples
    12. {
    13.     /// <summary>
    14.     /// This component listens for images detected by the <c>XRImageTrackingSubsystem</c>
    15.     /// and overlays some prefabs on top of the detected image.
    16.     /// </summary>
    17.     [RequireComponent(typeof(ARTrackedImageManager))]
    18.     public class PrefabImagePairManager : MonoBehaviour, ISerializationCallbackReceiver
    19.     {
    20.         /// <summary>
    21.         /// Used to associate an `XRReferenceImage` with a Prefab by using the `XRReferenceImage`'s guid as a unique identifier for a particular reference image.
    22.         /// </summary>
    23.        
    24.         private GameObject m_CurrentPrefab;
    25.        
    26.         [Serializable]
    27.         struct NamedPrefab
    28.         {
    29.             // System.Guid isn't serializable, so we store the Guid as a string. At runtime, this is converted back to a System.Guid
    30.             public string imageGuid;
    31.             public GameObject imagePrefab;
    32.  
    33.      
    34.  
    35.             public NamedPrefab(Guid guid, GameObject prefab)
    36.             {
    37.                 imageGuid = guid.ToString();
    38.                 imagePrefab = prefab;
    39.             }
    40.         }
    41.  
    42.         [SerializeField]
    43.         [HideInInspector]
    44.         List<NamedPrefab> m_PrefabsList = new List<NamedPrefab>();
    45.  
    46.         Dictionary<Guid, GameObject> m_PrefabsDictionary = new Dictionary<Guid, GameObject>();
    47.         Dictionary<Guid, GameObject> m_Instantiated = new Dictionary<Guid, GameObject>();
    48.         ARTrackedImageManager m_TrackedImageManager;
    49.  
    50.         [SerializeField]
    51.         [Tooltip("Reference Image Library")]
    52.         XRReferenceImageLibrary m_ImageLibrary;
    53.  
    54.         /// <summary>
    55.         /// Get the <c>XRReferenceImageLibrary</c>
    56.         /// </summary>
    57.         public XRReferenceImageLibrary imageLibrary
    58.         {
    59.             get => m_ImageLibrary;
    60.             set => m_ImageLibrary = value;
    61.         }
    62.  
    63.         public void OnBeforeSerialize()
    64.         {
    65.             m_PrefabsList.Clear();
    66.             foreach (var kvp in m_PrefabsDictionary)
    67.             {
    68.                 m_PrefabsList.Add(new NamedPrefab(kvp.Key, kvp.Value));
    69.             }
    70.         }
    71.  
    72.         public void OnAfterDeserialize()
    73.         {
    74.             m_PrefabsDictionary = new Dictionary<Guid, GameObject>();
    75.             foreach (var entry in m_PrefabsList)
    76.             {
    77.                 m_PrefabsDictionary.Add(Guid.Parse(entry.imageGuid), entry.imagePrefab);
    78.             }
    79.         }
    80.  
    81.         void Awake()
    82.         {
    83.             m_TrackedImageManager = GetComponent<ARTrackedImageManager>();
    84.         }
    85.  
    86.         void OnEnable()
    87.         {
    88.             m_TrackedImageManager.trackedImagesChanged += OnTrackedImagesChanged;
    89.         }
    90.  
    91.         void OnDisable()
    92.         {
    93.             m_TrackedImageManager.trackedImagesChanged -= OnTrackedImagesChanged;
    94.         }
    95.  
    96.         void OnTrackedImagesChanged(ARTrackedImagesChangedEventArgs eventArgs)
    97.         {
    98.            
    99.             foreach (var trackedImage in eventArgs.added)
    100.             {
    101.                 // Give the initial image a reasonable default scale
    102.                 //  var minLocalScalar = Mathf.Min(trackedImage.size.x, trackedImage.size.y) / 2;
    103.  
    104.                 // trackedImage.transform.localScale = new Vector3(minLocalScalar, minLocalScalar, minLocalScalar);
    105.              
    106.                 AssignPrefab(trackedImage);
    107.                
    108.             }
    109.         }
    110.  
    111.         void AssignPrefab(ARTrackedImage trackedImage)
    112.         {
    113.            
    114.                 //destroy previous prefab if there was one
    115.  
    116.                 if (m_CurrentPrefab != null)
    117.                 {
    118.                     Destroy(m_CurrentPrefab);
    119.                     m_CurrentPrefab = null;
    120.                 }
    121.  
    122.                 //initiate the new prefab
    123.                 if (m_PrefabsDictionary.TryGetValue(trackedImage.referenceImage.guid, out var prefab))
    124.                     //   m_Instantiated[trackedImage.referenceImage.guid] = Instantiate(prefab, trackedImage.transform);
    125.                     m_CurrentPrefab = Instantiate(prefab, trackedImage.transform);
    126.            
    127.  
    128.          
    129.         }
    130.  
    131.         public GameObject GetPrefabForReferenceImage(XRReferenceImage referenceImage)
    132.             => m_PrefabsDictionary.TryGetValue(referenceImage.guid, out var prefab) ? prefab : null;
    133.  
    134.         public void SetPrefabForReferenceImage(XRReferenceImage referenceImage, GameObject alternativePrefab)
    135.         {
    136.             m_PrefabsDictionary[referenceImage.guid] = alternativePrefab;
    137.             if (m_Instantiated.TryGetValue(referenceImage.guid, out var instantiatedPrefab))
    138.             {
    139.                 m_Instantiated[referenceImage.guid] = Instantiate(alternativePrefab, instantiatedPrefab.transform.parent);
    140.                 Destroy(instantiatedPrefab);
    141.             }
    142.         }
    143.  
    144. #if UNITY_EDITOR
    145.         /// <summary>
    146.         /// This customizes the inspector component and updates the prefab list when
    147.         /// the reference image library is changed.
    148.         /// </summary>
    149.         [CustomEditor(typeof(PrefabImagePairManager))]
    150.         class PrefabImagePairManagerInspector : Editor
    151.         {
    152.             List<XRReferenceImage> m_ReferenceImages = new List<XRReferenceImage>();
    153.             bool m_IsExpanded = true;
    154.  
    155.             bool HasLibraryChanged(XRReferenceImageLibrary library)
    156.             {
    157.                 if (library == null)
    158.                     return m_ReferenceImages.Count == 0;
    159.  
    160.                 if (m_ReferenceImages.Count != library.count)
    161.                     return true;
    162.  
    163.                 for (int i = 0; i < library.count; i++)
    164.                 {
    165.                     if (m_ReferenceImages[i] != library[i])
    166.                         return true;
    167.                 }
    168.  
    169.                 return false;
    170.             }
    171.  
    172.             public override void OnInspectorGUI()
    173.             {
    174.                 //customized inspector
    175.                 var behaviour = serializedObject.targetObject as PrefabImagePairManager;
    176.  
    177.                 serializedObject.Update();
    178.                 using (new EditorGUI.DisabledScope(true))
    179.                 {
    180.                     EditorGUILayout.PropertyField(serializedObject.FindProperty("m_Script"));
    181.                 }
    182.  
    183.                 var libraryProperty = serializedObject.FindProperty(nameof(m_ImageLibrary));
    184.                 EditorGUILayout.PropertyField(libraryProperty);
    185.                 var library = libraryProperty.objectReferenceValue as XRReferenceImageLibrary;
    186.  
    187.                 //check library changes
    188.                 if (HasLibraryChanged(library))
    189.                 {
    190.                     if (library)
    191.                     {
    192.                         var tempDictionary = new Dictionary<Guid, GameObject>();
    193.                         foreach (var referenceImage in library)
    194.                         {
    195.                             tempDictionary.Add(referenceImage.guid, behaviour.GetPrefabForReferenceImage(referenceImage));
    196.                         }
    197.                         behaviour.m_PrefabsDictionary = tempDictionary;
    198.                     }
    199.                 }
    200.  
    201.                 // update current
    202.                 m_ReferenceImages.Clear();
    203.                 if (library)
    204.                 {
    205.                     foreach (var referenceImage in library)
    206.                     {
    207.                         m_ReferenceImages.Add(referenceImage);
    208.                     }
    209.                 }
    210.  
    211.                 //show prefab list
    212.                 m_IsExpanded = EditorGUILayout.Foldout(m_IsExpanded, "Prefab List");
    213.                 if (m_IsExpanded)
    214.                 {
    215.                     using (new EditorGUI.IndentLevelScope())
    216.                     {
    217.                         EditorGUI.BeginChangeCheck();
    218.  
    219.                         var tempDictionary = new Dictionary<Guid, GameObject>();
    220.                         foreach (var image in library)
    221.                         {
    222.                             var prefab = (GameObject)EditorGUILayout.ObjectField(image.name, behaviour.m_PrefabsDictionary[image.guid], typeof(GameObject), false);
    223.                             tempDictionary.Add(image.guid, prefab);
    224.                         }
    225.  
    226.                         if (EditorGUI.EndChangeCheck())
    227.                         {
    228.                             Undo.RecordObject(target, "Update Prefab");
    229.                             behaviour.m_PrefabsDictionary = tempDictionary;
    230.                             EditorUtility.SetDirty(target);
    231.                         }
    232.                     }
    233.                 }
    234.  
    235.                 serializedObject.ApplyModifiedProperties();
    236.             }
    237.         }
    238. #endif
    239.     }
    240. }
     
    Cyberus likes this.
  6. SAKDT

    SAKDT

    Joined:
    Aug 8, 2022
    Posts:
    4
    I found what I was missing - 'OnTrackedImagesChanged' method was not handling the 'updated' case.
    Below is what I added if anyone wants to modify as I did.

    Code (CSharp):
    1.  void OnTrackedImagesChanged(ARTrackedImagesChangedEventArgs eventArgs)
    2.         {
    3.          
    4.             foreach (var trackedImage in eventArgs.added)
    5.             {
    6.                 AssignPrefab(trackedImage);
    7.             }
    8.  
    9.             foreach (var trackedImage in eventArgs.updated)
    10.             {
    11.                 // If the updated image is currently being tracked, assign its prefab
    12.                 if (trackedImage.trackingState == TrackingState.Tracking)
    13.                 {
    14.                     AssignPrefab(trackedImage);
    15.                 }
    16.             }
    17.  
    18.  
    19.         }
     
    Cyberus and karthikeyuboosa like this.
  7. saadaax

    saadaax

    Joined:
    Feb 14, 2023
    Posts:
    1
    Hey! I'm trying to get something similar working. I'm a complete newbie at Unity and C# so trying to add this script, but keep getting an error "PrefabImagePairManager already defines a member oninspectorGUI with the same parameter types". Could you pleae help me out? Thanks in advance!