Skip to content

Commit

Permalink
fix(Presence): ensure position rewind does not go through walls
Browse files Browse the repository at this point in the history
The Position Rewind script was not translating the position correctly
and could end up actually projecting the user through the wall rather
than rewinding them back to their original position.

The rewind logic has now been changed to make it work correctly.

The Position Rewind script now also just uses the Body Physics script
for dealing with the play area rigidbody.
  • Loading branch information
thestonefox committed Apr 5, 2017
1 parent 8add2f3 commit ebcd139
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 25 deletions.
11 changes: 10 additions & 1 deletion Assets/VRTK/Scripts/Presence/VRTK_BodyPhysics.cs
Expand Up @@ -276,6 +276,15 @@ public virtual Vector3 GetAngularVelocity()
return (bodyRigidbody != null ? bodyRigidbody.angularVelocity : Vector3.zero);
}

/// <summary>
/// The ResetVelocities method sets the rigidbody velocity and angular velocity to zero to stop the Play Area rigidbody from continuing to move if it has a velocity already.
/// </summary>
public virtual void ResetVelocities()
{
bodyRigidbody.velocity = Vector3.zero;
bodyRigidbody.angularVelocity = Vector3.zero;
}

protected override void OnEnable()
{
base.OnEnable();
Expand Down Expand Up @@ -1065,4 +1074,4 @@ protected virtual void ApplyBodyMomentum(bool applyMomentum = false)
}
}
}
}
}
52 changes: 28 additions & 24 deletions Assets/VRTK/Scripts/Presence/VRTK_PositionRewind.cs
Expand Up @@ -7,11 +7,13 @@ namespace VRTK
/// The Position Rewind script is used to reset the user back to a good known standing position upon receiving a headset collision event.
/// </summary>
/// <example>
/// /// `VRTK/Examples/017_CameraRig_TouchpadWalking` has the position rewind script to reset the user's position if they walk into objects.
/// `VRTK/Examples/017_CameraRig_TouchpadWalking` has the position rewind script to reset the user's position if they walk into objects.
/// </example>
[RequireComponent(typeof(VRTK_HeadsetCollision))]
public class VRTK_PositionRewind : MonoBehaviour
{
[Header("Rewind Settings")]

[Tooltip("The amount of time from original headset collision until the rewind to the last good known position takes place.")]
public float rewindDelay = 0.5f;
[Tooltip("The additional distance to push the play area back upon rewind to prevent being right next to the wall again.")]
Expand All @@ -21,11 +23,15 @@ public class VRTK_PositionRewind : MonoBehaviour
[Tooltip("The threshold to determind how low the headset can be to perform a position rewind. If the headset Y position is lower than this threshold then a rewind won't occur.")]
public float crouchRewindThreshold = 0.1f;

[Header("Custom Settings")]

[Tooltip("The VRTK Body Physics script to use for the collisions and rigidbodies. If this is left blank then the first Body Physics script found in the scene will be used.")]
public VRTK_BodyPhysics bodyPhysics;
[Tooltip("The VRTK Headset Collision script to use to determine if the headset is colliding. If this is left blank then the script will need to be applied to the same GameObject.")]
public VRTK_HeadsetCollision headsetCollision;

protected Transform headset;
protected Transform playArea;
protected Rigidbody playareaRigidbody;
protected VRTK_BodyPhysics bodyPhysics;
protected VRTK_HeadsetCollision headsetCollision;

protected Vector3 lastGoodStandingPosition;
protected Vector3 lastGoodHeadsetPosition;
Expand All @@ -34,21 +40,22 @@ public class VRTK_PositionRewind : MonoBehaviour
protected bool lastGoodPositionSet = false;
protected bool hasCollided = false;
protected bool isColliding = false;
protected bool isRewinding = false;
protected float collideTimer = 0f;

protected virtual void OnEnable()
{
lastGoodPositionSet = false;
headset = VRTK_DeviceFinder.HeadsetTransform();
playArea = VRTK_DeviceFinder.PlayAreaTransform();
bodyPhysics = FindObjectOfType<VRTK_BodyPhysics>();
playareaRigidbody = playArea.GetComponent<Rigidbody>();
headsetCollision = GetComponent<VRTK_HeadsetCollision>();
ManageHeadsetListeners(true);
if (!playArea)
if (playArea == null)
{
VRTK_Logger.Error(VRTK_Logger.GetCommonMessage(VRTK_Logger.CommonMessageKeys.SDK_OBJECT_NOT_FOUND, "PlayArea", "Boundaries SDK"));
}

bodyPhysics = (bodyPhysics != null ? bodyPhysics : FindObjectOfType<VRTK_BodyPhysics>());
headsetCollision = (headsetCollision != null ? headsetCollision : GetComponentInChildren<VRTK_HeadsetCollision>());
ManageHeadsetListeners(true);
}

protected virtual void OnDisable()
Expand All @@ -75,9 +82,9 @@ protected virtual void Update()

protected virtual void FixedUpdate()
{
if (!isColliding && playArea)
if (!isColliding && playArea != null)
{
var floorVariant = 0.005f;
float floorVariant = 0.005f;
if (playArea.position.y > (lastPlayAreaY + floorVariant) || playArea.position.y < (lastPlayAreaY - floorVariant))
{
highestHeadsetY = crouchThreshold;
Expand Down Expand Up @@ -115,6 +122,7 @@ protected virtual void EndCollision()
{
isColliding = false;
hasCollided = false;
isRewinding = false;
}

protected virtual bool BodyCollisionsEnabled()
Expand All @@ -124,27 +132,23 @@ protected virtual bool BodyCollisionsEnabled()

protected virtual void RewindPosition()
{
if (lastGoodPositionSet && headset.localPosition.y > crouchRewindThreshold && BodyCollisionsEnabled())
if (!isRewinding && playArea != null & lastGoodPositionSet && headset.localPosition.y > crouchRewindThreshold && BodyCollisionsEnabled())
{
var xReset = playArea.position.x - (headset.position.x - lastGoodHeadsetPosition.x);
var zReset = playArea.position.z - (headset.position.z - lastGoodHeadsetPosition.z);

var currentPosition = new Vector3(headset.position.x, lastGoodStandingPosition.y, headset.position.z);
var resetPosition = new Vector3(xReset, lastGoodStandingPosition.y, zReset);
var finalPosition = currentPosition + (resetPosition - currentPosition).normalized * (Vector3.Distance(resetPosition, currentPosition) + pushbackDistance);

playArea.position = finalPosition;
if (playareaRigidbody)
isRewinding = true;
Vector3 rewindDirection = lastGoodHeadsetPosition - headset.position;
float rewindDistance = Vector2.Distance(new Vector2(headset.position.x, headset.position.z), new Vector2(lastGoodHeadsetPosition.x, lastGoodHeadsetPosition.z));
playArea.Translate(rewindDirection.normalized * (rewindDistance + pushbackDistance));
playArea.position = new Vector3(playArea.position.x, lastGoodStandingPosition.y, playArea.position.z);
if (bodyPhysics != null)
{
playareaRigidbody.velocity = Vector3.zero;
playareaRigidbody.angularVelocity = Vector3.zero;
bodyPhysics.ResetVelocities();
}
}
}

protected virtual void ManageHeadsetListeners(bool state)
{
if (headsetCollision)
if (headsetCollision != null)
{
if (state)
{
Expand Down
13 changes: 13 additions & 0 deletions DOCUMENTATION.md
Expand Up @@ -4819,6 +4819,17 @@ The GetVelocity method returns the velocity of the body physics rigidbody.

The GetAngularVelocity method returns the angular velocity of the body physics rigidbody.

#### ResetVelocities/0

> `public virtual void ResetVelocities()`

* Parameters
* _none_
* Returns
* _none_

The ResetVelocities method sets the rigidbody velocity and angular velocity to zero to stop the Play Area rigidbody from continuing to move if it has a velocity already.

### Example

`VRTK/Examples/017_CameraRig_TouchpadWalking` has a collection of walls and slopes that can be traversed by the user with the touchpad but the user cannot pass through the objects as they are collidable and the rigidbody physics won't allow the intersection to occur.
Expand All @@ -4837,6 +4848,8 @@ The Position Rewind script is used to reset the user back to a good known standi
* **Pushback Distance:** The additional distance to push the play area back upon rewind to prevent being right next to the wall again.
* **Crouch Threshold:** The threshold to determine how low the headset has to be before it is considered the user is crouching. The last good position will only be recorded in a non-crouching position.
* **Crouch Rewind Threshold:** The threshold to determind how low the headset can be to perform a position rewind. If the headset Y position is lower than this threshold then a rewind won't occur.
* **Body Physics:** The VRTK Body Physics script to use for the collisions and rigidbodies. If this is left blank then the first Body Physics script found in the scene will be used.
* **Headset Collision:** The VRTK Headset Collision script to use to determine if the headset is colliding. If this is left blank then the script will need to be applied to the same GameObject.

### Example

Expand Down

0 comments on commit ebcd139

Please sign in to comment.