// 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; } } }