// Dash Teleport|Locomotion|20030 namespace VRTK { using UnityEngine; using System.Collections; /// /// Event Payload /// /// An array of RaycastHits that the CapsuleCast has collided with. public struct DashTeleportEventArgs { public RaycastHit[] hits; } /// /// Event Payload /// /// this object /// public delegate void DashTeleportEventHandler(object sender, DashTeleportEventArgs e); /// /// Updates the `x/y/z` position of the SDK Camera Rig with a lerp to the new position creating a dash effect. /// /// /// **Script Usage:** /// * Place the `VRTK_DashTeleport` 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/038_CameraRig_DashTeleport` shows how to turn off the mesh renderers of objects that are in the way during the dash. /// [AddComponentMenu("VRTK/Scripts/Locomotion/VRTK_DashTeleport")] public class VRTK_DashTeleport : VRTK_HeightAdjustTeleport { [Header("Dash Settings")] [Tooltip("The fixed time it takes to dash to a new position.")] public float normalLerpTime = 0.1f; [Tooltip("The minimum speed for dashing in meters per second.")] public float minSpeedMps = 50.0f; [Tooltip("The Offset of the CapsuleCast above the camera.")] public float capsuleTopOffset = 0.2f; [Tooltip("The Offset of the CapsuleCast below the camera.")] public float capsuleBottomOffset = 0.5f; [Tooltip("The radius of the CapsuleCast.")] public float capsuleRadius = 0.5f; /// /// Emitted when the CapsuleCast towards the target has found that obstacles are in the way. /// public event DashTeleportEventHandler WillDashThruObjects; /// /// Emitted when obstacles have been crossed and the dash has ended. /// public event DashTeleportEventHandler DashedThruObjects; protected float minDistanceForNormalLerp; protected float lerpTime = 0.1f; protected Coroutine attemptLerpRoutine; public virtual void OnWillDashThruObjects(DashTeleportEventArgs e) { if (WillDashThruObjects != null) { WillDashThruObjects(this, e); } } public virtual void OnDashedThruObjects(DashTeleportEventArgs e) { if (DashedThruObjects != null) { DashedThruObjects(this, e); } } protected override void OnEnable() { base.OnEnable(); minDistanceForNormalLerp = minSpeedMps * normalLerpTime; } protected override void OnDisable() { base.OnDisable(); if (attemptLerpRoutine != null) { StopCoroutine(attemptLerpRoutine); attemptLerpRoutine = null; } } protected override Vector3 SetNewPosition(Vector3 position, Transform target, bool forceDestinationPosition) { return CheckTerrainCollision(position, target, forceDestinationPosition); } protected override Quaternion SetNewRotation(Quaternion? rotation) { if (ValidRigObjects()) { return (rotation != null ? (Quaternion)rotation : playArea.rotation); } return Quaternion.identity; } protected override void StartTeleport(object sender, DestinationMarkerEventArgs e) { base.StartTeleport(sender, e); } protected override void ProcessOrientation(object sender, DestinationMarkerEventArgs e, Vector3 targetPosition, Quaternion targetRotation) { if (ValidRigObjects()) { Vector3 finalPosition = CalculateOffsetPosition(targetPosition, targetRotation); attemptLerpRoutine = StartCoroutine(lerpToPosition(sender, e, playArea.position, finalPosition, playArea.rotation, targetRotation)); } } protected virtual Vector3 CalculateOffsetPosition(Vector3 targetPosition, Quaternion targetRotation) { if (!headsetPositionCompensation) { return targetPosition; } Vector3 playerOffset = new Vector3(headset.position.x - playArea.position.x, 0, headset.position.z - playArea.position.z); Quaternion relativeRotation = Quaternion.Inverse(playArea.rotation) * targetRotation; Vector3 adjustedOffset = relativeRotation * playerOffset; return targetPosition - (adjustedOffset - playerOffset); } protected override void EndTeleport(object sender, DestinationMarkerEventArgs e) { } protected virtual IEnumerator lerpToPosition(object sender, DestinationMarkerEventArgs e, Vector3 startPosition, Vector3 targetPosition, Quaternion startRotation, Quaternion targetRotation) { enableTeleport = false; bool gameObjectInTheWay = false; // Find the objects we will be dashing through and broadcast them via events Vector3 eyeCameraPosition = headset.transform.position; Vector3 eyeCameraPositionOnGround = new Vector3(eyeCameraPosition.x, playArea.position.y, eyeCameraPosition.z); Vector3 eyeCameraRelativeToRig = eyeCameraPosition - playArea.position; Vector3 targetEyeCameraPosition = targetPosition + eyeCameraRelativeToRig; Vector3 direction = (targetEyeCameraPosition - eyeCameraPosition).normalized; Vector3 bottomPoint = eyeCameraPositionOnGround + (Vector3.up * capsuleBottomOffset) + direction; Vector3 topPoint = eyeCameraPosition + (Vector3.up * capsuleTopOffset) + direction; float maxDistance = Vector3.Distance(playArea.position, targetPosition - direction * 0.5f); RaycastHit[] allHits = Physics.CapsuleCastAll(bottomPoint, topPoint, capsuleRadius, direction, maxDistance); for (int i = 0; i < allHits.Length; i++) { gameObjectInTheWay = (allHits[i].collider.gameObject != e.target.gameObject ? true : false); } if (gameObjectInTheWay) { OnWillDashThruObjects(SetDashTeleportEvent(allHits)); } lerpTime = (maxDistance >= minDistanceForNormalLerp ? normalLerpTime : VRTK_SharedMethods.DividerToMultiplier(minSpeedMps) * maxDistance); float elapsedTime = 0f; float currentLerpedTime = 0f; WaitForEndOfFrame delayInstruction = new WaitForEndOfFrame(); while (currentLerpedTime < 1f) { playArea.position = Vector3.Lerp(startPosition, targetPosition, currentLerpedTime); playArea.rotation = Quaternion.Lerp(startRotation, targetRotation, currentLerpedTime); elapsedTime += Time.deltaTime; currentLerpedTime = elapsedTime / lerpTime; yield return delayInstruction; } playArea.position = targetPosition; playArea.rotation = targetRotation; if (gameObjectInTheWay) { OnDashedThruObjects(SetDashTeleportEvent(allHits)); } base.EndTeleport(sender, e); gameObjectInTheWay = false; enableTeleport = true; } protected virtual DashTeleportEventArgs SetDashTeleportEvent(RaycastHit[] hits) { DashTeleportEventArgs e; e.hits = hits; return e; } } }