Hello! I’m working on project, which detects some images. I made a quite simple script, which allows me to scan 5 images and then place 5 corresponding 3D prefabs according to every image. Also there is some text which shows name of scanned image, if this process was succesful or limited tracking if we not scan apprpriate images. Basically script looks like this: `
using UnityEngine;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;
using System.Collections.Generic;
using TMPro;
public class MultipleImageTracker : MonoBehaviour
{
[System.Serializable]
public struct TrackableImage
{
public string imageName;
public GameObject prefabToInstantiate;
}
public List<TrackableImage> trackableImages;
public GameObject statusTextPrefab;
public float statusDisplayTime = 5f;
public Canvas arCanvas;
private ARTrackedImageManager trackedImageManager;
private Dictionary<string, GameObject> spawnedPrefabs = new Dictionary<string, GameObject>();
private Dictionary<string, GameObject> statusTexts = new Dictionary<string, GameObject>();
private Dictionary<string, TrackingState> lastTrackingStates = new Dictionary<string, TrackingState>();
private GameObject currentActiveStatusText;
private void Awake()
{
trackedImageManager = FindObjectOfType<ARTrackedImageManager>();
if (arCanvas == null)
{
Debug.LogError("AR Canvas is not assigned!");
}
}
private void OnEnable()
{
trackedImageManager.trackedImagesChanged += OnTrackedImagesChanged;
}
private void OnDisable()
{
trackedImageManager.trackedImagesChanged -= OnTrackedImagesChanged;
}
private void OnTrackedImagesChanged(ARTrackedImagesChangedEventArgs eventArgs)
{
foreach (var trackedImage in eventArgs.added)
{
UpdateImage(trackedImage);
}
foreach (var trackedImage in eventArgs.updated)
{
UpdateImage(trackedImage);
}
foreach (var trackedImage in eventArgs.removed)
{
string imageName = trackedImage.referenceImage.name;
if (spawnedPrefabs.ContainsKey(imageName))
{
Destroy(spawnedPrefabs[imageName]);
spawnedPrefabs.Remove(imageName);
}
if (statusTexts.ContainsKey(imageName))
{
Destroy(statusTexts[imageName]);
statusTexts.Remove(imageName);
}
lastTrackingStates.Remove(imageName);
}
}
private void UpdateImage(ARTrackedImage trackedImage)
{
string imageName = trackedImage.referenceImage.name;
Vector3 position = trackedImage.transform.position;
TrackableImage trackableImage = trackableImages.Find(x => x.imageName == imageName);
if (trackableImage.prefabToInstantiate != null)
{
UpdateSpawnedPrefab(trackableImage, imageName, position, trackedImage.trackingState);
UpdateStatusText(imageName, trackedImage.trackingState);
}
}
private void UpdateSpawnedPrefab(TrackableImage trackableImage, string imageName, Vector3 position, TrackingState trackingState)
{
if (spawnedPrefabs.ContainsKey(imageName))
{
spawnedPrefabs[imageName].transform.position = position;
spawnedPrefabs[imageName].SetActive(trackingState == TrackingState.Tracking);
}
else
{
GameObject prefabInstance = Instantiate(trackableImage.prefabToInstantiate, position, Quaternion.identity);
prefabInstance.AddComponent<TouchInputHandler>();
spawnedPrefabs[imageName] = prefabInstance;
}
}
private void UpdateStatusText(string imageName, TrackingState currentTrackingState)
{
if (!lastTrackingStates.ContainsKey(imageName) || lastTrackingStates[imageName] != currentTrackingState)
{
ShowStatusText(imageName, currentTrackingState);
lastTrackingStates[imageName] = currentTrackingState;
}
}
private void ShowStatusText(string imageName, TrackingState trackingState)
{
if (currentActiveStatusText != null)
{
StopAllCoroutines();
currentActiveStatusText.SetActive(false);
}
GameObject statusTextObject;
if (!statusTexts.ContainsKey(imageName))
{
statusTextObject = Instantiate(statusTextPrefab, arCanvas.transform);
statusTexts[imageName] = statusTextObject;
RectTransform rectTransform = statusTextObject.GetComponent<RectTransform>();
rectTransform.anchorMin = new Vector2(1, 1);
rectTransform.anchorMax = new Vector2(1, 1);
rectTransform.anchoredPosition = new Vector2(-200, -200);
rectTransform.sizeDelta = new Vector2(200, 50);
}
else
{
statusTextObject = statusTexts[imageName];
}
TextMeshProUGUI tmp = statusTextObject.GetComponent<TextMeshProUGUI>();
if (tmp != null)
{
switch (trackingState)
{
case TrackingState.Tracking:
tmp.color = Color.green;
tmp.text = $"Recognized: {imageName}";
break;
case TrackingState.Limited:
tmp.color = Color.yellow;
tmp.text = "Tracking limited...";
break;
case TrackingState.None:
tmp.color = Color.red;
tmp.text = "Lost tracking";
break;
}
}
statusTextObject.SetActive(true);
currentActiveStatusText = statusTextObject;
StartCoroutine(HideStatusText(imageName));
}
private System.Collections.IEnumerator HideStatusText(string imageName)
{
yield return new WaitForSeconds(statusDisplayTime);
if (statusTexts.ContainsKey(imageName) && statusTexts[imageName] == currentActiveStatusText)
{
statusTexts[imageName].SetActive(false);
currentActiveStatusText = null;
}
}
}
Now I want to add some interactions with my prefabs that appears according to images. If I touch prefab, it should scale up two times and go back to normal size, like simple animation. If I touch again that prefab, it stops animating and returns to started scale. I tried something like this:
using UnityEngine;
public class TouchInputHandler : MonoBehaviour
{
private bool isScaled = false;
void Update()
{
if (Input.touchCount > 0 && Input.touches[0].phase == TouchPhase.Began)
{
Ray ray = Camera.main.ScreenPointToRay(Input.touches[0].position);
RaycastHit[] hits = Physics.RaycastAll(ray);
Debug.Log("Touch detected");
foreach (RaycastHit hit in hits)
{
Debug.Log($"Raycast hit: {hit.transform.name}");
if (hit.transform == transform)
{
Debug.Log($"Object {hit.transform.name} was touched");
if (!isScaled)
{
StartCoroutine(ScaleObject(transform, 2f, 0.5f));
}
else
{
StartCoroutine(ScaleObject(transform, 1f, 0.5f));
}
isScaled = !isScaled;
}
}
}
}
private System.Collections.IEnumerator ScaleObject(Transform obj, float targetScale, float duration)
{
Vector3 originalScale = obj.localScale;
Vector3 destinationScale = originalScale * targetScale;
float currentTime = 0.0f;
while (currentTime <= duration)
{
obj.localScale = Vector3.Lerp(originalScale, destinationScale, currentTime / duration);
currentTime += Time.deltaTime;
yield return null;
}
obj.localScale = destinationScale;
}
}
But nothing happens. All my prefabs have Box Collider, I added AR Raycast Manager to my scene and check I guess everything, but nothing works.
Can anybody help me to fix this problem, that my images will do animation?
Updated code for TouchInputHandler (renamed to TouchScaler):
using UnityEngine;
using System.Collections;
using UnityEngine.XR.ARFoundation;
using System.Collections.Generic;
public class TouchScaler : MonoBehaviour
{
private Vector3 originalScale;
public float scaleFactor = 2.0f;
public float scaleSpeed = 2.0f;
private bool isAnimating = false;
private Coroutine scaleCoroutine;
private ARRaycastManager arRaycastManager;
// public float overlapSphereRadius = 0.1f; // Increased default radius
private void Start()
{
originalScale = transform.localScale;
Debug.Log($"TouchScaler: Started on {gameObject.name}. Original scale: {originalScale}");
arRaycastManager = FindObjectOfType<ARRaycastManager>();
if (arRaycastManager == null)
{
Debug.LogError("TouchScaler: ARRaycastManager not found in the scene.");
}
AdjustColliderSize();
}
private void AdjustColliderSize()
{
BoxCollider boxCollider = GetComponent<BoxCollider>();
if (boxCollider != null)
{
Renderer renderer = GetComponent<Renderer>();
if (renderer != null)
{
boxCollider.size = renderer.bounds.size;
boxCollider.center = renderer.bounds.center - transform.position;
Debug.Log($"TouchScaler: Adjusted collider size to {boxCollider.size} for {gameObject.name}");
}
}
}
private void Update()
{
if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Began)
{
Debug.Log($"TouchScaler: Touch detected on {gameObject.name}");
Vector3 touchPosition = Input.GetTouch(0).position;
HandleInteraction(touchPosition);
}
// if (Input.GetMouseButtonDown(0))
// {
// Debug.Log($"TouchScaler: Mouse click detected on {gameObject.name}");
// Vector3 mousePosition = Input.mousePosition;
// HandleInteraction(mousePosition);
// }
}
private void HandleInteraction(Vector3 screenPosition)
{
Ray ray = Camera.main.ScreenPointToRay(screenPosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
Debug.Log($"TouchScaler: Raycast hit {hit.collider.gameObject.name}");
if (hit.collider.gameObject == this.gameObject)
{
Debug.Log($"TouchScaler: Raycast hit this object: {gameObject.name}");
ToggleScaling();
}
}
else
{
Debug.Log($"TouchScaler: No hit detected for {gameObject.name}");
}
}
private void ToggleScaling()
{
Debug.Log($"TouchScaler: ToggleScaling called for {gameObject.name}");
if (isAnimating)
{
StopScaling();
}
else
{
StartScaling();
}
}
private void StartScaling()
{
Debug.Log($"TouchScaler: StartScaling called for {gameObject.name}");
if (scaleCoroutine != null)
{
StopCoroutine(scaleCoroutine);
}
scaleCoroutine = StartCoroutine(ScaleAnimation());
isAnimating = true;
}
private void StopScaling()
{
Debug.Log($"TouchScaler: StopScaling called for {gameObject.name}");
if (scaleCoroutine != null)
{
StopCoroutine(scaleCoroutine);
}
StartCoroutine(ResetScale());
isAnimating = false;
}
private IEnumerator ScaleAnimation()
{
Debug.Log($"TouchScaler: ScaleAnimation started for {gameObject.name}");
while (true)
{
yield return ScaleObject(originalScale * scaleFactor);
yield return ScaleObject(originalScale);
}
}
private IEnumerator ScaleObject(Vector3 targetScale)
{
Debug.Log($"TouchScaler: ScaleObject called for {gameObject.name}. Target scale: {targetScale}");
while (Vector3.Distance(transform.localScale, targetScale) > 0.01f)
{
transform.localScale = Vector3.Lerp(transform.localScale, targetScale, scaleSpeed * Time.deltaTime);
yield return null;
}
transform.localScale = targetScale;
Debug.Log($"TouchScaler: Scaling completed for {gameObject.name}. Current scale: {transform.localScale}");
}
private IEnumerator ResetScale()
{
Debug.Log($"TouchScaler: ResetScale called for {gameObject.name}");
yield return ScaleObject(originalScale);
}
}
So I made some improvements to code, but now the problem is that I can’t stop animation, cause of no hit detected and I even can’t start animation if I go back to the same image (the animation only works for first time for any image and then everything breaks). Here are my logs:
