diff --git a/Assets/VRTK/Scripts/Locomotion/TouchpadControlActions/VRTK_SnapRotateTouchpadControlAction.cs b/Assets/VRTK/Scripts/Locomotion/TouchpadControlActions/VRTK_SnapRotateTouchpadControlAction.cs
new file mode 100644
index 000000000..1181ec31c
--- /dev/null
+++ b/Assets/VRTK/Scripts/Locomotion/TouchpadControlActions/VRTK_SnapRotateTouchpadControlAction.cs
@@ -0,0 +1,115 @@
+// Snap Rotate Touchpad Control Action|TouchpadControlActions|25030
+namespace VRTK
+{
+ using UnityEngine;
+
+ ///
+ /// The Snap Rotate Touchpad Control Action script is used to snap rotate the controlled GameObject around the up vector when changing the touchpad axis.
+ ///
+ ///
+ /// The effect is a immediate snap rotation to quickly face in a new direction.
+ ///
+ public class VRTK_SnapRotateTouchpadControlAction : VRTK_BaseTouchpadControlAction
+ {
+ [Tooltip("The angle to rotate for each snap.")]
+ public float anglePerSnap = 30f;
+ [Tooltip("The snap angle multiplier to be applied when the modifier button is pressed.")]
+ public float angleMultiplier = 1.5f;
+ [Tooltip("The amount of time required to pass before another snap rotation can be carried out.")]
+ public float snapDelay = 0.5f;
+ [Tooltip("The speed for the headset to fade out and back in. Having a blink between rotations can reduce nausia.")]
+ public float blinkTransitionSpeed = 0.6f;
+
+ private Collider centerCollider;
+ private Transform controlledTransform;
+ private Transform playArea;
+ private float snapDelayTimer = 0f;
+
+ ///
+ /// The ProcessFixedUpdate method is run for every FixedUpdate on the Touchpad Control script.
+ ///
+ /// The GameObject that is going to be affected.
+ /// The device that is used for the direction.
+ /// The axis that is being affected from the touchpad.
+ /// The value of the current touchpad touch point based across the axis direction.
+ /// The value of the deadzone based across the axis direction.
+ /// Whether the controlled GameObject is currently falling.
+ /// Whether the modifier button is pressed.
+ public override void ProcessFixedUpdate(GameObject controlledGameObject, Transform directionDevice, Vector3 axisDirection, float axis, float deadzone, bool currentlyFalling, bool modifierActive)
+ {
+ if (snapDelayTimer < Time.timeSinceLevelLoad)
+ {
+ float angle = Rotate(axis, modifierActive);
+ if (angle != 0f)
+ {
+ Blink();
+ RotateAroundPlayer(controlledGameObject, angle);
+ }
+ }
+ }
+
+ protected virtual void OnEnable()
+ {
+ playArea = VRTK_DeviceFinder.PlayAreaTransform();
+ }
+
+ protected virtual void RotateAroundPlayer(GameObject controlledGameObject, float angle)
+ {
+ Vector3 objectCenter = GetObjectCenter(controlledGameObject.transform);
+ Vector3 objectPosition = controlledGameObject.transform.TransformPoint(objectCenter);
+ controlledGameObject.transform.Rotate(Vector3.up, angle);
+ objectPosition -= controlledGameObject.transform.TransformPoint(objectCenter);
+ controlledGameObject.transform.position += objectPosition;
+ }
+
+ protected virtual float Rotate(float axis, bool modifierActive)
+ {
+ snapDelayTimer = Time.timeSinceLevelLoad + snapDelay;
+ int axisDirection = 0;
+ if (axis < 0)
+ {
+ axisDirection = -1;
+ }
+ else if (axis > 0)
+ {
+ axisDirection = 1;
+ }
+ return (anglePerSnap * (modifierActive ? angleMultiplier : 1)) * axisDirection;
+ }
+
+ protected virtual void Blink()
+ {
+ if (blinkTransitionSpeed > 0f)
+ {
+ VRTK_SDK_Bridge.HeadsetFade(Color.black, 0);
+ Invoke("ReleaseBlink", 0.01f);
+ }
+ }
+
+ protected virtual void ReleaseBlink()
+ {
+ VRTK_SDK_Bridge.HeadsetFade(Color.clear, blinkTransitionSpeed);
+ }
+
+ protected virtual Vector3 GetObjectCenter(Transform checkObject)
+ {
+ if (centerCollider == null || checkObject != controlledTransform)
+ {
+ controlledTransform = checkObject;
+
+ if (checkObject == playArea)
+ {
+ CapsuleCollider playAreaCollider = playArea.GetComponent();
+ centerCollider = playAreaCollider;
+ return playAreaCollider.center;
+ }
+ else
+ {
+ centerCollider = checkObject.GetComponent();
+ }
+ }
+
+ return Vector3.zero;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/VRTK/Scripts/Locomotion/TouchpadControlActions/VRTK_SnapRotateTouchpadControlAction.cs.meta b/Assets/VRTK/Scripts/Locomotion/TouchpadControlActions/VRTK_SnapRotateTouchpadControlAction.cs.meta
new file mode 100644
index 000000000..a07e9bff0
--- /dev/null
+++ b/Assets/VRTK/Scripts/Locomotion/TouchpadControlActions/VRTK_SnapRotateTouchpadControlAction.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 11c28f695996f534b8a8e4935ae2a651
+timeCreated: 1485957999
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/VRTK/Scripts/Locomotion/VRTK_TouchpadControl.cs b/Assets/VRTK/Scripts/Locomotion/VRTK_TouchpadControl.cs
index d2fa7dd27..5b7ccdd66 100644
--- a/Assets/VRTK/Scripts/Locomotion/VRTK_TouchpadControl.cs
+++ b/Assets/VRTK/Scripts/Locomotion/VRTK_TouchpadControl.cs
@@ -98,17 +98,22 @@ protected virtual void FixedUpdate()
CheckDirectionDevice();
CheckFalling();
ModifierButtonActive();
- if (xAxisActionScript && xAxisActionScript.enabled)
+ if (xAxisActionScript && xAxisActionScript.enabled && OutsideDeadzone(touchpadAxis.x, axisDeadzone.x))
{
xAxisActionScript.ProcessFixedUpdate(controlledGameObject, directionDevice, directionDevice.right, touchpadAxis.x, axisDeadzone.x, currentlyFalling, modifierActive);
}
- if (yAxisActionScript && yAxisActionScript.enabled)
+ if (yAxisActionScript && yAxisActionScript.enabled && OutsideDeadzone(touchpadAxis.y, axisDeadzone.y))
{
yAxisActionScript.ProcessFixedUpdate(controlledGameObject, directionDevice, directionDevice.forward, touchpadAxis.y, axisDeadzone.y, currentlyFalling, modifierActive);
}
}
+ protected virtual bool OutsideDeadzone(float axisValue, float deadzoneThreshold)
+ {
+ return (axisValue > deadzoneThreshold || axisValue < -deadzoneThreshold);
+ }
+
protected virtual void CheckSetupControlAction()
{
if (xAxisActionScript && yAxisActionScript && xAxisActionScript.GetInstanceID() == yAxisActionScript.GetInstanceID())
diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md
index a61df6343..1b74bce23 100644
--- a/DOCUMENTATION.md
+++ b/DOCUMENTATION.md
@@ -1281,6 +1281,7 @@ This directory contains scripts that are used to provide different actions when
* [Base Touchpad Control Action](#base-touchpad-control-action-vrtk_basetouchpadcontrolaction)
* [Slide Touchpad Control Action](#slide-touchpad-control-action-vrtk_slidetouchpadcontrolaction)
* [Rotate Touchpad Control Action](#rotate-touchpad-control-action-vrtk_rotatetouchpadcontrolaction)
+ * [Snap Rotate Touchpad Control Action](#snap-rotate-touchpad-control-action-vrtk_snaprotatetouchpadcontrolaction)
---
@@ -1393,6 +1394,43 @@ The ProcessFixedUpdate method is run for every FixedUpdate on the Touchpad Contr
---
+## Snap Rotate Touchpad Control Action (VRTK_SnapRotateTouchpadControlAction)
+ > extends [VRTK_BaseTouchpadControlAction](#base-touchpad-control-action-vrtk_basetouchpadcontrolaction)
+
+### Overview
+
+The Snap Rotate Touchpad Control Action script is used to snap rotate the controlled GameObject around the up vector when changing the touchpad axis.
+
+The effect is a immediate snap rotation to quickly face in a new direction.
+
+### Inspector Parameters
+
+ * **Angle Per Snap:** The angle to rotate for each snap.
+ * **Angle Multiplier:** The snap angle multiplier to be applied when the modifier button is pressed.
+ * **Snap Delay:** The amount of time required to pass before another snap rotation can be carried out.
+ * **Blink Transition Speed:** The speed for the headset to fade out and back in. Having a blink between rotations can reduce nausia.
+
+### Class Methods
+
+#### ProcessFixedUpdate/7
+
+ > `public override void ProcessFixedUpdate(GameObject controlledGameObject, Transform directionDevice, Vector3 axisDirection, float axis, float deadzone, bool currentlyFalling, bool modifierActive)`
+
+ * Parameters
+ * `GameObject controlledGameObject` - The GameObject that is going to be affected.
+ * `Transform directionDevice` - The device that is used for the direction.
+ * `Vector3 axisDirection` - The axis that is being affected from the touchpad.
+ * `float axis` - The value of the current touchpad touch point based across the axis direction.
+ * `float deadzone` - The value of the deadzone based across the axis direction.
+ * `bool currentlyFalling` - Whether the controlled GameObject is currently falling.
+ * `bool modifierActive` - Whether the modifier button is pressed.
+ * Returns
+ * _none_
+
+The ProcessFixedUpdate method is run for every FixedUpdate on the Touchpad Control script.
+
+---
+
# Interactions (VRTK/Scripts/Interactions)
A collection of scripts that provide the ability to interact with game objects with the controllers.