Search code examples
unity-game-enginerotationhololensmrtkwindows-mixed-reality

Setup an helical movement when interacting using the MRTK


I am trying to achieve an helical movement with min/max limiation :

  • positive rotation > negative transform
  • negative rotation > positive transform

Both positive & negative rotation would be done with the MRTK hands interactions provided script, so this is not my problem. I (think) that I am struggling with the logic.

tl;dr : I want to screw something using Unity, MRTK and virtual hands.


Solution

  • I ended up solving my problem, after days of scratching my head really, really hard. To be honest, I probably wouldn't have succeeded on my own, because of a logic problem (this is very often the case, isn't it?).


    Initial Setup

    In my case, the gameObject to interact with, is a screw. I wanted it to perform a positive translation when a positive rotation occurs, vice-versa. On this gameObject are attached some components :

    • a collider (mesh, box, capsule)
    • a rigidbody
    • a PointerHandler.cs (MRTK)
    • a ObjectManipulator.cs (MRTK)
    • a NearInteractionGrabbable.cs (MRTK)
    • the TwistingRotation.cs found below

    The rigidbody, PointerHandler.cs, ObjectManipulator.cs and the TwistingRotation.cs configurations can be found here :

    Components configuration

    The magic appear with the PointerHandler.cs that allow us to detect when the near interaction happen with the screw, thx to the OnPointerDragged event.


    TwistingRotation.cs

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    /// <summary>
    /// Translation of the object while rotating it when grabbed using the MRTK.ObjectManipulator
    /// Boundaries and axis restrictions ongoing
    /// </summary>
    
    public class TwistingRotation : MonoBehaviour
    {
        /*******CACHE REFERENCE*******/
        private Transform _myTransform;
    
        [SerializeField] private float translationFactor = 90f;
    
        private Vector3 _minPosition;
        private Vector3 _maxPosition;
        private Vector3 _previousVector;
        private Rigidbody _rb;
    
    
        private void Start()
        {
            // Cache reference
            _myTransform = gameObject.transform;
            _rb = gameObject.GetComponent<Rigidbody>();
    
            // Reference for the previous rotation vector
            _previousVector = _myTransform.up;
            // Default position is the maximum transform.position (unscrewed)
            _maxPosition = _myTransform.position;
            // Minimum position is default transform.position + 1unit in local space direction
            _minPosition = _maxPosition + Vector3.forward;
        }
    
        /// <summary>
        /// Move the object according to the rotation angle value
        /// A positive rotation leads to a positive translation, and vice-versa
        /// </summary>
        public void TranslateRotation()
        {
            // Retrieve the angle on a defined local axis when the rotation occur
            var currentVector = _myTransform.up;
            // Compute the angle between the previous and the current vector on a defined local axis
            // Difference between the previous rotation vector, and the actual, on a global axis
            var angle = Vector3.SignedAngle(_previousVector, currentVector, Vector3.forward);
    
            // Move object proportional to its rotation
            var translation = Vector3.forward * (angle / translationFactor);
            _myTransform.Translate(translation, Space.Self);
    
            // Get the GO current position
            var currentPosition = _myTransform.position;
            // Clamp for each axis between _minPosition and _maxPosition (the default spawn position)
            // Doing a Mathf.Min/Max inside the Mathf.Clamp to insure that the min and max values are correct 
            var x = Mathf.Clamp(currentPosition.x, Mathf.Min(_minPosition.x, _maxPosition.x), Mathf.Max(_minPosition.x, _maxPosition.x));
            var y = Mathf.Clamp(currentPosition.y, Mathf.Min(_minPosition.y, _maxPosition.y), Mathf.Max(_minPosition.y, _maxPosition.y));
            var z = Mathf.Clamp(currentPosition.z, Mathf.Min(_minPosition.z, _maxPosition.z), Mathf.Max(_minPosition.z, _maxPosition.z));
    
            // Compute the new position while taking the boundaries into consideration
            var newPosition = new Vector3(x, y, z);
            _myTransform.position = newPosition;
    
            // Save position for the next frame
            _previousVector = currentVector;
        }
    }
    

    What is happening ?

    Quite simple : the screw is translating over 1 (unity) unit while rotating. The value of the rotation depend on the value of the translationFactor variable. In my case, the value is 360 so a complete rotation over a 1 (unity) unit translation.


    Result

    It is far from being perfect, probably very much "meh" but hey, it is working (not as intended tho, but still) and it allowed me to move forward and I made my presentation.

    Final result