// Height Adjust Teleport|Locomotion|20020
namespace VRTK
{
using UnityEngine;
///
/// Updates the `x/y/z` position of the SDK Camera Rig with an optional screen fade.
///
///
/// > The Camera Rig can be automatically teleported to the nearest floor `y` position when utilising this teleporter.
///
/// **Script Usage:**
/// * Place the `VRTK_HeightAdjustTeleport` script on any active scene GameObject.
///
/// **Script Dependencies:**
/// * An optional Destination Marker (such as a Pointer) to set the destination of the teleport location.
///
///
/// `VRTK/Examples/007_CameraRig_HeightAdjustTeleport` has a collection of varying height objects that the user can either walk up and down or use the laser pointer to climb on top of them.
///
/// `VRTK/Examples/010_CameraRig_TerrainTeleporting` shows how the teleportation of a user can also traverse terrain colliders.
///
/// `VRTK/Examples/020_CameraRig_MeshTeleporting` shows how the teleportation of a user can also traverse mesh colliders.
///
[AddComponentMenu("VRTK/Scripts/Locomotion/VRTK_HeightAdjustTeleport")]
public class VRTK_HeightAdjustTeleport : VRTK_BasicTeleport
{
[Header("Height Adjust Settings")]
[Tooltip("If this is checked, then the teleported Y position will snap to the nearest available below floor. If it is unchecked, then the teleported Y position will be where ever the destination Y position is.")]
public bool snapToNearestFloor = true;
[Tooltip("If this is checked then the teleported Y position will also be offset by the play area parent Transform Y position (if the play area has a parent).")]
public bool applyPlayareaParentOffset = false;
[Tooltip("A custom raycaster to use when raycasting to find floors.")]
public VRTK_CustomRaycast customRaycast;
protected override void OnEnable()
{
base.OnEnable();
adjustYForTerrain = true;
AdjustForParentOffset();
}
protected override void OnDisable()
{
base.OnDisable();
}
protected override Vector3 GetNewPosition(Vector3 tipPosition, Transform target, bool returnOriginalPosition)
{
Vector3 basePosition = base.GetNewPosition(tipPosition, target, returnOriginalPosition);
if (!returnOriginalPosition)
{
basePosition.y = GetTeleportY(target, tipPosition);
}
return basePosition;
}
protected virtual void AdjustForParentOffset()
{
if (snapToNearestFloor && applyPlayareaParentOffset && playArea != null && playArea.parent != null)
{
Ray ray = new Ray(playArea.parent.position, -playArea.up);
RaycastHit rayCollidedWith;
if (VRTK_CustomRaycast.Raycast(customRaycast, ray, out rayCollidedWith, Physics.IgnoreRaycastLayer, Mathf.Infinity, QueryTriggerInteraction.Ignore))
{
playArea.position = new Vector3(playArea.position.x, playArea.position.y + rayCollidedWith.point.y, playArea.position.z);
}
}
}
protected virtual float GetParentOffset()
{
return (applyPlayareaParentOffset && playArea.parent != null ? playArea.parent.transform.localPosition.y : 0f);
}
protected virtual float GetTeleportY(Transform target, Vector3 tipPosition)
{
float parentOffset = GetParentOffset();
if (!snapToNearestFloor || !ValidRigObjects())
{
return tipPosition.y + parentOffset;
}
float newY = playArea.position.y;
float heightOffset = 0.1f;
//Check to see if the tip is on top of an object
Vector3 rayStartPositionOffset = Vector3.up * heightOffset;
Ray ray = new Ray(tipPosition + rayStartPositionOffset, -playArea.up);
RaycastHit rayCollidedWith;
if (target != null && VRTK_CustomRaycast.Raycast(customRaycast, ray, out rayCollidedWith, Physics.IgnoreRaycastLayer, Mathf.Infinity, QueryTriggerInteraction.Ignore))
{
newY = (tipPosition.y - rayCollidedWith.distance) + heightOffset;
}
return newY + parentOffset;
}
}
}