// Dash Teleport|Locomotion|20030
|
|
namespace VRTK
|
|
{
|
|
using UnityEngine;
|
|
using System.Collections;
|
|
|
|
/// <summary>
|
|
/// Event Payload
|
|
/// </summary>
|
|
/// <param name="hits">An array of RaycastHits that the CapsuleCast has collided with.</param>
|
|
public struct DashTeleportEventArgs
|
|
{
|
|
public RaycastHit[] hits;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Event Payload
|
|
/// </summary>
|
|
/// <param name="sender">this object</param>
|
|
/// <param name="e"><see cref="DashTeleportEventArgs"/></param>
|
|
public delegate void DashTeleportEventHandler(object sender, DashTeleportEventArgs e);
|
|
|
|
/// <summary>
|
|
/// Updates the `x/y/z` position of the SDK Camera Rig with a lerp to the new position creating a dash effect.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// **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.
|
|
/// </remarks>
|
|
/// <example>
|
|
/// `VRTK/Examples/038_CameraRig_DashTeleport` shows how to turn off the mesh renderers of objects that are in the way during the dash.
|
|
/// </example>
|
|
[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;
|
|
|
|
/// <summary>
|
|
/// Emitted when the CapsuleCast towards the target has found that obstacles are in the way.
|
|
/// </summary>
|
|
public event DashTeleportEventHandler WillDashThruObjects;
|
|
/// <summary>
|
|
/// Emitted when obstacles have been crossed and the dash has ended.
|
|
/// </summary>
|
|
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;
|
|
}
|
|
}
|
|
}
|