Chooser

The complete solution for identifying and responding to choice and gaze.

Digital interactions are becoming more human-centric with the onslaught of virtual reality(VR), augmented reality(AR), and mixed reality(MR) - or just "XR" as an umbrella term for all of these. This shift brings all sorts of new challenges for enabling a user to interact with your content. One of the core challenges involved with these technologies is how to enable the concept of a cursor and choice for a user. Chooser allows for spatial choice and interaction that feels as familiar as a mouse, takes seconds to set up, and works with any collider in your scene. In addition, Chooser can easily be used for solving numerous gameplay issues that require aiming and selection and not just XR-related challenges.

Feedback Configuration

After you drop Chooser onto an object you have numerous ways to configure it depending on your needs:

  • Source will auto-populate to the GameObject you drop the Chooser onto and can easily be changed to anything else you may need to use.
  • It's best to offer your users a cursor so they can see where they are aiming or looking. Simply drop any transform into the Cursor Properties "cursor" slot and go to town with the available configurations. If you need to get something setup easily, a scaled sphere with a material that simply uses a built-in Unlit Color shader works perfectly well. Take notice that after you set up a cursor for Chooser it will draw a yellow line to it in the editor when selected to help you find the connection.
  • If you are using Chooser from a VR controller or something similar that a user needs to hold, it helps to draw a connecting line to the cursor. If you simply add a LineRenderer into the appropriate slot under Cursor Properties you are good to go - you can easily add this LineRenderer to the same gameobject and simply drag that reference in. It's best to set the width of this LineRenderer to something very thin and the same Unlit Color material idea mentioned for the cursor setup will also do well here.
  • Surface Offset lets you pull the cursor away from the surface it is hitting which is useful for cursors like a sphere where the pivot is inside the visual.
  • Idle Distance sets how far from the source you want the cursor to sit if it is not hitting anything in the world.
  • Stability Delta, Snap Delta, Stable Speed, and Unstable Speed are all useful for keeping your cursor responsive and stable when an XR platform's tracking is jittery or when you want to use the camera with your Chooser and these values help stabalize a user's movement so things feel more natural.
  • Match Surface Normal will cause your cursor to align its forward direction to the normal of the surface the Chooser is hitting. If you turn this off the cursor will face the source. Facing the source is useful for camera-relative content you want to respect the world by "pushing off of it" while it is visible.

Interacting with choices

You have a few options for responding to when gazing begins and ends with Chooser as well as when input is pressed and released. The first way to use all of the events that Chooser provides is to create a simple script that implements the IChooser interface (or simply add these methods to your script without explicitly using the interface - your call). You could place the following code onto a cube with an active collider and begin to get all the events involved with gazing and interaction from Chooser.

using UnityEngine;
using Pixelplacement;

public class ChooserResponse : MonoBehaviour, IChooser
{
	public void Selected()
	{
		Debug.Log(name + " was selected.");
	}

	public void Deselected()
	{
		Debug.Log(name + " was deselected.");
	}

	public void Pressed()
	{
		Debug.Log(name + " was pressed.");
	}

	public void Released()
	{
		Debug.Log(name + " was released.");
	}
}

To maximize platform support of Chooser, the actual inputs that drive the Pressed and Released events can be fully customized. Out of the box you can use the Pressed Input section of the Chooser inspector to set any number of keys or joystick input you may need. You can even leverage custom calls into Chooser if you are supporting multiple platforms for even greater control by simply calling Chooser's Pressed() and Released() methods. For instance, let's support the Vive with input from either trigger:

using UnityEngine;
using Pixelplacement;

public class ViveInput : MonoBehaviour
{
	public Chooser chooser;

	private void Update()
	{
		if (Input.GetKeyDown(KeyCode.JoystickButton14) || Input.GetKeyDown(KeyCode.JoystickButton15)) 
		{
			chooser.Pressed();
		}

		if (Input.GetKeyUp(KeyCode.JoystickButton14) || Input.GetKeyUp(KeyCode.JoystickButton15))
		{
			chooser.Released();
		}
	}
}

With the previous code and using a Vive controller as our Chooser source we could now point our controller at a box that has all of our response functions previously listed, squeeze the trigger, and make things happen with very little code.

In addition to handling events on the target objects themseleves we can also hook into Chooser's direct events through code or the inspector (under Unity Events).

using UnityEngine;
using Pixelplacement;

public class ChooserEvents : MonoBehaviour
{
	public Chooser chooser;

	private void Awake()
	{
		chooser.OnSelected.AddListener(HandleSelected);
		chooser.OnDeselected.AddListener(HandleDeselected);
		chooser.OnPressed.AddListener(HandlePressed);
		chooser.OnReleased.AddListener(HandleReleased);
	}

	void HandleSelected(GameObject choice)
	{
		Debug.Log(choice.name + " was selected.");
	}

	void HandleDeselected(GameObject choice)
	{
		Debug.Log(choice.name + " was selected.");
	}

	void HandlePressed(GameObject choice)
	{
		Debug.Log(choice.name + " was pressed.");
	}

	void HandleReleased(GameObject choice)
	{
		Debug.Log(choice.name + " was released.");
	}
}

Collider Button support

Instead of creating your own classes for responding to interactions from Chooser you can simply drop Surge's Collider Button onto anything in your scene and hit the ground running with even more control and feedback with no coding.