// Step Multiplier|Locomotion|20130
|
|
namespace VRTK
|
|
{
|
|
using UnityEngine;
|
|
|
|
/// <summary>
|
|
/// Multiplies each real world step within the play area to enable further distances to be travelled in the virtual world.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// **Optional Components:**
|
|
/// * `VRTK_ControllerEvents` - The events component to listen for the button presses on. This must be applied on the same GameObject as this script if one is not provided via the `Controller Events` parameter.
|
|
///
|
|
/// **Script Usage:**
|
|
/// * Place the `VRTK_StepMultiplier` script on either:
|
|
/// * Any GameObject in the scene if no activation button is required.
|
|
/// * The GameObject with the Controller Events scripts if an activation button is required.
|
|
/// * Any other scene GameObject and provide a valid `VRTK_ControllerEvents` component to the `Controller Events` parameter of this script if an activation button is required.
|
|
/// </remarks>
|
|
/// <example>
|
|
/// `VRTK/Examples/028_CameraRig_RoomExtender` shows how the Step Multiplier can be used to move around the scene with multiplied steps.
|
|
/// </example>
|
|
[AddComponentMenu("VRTK/Scripts/Locomotion/VRTK_StepMultiplier")]
|
|
public class VRTK_StepMultiplier : MonoBehaviour
|
|
{
|
|
/// <summary>
|
|
/// Movement methods.
|
|
/// </summary>
|
|
public enum MovementFunction
|
|
{
|
|
/// <summary>
|
|
/// Moves the head with a non-linear drift movement.
|
|
/// </summary>
|
|
Nonlinear,
|
|
/// <summary>
|
|
/// Moves the headset in a direct linear movement.
|
|
/// </summary>
|
|
LinearDirect
|
|
}
|
|
|
|
[Header("Step Multiplier Settings")]
|
|
|
|
[Tooltip("The controller button to activate the step multiplier effect. If it is `Undefined` then the step multiplier will always be active.")]
|
|
public VRTK_ControllerEvents.ButtonAlias activationButton = VRTK_ControllerEvents.ButtonAlias.Undefined;
|
|
[Tooltip("This determines the type of movement used by the extender.")]
|
|
public MovementFunction movementFunction = MovementFunction.LinearDirect;
|
|
[Tooltip("This is the factor by which movement at the edge of the circle is amplified. `0` is no movement of the play area. Higher values simulate a bigger play area but may be too uncomfortable.")]
|
|
[Range(0, 10)]
|
|
public float additionalMovementMultiplier = 1.0f;
|
|
[Tooltip("This is the size of the circle in which the play area is not moved and everything is normal. If it is to low it becomes uncomfortable when crouching.")]
|
|
[Range(0, 5)]
|
|
public float headZoneRadius = 0.25f;
|
|
|
|
[Header("Custom Settings")]
|
|
|
|
[Tooltip("The Controller Events to listen for the events on. If the script is being applied onto a controller then this parameter can be left blank as it will be auto populated by the controller the script is on at runtime.")]
|
|
public VRTK_ControllerEvents controllerEvents;
|
|
|
|
protected Vector3 relativeMovementOfCameraRig = new Vector3();
|
|
protected Transform movementTransform;
|
|
protected Transform playArea;
|
|
protected Vector3 headCirclePosition;
|
|
protected Vector3 lastPosition;
|
|
protected Vector3 lastMovement;
|
|
protected bool activationEnabled;
|
|
protected VRTK_ControllerEvents.ButtonAlias subscribedActivationButton = VRTK_ControllerEvents.ButtonAlias.Undefined;
|
|
protected bool buttonSubscribed;
|
|
|
|
protected virtual void Awake()
|
|
{
|
|
VRTK_SDKManager.AttemptAddBehaviourToToggleOnLoadedSetupChange(this);
|
|
}
|
|
|
|
protected virtual void OnEnable()
|
|
{
|
|
activationEnabled = false;
|
|
buttonSubscribed = false;
|
|
movementTransform = VRTK_DeviceFinder.HeadsetTransform();
|
|
playArea = VRTK_DeviceFinder.PlayAreaTransform();
|
|
MoveHeadCircleNonLinearDrift();
|
|
if (movementTransform != null)
|
|
{
|
|
lastPosition = movementTransform.localPosition;
|
|
}
|
|
}
|
|
|
|
protected virtual void OnDestroy()
|
|
{
|
|
VRTK_SDKManager.AttemptRemoveBehaviourToToggleOnLoadedSetupChange(this);
|
|
}
|
|
|
|
protected virtual void Update()
|
|
{
|
|
ManageButtonSubscription();
|
|
switch (movementFunction)
|
|
{
|
|
case MovementFunction.Nonlinear:
|
|
MoveHeadCircleNonLinearDrift();
|
|
break;
|
|
case MovementFunction.LinearDirect:
|
|
MoveHeadCircle();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
protected virtual void ManageButtonSubscription()
|
|
{
|
|
controllerEvents = (controllerEvents != null ? controllerEvents : GetComponentInParent<VRTK_ControllerEvents>());
|
|
|
|
if (controllerEvents != null && buttonSubscribed && subscribedActivationButton != VRTK_ControllerEvents.ButtonAlias.Undefined && activationButton != subscribedActivationButton)
|
|
{
|
|
buttonSubscribed = false;
|
|
controllerEvents.UnsubscribeToButtonAliasEvent(subscribedActivationButton, true, ActivationButtonPressed);
|
|
controllerEvents.UnsubscribeToButtonAliasEvent(subscribedActivationButton, false, ActivationButtonReleased);
|
|
subscribedActivationButton = VRTK_ControllerEvents.ButtonAlias.Undefined;
|
|
}
|
|
|
|
if (controllerEvents != null && !buttonSubscribed && activationButton != VRTK_ControllerEvents.ButtonAlias.Undefined)
|
|
{
|
|
controllerEvents.SubscribeToButtonAliasEvent(activationButton, true, ActivationButtonPressed);
|
|
controllerEvents.SubscribeToButtonAliasEvent(activationButton, false, ActivationButtonReleased);
|
|
buttonSubscribed = true;
|
|
subscribedActivationButton = activationButton;
|
|
}
|
|
}
|
|
|
|
protected virtual void ActivationButtonPressed(object sender, ControllerInteractionEventArgs e)
|
|
{
|
|
activationEnabled = true;
|
|
}
|
|
|
|
protected virtual void ActivationButtonReleased(object sender, ControllerInteractionEventArgs e)
|
|
{
|
|
activationEnabled = false;
|
|
}
|
|
|
|
protected virtual void Move(Vector3 movement)
|
|
{
|
|
headCirclePosition += movement;
|
|
if (playArea != null && (activationEnabled || activationButton == VRTK_ControllerEvents.ButtonAlias.Undefined))
|
|
{
|
|
playArea.localPosition += movement * additionalMovementMultiplier;
|
|
relativeMovementOfCameraRig += movement * additionalMovementMultiplier;
|
|
}
|
|
}
|
|
|
|
protected virtual void MoveHeadCircle()
|
|
{
|
|
if (movementTransform != null)
|
|
{
|
|
//Get the movement of the head relative to the headCircle.
|
|
Vector3 circleCenterToHead = new Vector3(movementTransform.localPosition.x - headCirclePosition.x, 0, movementTransform.localPosition.z - headCirclePosition.z);
|
|
|
|
//Get the direction of the head movement.
|
|
UpdateLastMovement();
|
|
|
|
//Checks if the head is outside of the head cirlce and moves the head circle and play area in the movementDirection.
|
|
if (circleCenterToHead.sqrMagnitude > headZoneRadius * headZoneRadius && lastMovement != Vector3.zero)
|
|
{
|
|
//Just move like the headset moved
|
|
Move(lastMovement);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected virtual void MoveHeadCircleNonLinearDrift()
|
|
{
|
|
if (movementTransform != null)
|
|
{
|
|
Vector3 movement = new Vector3(movementTransform.localPosition.x - headCirclePosition.x, 0, movementTransform.localPosition.z - headCirclePosition.z);
|
|
if (movement.sqrMagnitude > headZoneRadius * headZoneRadius)
|
|
{
|
|
Vector3 deltaMovement = movement.normalized * (movement.magnitude - headZoneRadius);
|
|
Move(deltaMovement);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected virtual void UpdateLastMovement()
|
|
{
|
|
if (movementTransform != null)
|
|
{
|
|
//Save the last movement
|
|
lastMovement = movementTransform.localPosition - lastPosition;
|
|
lastMovement.y = 0;
|
|
lastPosition = movementTransform.localPosition;
|
|
}
|
|
}
|
|
}
|
|
}
|