I am trying to write two scripts, one attached to the HoloLensCamera, where it updates the size/scale of a canvas as the user (i.e. the hololens camera) moves back and forth, so that it always stays as big, and the reverse; and the other attached to any game object, as a generalization of the first script, to work on any scene object.
I thought (late)updating the height of the UI canvas to the holo cam’s frustum height would make sense, at least initially, so that the menu always takes up exactly the vertical height of the camera’s view. In the second (more general) script, I basically re-scale the game object.
The following shows my work so far, but I need help. First, I would like to know if this is the right approach. Second, I would like to know how to re-size any child objects (e.g. canvas elements) so that the growing and shrinking is consistent across the entire canvas and panels and buttons, etc… In general, I would appreciate help in getting this right.
/// <summary>
/// Updates the size/scale of a canvas as the user
/// (i.e. the hololens camera) moves back and forth.
/// This script should be attached to the HoloLensCamera.
/// It will re-size the canvas to fit the frustum height,
/// or whatever specified portion of it.
/// </summary>
public class CanvasScaler : MonoBehaviour
{
/// <summary>
/// In the Inspector panel, drag and drop the UI
/// element whose size you would like to adjust.
/// Initially, this is set to the UI Canvas.
/// </summary>
public RectTransform _rectTransform;
/// <summary>
/// The width-to-height ratio of the UI element.
/// Initially the UI Canvas.
/// </summary>
private float _ratio;
/// <summary>
/// Pixels per Unit for converting units to pixels.
/// </summary>
private float _pixPerUnit = 100f;
/// <summary>
/// Th gaze origin which is the user's head position
/// as well as the HoloLensCamera' location in the world.
/// </summary>
private Vector3 _gazeOrigin;
/// <summary>
/// The UI element's origin. Initially the UI Canvas.
/// </summary>
private Vector3 _uiOrigin;
/// <summary>
/// The HoloLensCamera's (vertical) frustum height.
/// The new re-sized Canvas height should be set to this,
/// or some variation of it. Here, it is 1.5f instead of 2f
/// that is used in the calculation formula taken from Unity Docs.
/// </summary>
private float _frustumHeight;
/// <summary>
/// The distance from the HoloLensCamera
/// to the UI element. Initially the UI Canvas.
/// </summary>
private float _distance;
/// <summary>
/// Whether or not you want the size/scale re-adjusting.
/// </summary>
public bool _reSizeCanvas;
private void Start()
{
Debug.Log("Canvas width: " + _rectTransform.rect.width + " Canvas height: " + _rectTransform.rect.height);
// In the Inspector Panel, check this option if
// you would like the UI size-adjusting to work.
if (_reSizeCanvas)
{
Debug.Log("UI size-adjusting in progress!");
//Vector2 initialSize = _rectTransform.sizeDelta;
_ratio = _rectTransform.rect.width / _rectTransform.rect.height;
}
}
private void LateUpdate()
{
_gazeOrigin = transform.position;
_uiOrigin = _rectTransform.position;
_distance = Vector3.Distance(_gazeOrigin, _uiOrigin);
// Calculate the new holoLens camera's (vertical) frustum height,
// so that you can set the new re-sized Canvas height to this value.
_frustumHeight = 1.5f * _distance * Mathf.Tan(this.GetComponent<Camera>().fieldOfView * 0.5f * Mathf.Deg2Rad);
// Lerp from initial size to new size based on the distance change.
// Find the new height from the camera frustum and the new width from the
// initial canvas ratio, so that the width-to-height ratio always stays the same.
_rectTransform.sizeDelta = new Vector2(_ratio * _frustumHeight * _pixPerUnit, _frustumHeight * _pixPerUnit);
}
}
/// <summary>
/// Updates the size/scale of an object as the user
/// (i.e. the hololens camera) moves back and forth.
/// This script should be attached to the game object.
/// </summary>
public class ObjectScaler : MonoBehaviour
{
/// <summary>
/// In the Inspector panel, drag and drop the
/// intended object that needs to be re-sized.
/// </summary>
[SerializeField]
public GameObject _renderCamera;
private Vector3 _startingScale;
private float _startingDistance;
private float _currentDistance;
void Start()
{
var objectTransformPos = this.transform.position;
var cameraTransformPos = _renderCamera.transform.position;
_startingDistance = Vector3.Distance(cameraTransformPos, objectTransformPos);
_startingScale = this.transform.localScale;
// min scale ?
}
void LateUpdate()
{
_currentDistance = Vector3.Distance(_renderCamera.transform.position, this.transform.position);
this.transform.localScale = _startingScale * (_currentDistance / _startingDistance);
}
}