11 Apr 2018

Forge AR/VR Toolkit and 3D navigation with the 3dRudder

    This week I'm taking a look and playing with a fun device: the 3dRudder. That controller is advertised as follow by its creators: 3dRudder is a VR, gaming and 3D motion controller. Move intuitively and naturally in virtual reality and 3D games and applications while seated. Anyone can use it, even people who are not used to using a gamepad.

    As illustrated by the picture above, the device is of a size of a large plate (or if you prefer a large pizza;) and acts as a controller, mostly targeting navigation in an immersive VR or AR environment. It is manipulated by slight pressure from your feet, hence leaving your hands free and able to operate other manipulators such as HTC Vive controllers.

    My goal for this first experiment was to evaluate how easy it is to integrate the 3dRudder with our Forge AR VR Toolkit inside Unity. To prepare a scene that can be loaded from Forge in Unity using the toolkit, please refer to the extensive documentation we put on the Forge Toolkit tutorial

    In order to connect the 3dRudder to your operating system, start by downloading its driver at https://www.3drudder.com/start and make sure that the device can be detected. Using the controller inside Unity is very straightforward: use the Asset Store and browse for 3dRudder, then import the controller plugin.

3drudder controller plugin

Click import, it will import the required resources in your project assets:

import assets

    From there add a script to your project, for example I name it CameraController and place the following code in it:

new unity script

using UnityEngine;
using Unity3DRudder;

public class CameraController : MonoBehaviour
{
    public Vector3 SpeedTranslation = new Vector3(10, 10, 10);
    public float SpeedRotation = 50f;
    public bool CanMove = true;
    public bool CanRotate = true;
    public bool Move3D = true;
    public int IndexRudder = 0;
    public bool UseCurve = false;
    public ns3DRudder.ModeAxis ModeAxis = ns3DRudder.ModeAxis.NormalizedValueNonSymmetricalPitch;

    public AnimationCurve YawCurve = new AnimationCurve(new Keyframe(-1, -1), new Keyframe(-0.25f, 0), new Keyframe(0, 0), new Keyframe(0.25f, 0), new Keyframe(1, 1));
    public AnimationCurve PitchCurve = new AnimationCurve(new Keyframe(-1, -1), new Keyframe(-0.25f, 0), new Keyframe(0, 0), new Keyframe(0.25f, 0), new Keyframe(1, 1));
    public AnimationCurve RollCurve = new AnimationCurve(new Keyframe(-1, -1), new Keyframe(-0.25f, 0), new Keyframe(0, 0), new Keyframe(0.25f, 0), new Keyframe(1, 1));
    public AnimationCurve UpDownCurve = new AnimationCurve(new Keyframe(-1, -1), new Keyframe(-0.25f, 0), new Keyframe(0, 0), new Keyframe(0.25f, 0), new Keyframe(1, 1));

    protected Vector3 translation;
    protected Quaternion rotation;

    protected Transform cube;
    protected Rudder rudder;
    protected ns3DRudder.Axis axis;
    protected ns3DRudder.CurveArray curves;

    private CurveRudder Yaw;
    private CurveRudder Pitch;
    private CurveRudder Roll;
    private CurveRudder UpDown;

    // Use this for initialization
    void Start()
    {
        cube = transform;
        translation = Vector3.zero;
        rotation = transform.rotation;

        // Mode Curve
        curves = new ns3DRudder.CurveArray();
        if (UseCurve)
        {
            ModeAxis = ns3DRudder.ModeAxis.ValueWithCurveNonSymmetricalPitch;
            // SET ANIMATION CURVE
            Roll = new CurveRudder(RollCurve);
            Pitch = new CurveRudder(PitchCurve);
            UpDown = new CurveRudder(UpDownCurve);
            Yaw = new CurveRudder(YawCurve);
            // SET NEW RUDDER CURVES 
            curves.SetCurve(ns3DRudder.CurveType.CurveXAxis, Roll);
            curves.SetCurve(ns3DRudder.CurveType.CurveYAxis, Pitch);
            curves.SetCurve(ns3DRudder.CurveType.CurveZAxis, UpDown);
            curves.SetCurve(ns3DRudder.CurveType.CurveZRotation, Yaw);
        }
    }

    void OnApplicationQuit()
    {
#if !UNITY_EDITOR
        s3DRudderManager.Instance.ShutDown();
#endif
    }

    // Update is called once per frame
    void Update()
    {
        // Get info
        GetAxis();

        // Update position
        // Translate with the new direction in self space
        cube.Translate(translation, Move3D ? Space.Self : Space.World);
        cube.rotation = rotation;
    }

    void GetAxis()
    {
        // Get state of Controller with port number : 0
        rudder = s3DRudderManager.Instance.GetRudder(IndexRudder);
        if (UseCurve)
            axis = rudder.GetAxisWithCurve(ModeAxis, curves);
        else
            axis = rudder.GetAxis(ModeAxis);

        // Get the direction of Controller and multiply by deltatime and speed
        if (CanMove)
        {
            if (Move3D)
                translation = Vector3.Scale(rudder.GetAxis3D(axis), SpeedTranslation * Time.deltaTime);
            else
            {
                translation.x = axis.GetXAxis() * SpeedTranslation.x * Time.deltaTime;
                translation.z = axis.GetYAxis() * SpeedTranslation.z * Time.deltaTime;
            }
        }
        if (CanRotate)
            rotation *= Quaternion.AngleAxis(axis.GetZRotation() * SpeedRotation * Time.deltaTime, Vector3.up);
    }
}

    You can find more code samples in the 3dRudder imported assets:

3d rudder assets

    Press play and activate the Game tab, you should be good to go! It might be a bit challenging to navigate with the device at first as it is quite responsive but I'm sure that with a little practice I will get good at it :) Note that you can always adjust the speed programmatically with the API.

    Here is a quick recording of one of my first navigation attempt around a partially loaded Forge model: 

Related Article