The sliders should move.
The sliders don't follow the controller and stay fixed in their initial position.
I tried fixing this but can't make the current snapToStep feature work. I suggest using the top left slider on the wall and set
Released Friction to 5 (We want to get back the old slider behavior where you can flick the slider, currently this doesn't work even when this setting is 0.)Step Size to 10 (To test whether Snap To Step works.)Then make sure to test both Snap To Step false and true.
Here's what I've changed it to:
```C#
// Slider|Controls3D|100090
namespace VRTK
{
using UnityEngine;
/// <summary>
/// Attaching the script to a game object will allow the user to interact with it as if it were a horizontal or vertical slider. The direction can be freely set and auto-detection is supported.
/// </summary>
/// <remarks>
/// The script will instantiate the required Rigidbody and Interactable components automatically in case they do not exist yet.
/// </remarks>
/// <example>
/// `VRTK/Examples/025_Controls_Overview` has a selection of sliders at various angles with different step values to demonstrate their usage.
/// </example>
public class VRTK_Slider : VRTK_Control
{
[Tooltip("An optional game object to which the wheel will be connected. If the game object moves the wheel will follow along.")]
public GameObject connectedTo;
[Tooltip("The axis on which the slider should move. All other axis will be frozen.")]
public Direction direction = Direction.autodetect;
[Tooltip("The collider to specify the minimum limit of the slider.")]
public Collider minimumLimit;
[Tooltip("The collider to specify the maximum limit of the slider.")]
public Collider maximumLimit;
[Tooltip("The minimum value of the slider.")]
public float minimumValue = 0f;
[Tooltip("The maximum value of the slider.")]
public float maximumValue = 100f;
[Tooltip("The increments in which slider values can change.")]
public float stepSize = 0.1f;
[Tooltip("If this is checked then when the slider is released, it will snap to the nearest value position.")]
public bool snapToStep = false;
[Tooltip("The amount of friction the slider will have when it is released.")]
public float releasedFriction = 50f;
protected Direction finalDirection;
protected Rigidbody sliderRigidbody;
protected ConfigurableJoint sliderJoint;
protected bool sliderJointCreated = false;
protected Vector3 minimumLimitDiff;
protected Vector3 maximumLimitDiff;
protected Vector3 snapPosition;
protected override void OnDrawGizmos()
{
base.OnDrawGizmos();
if (!enabled || !setupSuccessful)
{
return;
}
Gizmos.DrawLine(transform.position, minimumLimit.transform.position);
Gizmos.DrawLine(transform.position, maximumLimit.transform.position);
}
protected override void InitRequiredComponents()
{
DetectSetup();
InitRigidbody();
InitInteractableObject();
InitJoint();
}
protected override bool DetectSetup()
{
if (sliderJointCreated)
{
if (connectedTo)
{
sliderJoint.connectedBody = connectedTo.GetComponent<Rigidbody>();
}
}
finalDirection = direction;
if (direction == Direction.autodetect)
{
RaycastHit hitRight;
RaycastHit hitUp;
RaycastHit hitForward;
bool rightHasHit = Physics.Raycast(transform.position, transform.right, out hitRight);
bool upHasHit = Physics.Raycast(transform.position, transform.up, out hitUp);
bool forwardHasHit = Physics.Raycast(transform.position, transform.forward, out hitForward);
Vector3 sliderDiff = transform.localScale / 2f;
//The right ray has found the min on the right, so max is on the left
if (rightHasHit && hitRight.collider.gameObject.Equals(minimumLimit.gameObject))
{
finalDirection = Direction.x;
minimumLimitDiff = CalculateDiff(minimumLimit.transform.localPosition, Vector3.right, minimumLimit.transform.localScale.x, sliderDiff.x, false);
maximumLimitDiff = CalculateDiff(maximumLimit.transform.localPosition, Vector3.right, maximumLimit.transform.localScale.x, sliderDiff.x, true);
}
//The right ray has found the max on the right, so min is on the left
if (rightHasHit && hitRight.collider.gameObject.Equals(maximumLimit.gameObject))
{
finalDirection = Direction.x;
minimumLimitDiff = CalculateDiff(minimumLimit.transform.localPosition, Vector3.right, minimumLimit.transform.localScale.x, sliderDiff.x, true);
maximumLimitDiff = CalculateDiff(maximumLimit.transform.localPosition, Vector3.right, maximumLimit.transform.localScale.x, sliderDiff.x, false);
}
// the up ray has found the min above, so max is below
if (upHasHit && hitUp.collider.gameObject.Equals(minimumLimit.gameObject))
{
finalDirection = Direction.y;
minimumLimitDiff = CalculateDiff(minimumLimit.transform.localPosition, Vector3.up, minimumLimit.transform.localScale.y, sliderDiff.y, false);
maximumLimitDiff = CalculateDiff(maximumLimit.transform.localPosition, Vector3.up, maximumLimit.transform.localScale.y, sliderDiff.y, true);
}
//the up ray has found the max above, so the min ix below
if (upHasHit && hitUp.collider.gameObject.Equals(maximumLimit.gameObject))
{
finalDirection = Direction.y;
minimumLimitDiff = CalculateDiff(minimumLimit.transform.localPosition, Vector3.up, minimumLimit.transform.localScale.y, sliderDiff.y, true);
maximumLimitDiff = CalculateDiff(maximumLimit.transform.localPosition, Vector3.up, maximumLimit.transform.localScale.y, sliderDiff.y, false);
}
//the forward ray has found the min in front, so the max is behind
if (forwardHasHit && hitForward.collider.gameObject.Equals(minimumLimit.gameObject))
{
finalDirection = Direction.z;
minimumLimitDiff = CalculateDiff(minimumLimit.transform.localPosition, Vector3.forward, minimumLimit.transform.localScale.y, sliderDiff.y, false);
maximumLimitDiff = CalculateDiff(maximumLimit.transform.localPosition, Vector3.forward, maximumLimit.transform.localScale.y, sliderDiff.y, true);
}
//the forward ray has found the max in front, so the min is behind
if (forwardHasHit && hitForward.collider.gameObject.Equals(maximumLimit.gameObject))
{
finalDirection = Direction.z;
minimumLimitDiff = CalculateDiff(minimumLimit.transform.localPosition, Vector3.forward, minimumLimit.transform.localScale.z, sliderDiff.z, true);
maximumLimitDiff = CalculateDiff(maximumLimit.transform.localPosition, Vector3.forward, maximumLimit.transform.localScale.z, sliderDiff.z, false);
}
}
return true;
}
protected override ControlValueRange RegisterValueRange()
{
return new ControlValueRange()
{
controlMin = minimumValue,
controlMax = maximumValue
};
}
protected override void HandleUpdate()
{
CalculateValue();
if (sliderRigidbody.velocity.x != 0)
{
Debug.Log(sliderRigidbody.velocity.x);
}
if (snapToStep && sliderRigidbody.velocity.x == 0)
{
SnapToValue();
}
}
protected virtual Vector3 CalculateDiff(Vector3 initialPosition, Vector3 givenDirection, float scaleValue, float diffMultiplier, bool addition)
{
var additionDiff = givenDirection * diffMultiplier;
var limitDiff = givenDirection * (scaleValue / 2f);
if (addition)
{
limitDiff = initialPosition + limitDiff;
}
else
{
limitDiff = initialPosition - limitDiff;
}
var answer = initialPosition - limitDiff;
if (addition)
{
answer -= additionDiff;
}
else
{
answer += additionDiff;
}
return answer;
}
protected virtual void InitRigidbody()
{
sliderRigidbody = GetComponent<Rigidbody>();
if (sliderRigidbody == null)
{
sliderRigidbody = gameObject.AddComponent<Rigidbody>();
}
sliderRigidbody.isKinematic = false;
sliderRigidbody.useGravity = false;
sliderRigidbody.constraints = RigidbodyConstraints.FreezeRotation;
sliderRigidbody.drag = releasedFriction;
if (connectedTo)
{
Rigidbody connectedToRigidbody = connectedTo.GetComponent<Rigidbody>();
if (connectedToRigidbody == null)
{
connectedToRigidbody = connectedTo.AddComponent<Rigidbody>();
connectedToRigidbody.useGravity = false;
connectedToRigidbody.isKinematic = true;
}
}
}
protected virtual void InitInteractableObject()
{
VRTK_InteractableObject sliderInteractableObject = GetComponent<VRTK_InteractableObject>();
if (sliderInteractableObject == null)
{
sliderInteractableObject = gameObject.AddComponent<VRTK_InteractableObject>();
}
sliderInteractableObject.isGrabbable = true;
sliderInteractableObject.grabAttachMechanicScript = gameObject.AddComponent<GrabAttachMechanics.VRTK_TrackObjectGrabAttach>();
sliderInteractableObject.secondaryGrabActionScript = gameObject.AddComponent<SecondaryControllerGrabActions.VRTK_SwapControllerGrabAction>();
sliderInteractableObject.grabAttachMechanicScript.precisionGrab = true;
sliderInteractableObject.stayGrabbedOnTeleport = false;
sliderInteractableObject.InteractableObjectGrabbed += InteractableObjectGrabbed;
sliderInteractableObject.InteractableObjectUngrabbed += InteractableObjectUngrabbed;
}
protected virtual void InteractableObjectGrabbed(object sender, InteractableObjectEventArgs e)
{
sliderRigidbody.drag = 0;
}
protected virtual void InteractableObjectUngrabbed(object sender, InteractableObjectEventArgs e)
{
sliderRigidbody.drag = releasedFriction;
}
protected virtual void InitJoint()
{
sliderJoint = GetComponent<ConfigurableJoint>();
if (sliderJoint == null)
{
sliderJoint = gameObject.AddComponent<ConfigurableJoint>();
}
sliderJoint.xMotion = (finalDirection == Direction.x ? ConfigurableJointMotion.Free : ConfigurableJointMotion.Locked);
sliderJoint.yMotion = (finalDirection == Direction.y ? ConfigurableJointMotion.Free : ConfigurableJointMotion.Locked);
sliderJoint.zMotion = (finalDirection == Direction.z ? ConfigurableJointMotion.Free : ConfigurableJointMotion.Locked);
sliderJoint.angularXMotion = ConfigurableJointMotion.Locked;
sliderJoint.angularYMotion = ConfigurableJointMotion.Locked;
sliderJoint.angularZMotion = ConfigurableJointMotion.Locked;
ToggleSpring(true);
sliderJointCreated = true;
}
protected virtual void CalculateValue()
{
Vector3 minPoint = minimumLimit.transform.localPosition - minimumLimitDiff;
Vector3 maxPoint = maximumLimit.transform.localPosition - maximumLimitDiff;
float maxDistance = Vector3.Distance(minPoint, maxPoint);
float currentDistance = Vector3.Distance(minPoint, transform.localPosition);
float currentValue = Mathf.Round((minimumValue + Mathf.Clamp01(currentDistance / maxDistance) * (maximumValue - minimumValue)) / stepSize) * stepSize;
float flatValue = currentValue - minimumValue;
float controlRange = maximumValue - minimumValue;
float actualPosition = (flatValue / controlRange);
snapPosition = minPoint + ((maxPoint - minPoint) * actualPosition);
value = currentValue;
}
protected virtual void ToggleSpring(bool state)
{
JointDrive snapDriver = new JointDrive();
snapDriver.positionSpring = (state ? 10000f : 0f);
snapDriver.positionDamper = (state ? 10f : 0f);
snapDriver.maximumForce = (state ? 100f : 0f);
sliderJoint.xDrive = snapDriver;
sliderJoint.yDrive = snapDriver;
sliderJoint.zDrive = snapDriver;
}
protected virtual void SnapToValue()
{
sliderJoint.targetPosition = snapPosition * -1f;
sliderJoint.targetVelocity = Vector3.zero;
Debug.Log("Snapped");
}
}
}
```
My understanding of joints is too limited currently 馃槄
Urgh, damn you Oculus!
I'll hook up the rift next week and take a look. I need to look at your PR anyway :p
Another issue is the Controller Alias objects (that get childed to the actual controller) gets moved when it's rigidbody is active.
Ok I fixed the rigidbody getting moved. It wasn't having it's position frozen so it was falling with gravity! :D
Time to look at the slider bug
Seems to be related to the drag of the rigidbody (which is the released friction setting) if you put it lower then you can move it....but why!
I have a gut feeling this is something to do with the Track Object mechanic and it not providing enough force to move the slider.
Maybe due to how the velocities on the rift are weaker than those on the vive?
Have the same problem with the Doors.
Anything that requires a high drag on grab doesn't work with the Oculus SDK... I wonder what's different between grabbing on the vive and grabbing on the rift :/
I tried multiplying the velocities by like 100 but it didn't change anything
I know what the problem is!
SteamVR changes the Settings -> TimeManager -> Fixed TimeStep value to 0.011111
Oculus SDK doesn't change it, so it stays at the default 0.02
This additional TimeStep is what causes the slider not to work properly
I don't have a good solution for this yet, because I don't know why Oculus doesn't change it.
We could force change it for oculus (not for all headsets). But I'd wanna know the reason why they don't change it first.
IMO any functions using FixedUpdate should take the fixed timestep value into account. This lets developers set the fixed timestep based on their requirements. SteamVR provides a way to turn off its auto-setting of the fixed timestep.
@chadfreemanDFM yeah I agree purposely messing with the timestep is not the best course of action.
Maybe the rigidbody drags need to take the current time step into consideration?
Or we leave this up to the user perhaps?
Since VRTK is a library I feel that it should take the timestep into consideration. The timestep can vary for many project, SDK or hardware-related reasons. An individual project might use a lower fixed update rate to increase performance, or a higher rate to increase accuracy. In addition, SteamVR doesn't lock this value to 90FPS, it locks it to the update rate of the headset. This means that DK2s (which update at 75FPS) could also have problems without it. And if new headsets come out with 120FPS update rates, the code will be future-proofed.
Here's the code line from SteamVR which sets the timestep:
Time.fixedDeltaTime = Time.timeScale / vr.hmd_DisplayFrequency;
The surrounding code contains a test to see if this behavior is desired by the project.
thanks for the info @chadfreemanDFM
Any Progress with this Problem?
@Adolar13 no progress on this yet, there is a work around so it's not a big priority at the moment