question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

VRTK_Slider doesn't work at all - with oculus rift

See original GitHub issue

Environment

  • Source of VRTK: Github
  • Version of VRTK:
  • Version of Unity3D: 5.5.3f1
  • Hardware used: Oculus Rift + Touch
  • SDK used: Oculus Utilities + Avatar

Steps to reproduce

  1. Open example scene 025.
  2. Try to grab and move any slider.
  3. Observe how the slider won’t move at all.

Expected behavior

The sliders should move.

Current behavior

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:

// 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");
        }
    }
}

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:15 (11 by maintainers)

github_iconTop GitHub Comments

1reaction
thestonefoxcommented, Apr 24, 2017

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

1reaction
thestonefoxcommented, Apr 13, 2017

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

Read more comments on GitHub >

github_iconTop Results From Across the Web

Resolved - VR UI Slider Issues
Using the Unity XR Plugin for Oculus Quest. ... I confirm that the UI slider doesn't work at all when I "Play" inside...
Read more >
Slider (step slider) doesn't work on iOS & android device
I'm using MRTK slider for my AR app and it works as expected on hololens 2, but now when I build it for...
Read more >
My Render Resolution slider is not working on Oculus ...
My Render Resolution slider is not working on Oculus quest 2. Seems whatever I have it set at all my games look blurry....
Read more >
Unity VR - Oculus Developer Forum - Meta Community Forums
Hi, I am experiencing a major performance spike while working with the OVRPlugin v. 1.38.1 and Oculus 2018.3.6f1. If I have my PlayerPrefab...
Read more >
Sliders - MRTK 2
Sliders work on AR and VR, using motion controllers, hands, or Gesture + Voice. Example scene. You can find examples in the SliderExample...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found