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