Using Touch to Manipulate AR Objects in Unity

Paul Killman
Antaeus AR
Published in
4 min readMar 20, 2024

In a previous article, I described how to create a way to examine AR Objects using Unity. In this article, I will extend the functionality of the object examiner by adding the ability to rotate the object and a way to set the scale of the object while it’s being examined. If you haven’t done so, I highly recommend reading the previous article.

Previously, I already had AR Translation, Scale, and Rotation Interactable components attached to the Placement Prefab Cube. For this article, I have disabled them.

I have two scripts that allow us to examine an AR Object. The first script — ObjectExaminer — is attached to the object that we wish to examine. The second script — ObjectExaminerManager — is attached to an empty game object called Object Examiner Manager.

I added the ability to determine if the screen was being touched while we were examing an object. If the screen is being touched, rotate the object according to how the touch is moving. This is all currently in the Update method but should probably be refactored into its own method.

    private void Update()
{
if (_isExamining) // Are we examining an object?
{
if (Input.touchCount > 0) // One or more touches on screen
{
Touch touch = Input.GetTouch(0); // Read data on first finger to touch

if (touch.phase == TouchPhase.Moved) // Finger moved on screen
{
Vector3 rotation = new Vector3(touch.deltaPosition.x, touch.deltaPosition.y, 0) * _rotationSpeed;
_currentExaminedObject.transform.Rotate(rotation);
}
}
}
}

Most of the changes in this script are in the Update method. It checks to see if we are currently examing an object, and then looks to see if the screen is being touched. If so, it reads the touch input information for the first finger to touch the screen. ( Touch touch = Input.GetTouch(0); ) Then, it rotates the examined object according to how we move our finger.

The next ability I added was to change the scale of the object while it was being examined. The cube prefab that I used is a good size for an examined object because it fills up most of the screen without going outside of the boundaries. However, other future objects will almost certainly be larger or smaller than the cube.

To fix this, I added a variable to the ObjectExaminer script called examineScaleOffset and assigned its default value to one. I made the variable a SerializeField so that it could be easily modified.

    /*
* Set a new scale for ObjectExaminerManager
* so that object fits screen when examined
*/
[SerializeField]
public float examineScaleOffset = 1;

Now that we have this variable, we can use it in the ObjectExaminerManager script. In the PerformExamine method, I added a way to cache the original scale value and a couple of lines of code to change the scale of the object to whatever value was assigned to it by the ObjectManager script.

/*
* Cache original transform info and then
* move object to Examine Target position
*/
public void PerformExamine(ObjectExaminer objectExaminer)
{
Vector3 offsetScale;

// Cache position, rotation and scale
_currentExaminedObject = objectExaminer;
_cachedPosition = objectExaminer.transform.position;
_cachedRotation = objectExaminer.transform.rotation;
_cachedScale = objectExaminer.transform.localScale;

// Assign _currentExaminedObject position, rotation and scale
_currentExaminedObject.transform.position = _examineTarget.position;
_currentExaminedObject.transform.parent = _examineTarget;
// Change scale of _currentExaminedObject to fit screen
offsetScale = _cachedScale * objectExaminer.examineScaleOffset;
_currentExaminedObject.transform.localScale = offsetScale;

_isExamining = true;
}

Then, in the PerformUnexamine method, I set the scale back to the cached value.

    /*
* Return object to its original position, rotation and scale
*/
public void PerformUnexamine()
{
_currentExaminedObject.transform.position = _cachedPosition;
_currentExaminedObject.transform.rotation = _cachedRotation;
_currentExaminedObject.transform.localScale = _cachedScale;
_currentExaminedObject.transform.parent = null;
_currentExaminedObject = null;

_isExamining = false;
}

When I tried it out, it worked. Here is the complete code for both scripts.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/*
* This is called by the
* AR Selection Interactable
* Select event
*/
public class ObjectExaminer : MonoBehaviour
{
/*
* Set a new scale for ObjectExaminerManager
* so that object fits screen when examined
*/
[SerializeField]
public float examineScaleOffset = 1;

private ObjectExaminerManager _objectExaminerManager;

void Start()
{
_objectExaminerManager = FindObjectOfType<ObjectExaminerManager>();

if(_objectExaminerManager == null )
{
Debug.LogError("Object Examiner Manager Not Found!");
}
}

/*
* Request for the object to be
* examined when selected - Select event from
* AR Selection Interactable on object
*/
public void RequestExamine()
{
_objectExaminerManager.PerformExamine(this);
}

/*
* Request for the object to be
* unexamined when unselected - Select Exited event from
* AR Selection Interactable on object
*/
public void RequestUnexamine()
{
_objectExaminerManager.PerformUnexamine();
}
}
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;

public class ObjectExaminerManager : MonoBehaviour
{
[SerializeField]
private Transform _examineTarget;
[SerializeField]
private float _rotationSpeed = 1f;

private Vector3 _cachedPosition;
private Quaternion _cachedRotation;
private Vector3 _cachedScale;
private ObjectExaminer _currentExaminedObject;
private bool _isExamining = false;

private void Update()
{
if (_isExamining) // Are we examining an object?
{
if (Input.touchCount > 0) // One or more touches on screen
{
Touch touch = Input.GetTouch(0); // Read data on first finger to touch

if (touch.phase == TouchPhase.Moved) // Finger moved on screen
{
Vector3 rotation = new Vector3(touch.deltaPosition.x, touch.deltaPosition.y, 0) * _rotationSpeed;
_currentExaminedObject.transform.Rotate(rotation);
}
}
}
}

/*
* Cache original transform info and then
* move object to Examine Target position
*/
public void PerformExamine(ObjectExaminer objectExaminer)
{
Vector3 offsetScale;

// Cache position, rotation and scale
_currentExaminedObject = objectExaminer;
_cachedPosition = objectExaminer.transform.position;
_cachedRotation = objectExaminer.transform.rotation;
_cachedScale = objectExaminer.transform.localScale;

// Assign _currentExaminedObject position, rotation and scale
_currentExaminedObject.transform.position = _examineTarget.position;
_currentExaminedObject.transform.parent = _examineTarget;
// Change scale of _currentExaminedObject to fit screen
offsetScale = _cachedScale * objectExaminer.examineScaleOffset;
_currentExaminedObject.transform.localScale = offsetScale;

_isExamining = true;
}

/*
* Return object to its original position, rotation and scale
*/
public void PerformUnexamine()
{
_currentExaminedObject.transform.position = _cachedPosition;
_currentExaminedObject.transform.rotation = _cachedRotation;
_currentExaminedObject.transform.localScale = _cachedScale;
_currentExaminedObject.transform.parent = null;
_currentExaminedObject = null;

_isExamining = false;
}
}

--

--