Detect When the Player is Looking at an Object in Unity
In Virtual Reality you want cool stuff to happen when you look at things. Here's one way to do it.
Super Condensed Version
- Create a C# script, "iGazeReceiver.cs"
- Define an interface called "iGazeReceiver" with methods GazingUpon() and NotGazingUpon()
- Create a C# script, "GazeManager.cs"
- Create a public variable for the Camera and a private variable for the lastGazedUpon GameObject
- On each frame, send a "NotGazingUpon" message to the last gazed upon object, then raycast from the camera. If it hits something, send a "GazingUpon" message to the object and store it in the lastGazedUpon variable.
- Create a C# script, "TestObject.cs"
- Implement GazingUpon() and NotGazingUpon(). They should set an isGazingUpon boolean accordingly.
- On each frame, check isGazingUpon, and perform an action accordingly.
- Apply the GazeManager script to a player object and attach the camera.
- Apply the TestObject script to an object in the scene.
Set up a test scene
1. Create a simple scene to test out the scripts you're going to create. In my scene, I have a Directional Light and one Cube.
2. Next, create an empty Game Object and call it "Player".
3. Drag the "Main Camera" (that was in the scene by default) onto the "Player" object, making it a child. Set the camera position to 0, 0, 0 and the Player position to 0, 0, -5.
Create the iGazeReceiver interface script
1. Create a new C# script in the Project pane and call it "iGazeReceiver.cs".
2. Double click on the script to open it in Visual Studio. Delete all text in the file and replace it with the following:
public interface iGazeReceiver { /// <summary> /// Should be called when the object is being looked at /// </summary> void GazingUpon(); /// <summary> /// Should be called when the object is no longer being looked at /// </summary> void NotGazingUpon(); }
An interface is basically a list of requirements. Any script that says "I implement the iGazeReceiver interface" musthave a function called GazingUpon() and a function called NotGazingUpon(). We'll be implementing these in another script.
Create the GazeManager script
1. Create a new C# script in the Project pane and call it "GazeManager.cs". Alternatively, you can add this code to the "SimpleGazeCursor.cs" script from this tutorial (http://www.immersivelimit.com/simple-gaze-cursor).
2. Double click on the script to open it in Visual Studio. You should see a template for your GazeManager class with a Start() function and an Update() function. We won't need Start() in this guide.
3. Add one public variable and one private variable after the curly bracket of the class declaration:
public class GazeManager : MonoBehaviour { public Camera viewCamera; private GameObject lastGazedUpon;
4. Add a call to UpdateCursor() to the Update() method, between the curly braces:
void Update () { CheckGaze(); }
5. Create a new method called CheckGaze(), which will contain all of the logic to check the gaze and act accordingly each frame:
private void CheckGaze() { if (lastGazedUpon) { lastGazedUpon.SendMessage("NotGazingUpon", SendMessageOptions.DontRequireReceiver); } Ray gazeRay = new Ray(viewCamera.transform.position, viewCamera.transform.rotation * Vector3.forward); RaycastHit hit; if (Physics.Raycast(gazeRay, out hit, Mathf.Infinity)) { hit.transform.SendMessage("GazingUpon", SendMessageOptions.DontRequireReceiver); lastGazedUpon = hit.transform.gameObject; } }
First, this script checks to see if we have a lastGazedUpon object. If so, it sends a message that effectively says "call the NotGazingUpon() method in any scripts on this object". SendMessageOptions.DontRequireReceiver tells the code not to complain if the method doesn't exist.
Second, the script creates a gaze ray coming out of the camera and performs a raycast to see if the player is looking at anything. If so, it sends a message that says "call the GazingUpon() method".
Create the TestObject script
1. Create a new C# script in the Project pane and call it "TestObject.cs".
2. Double click on the script to open it in Visual Studio. You should see a template for your TestObject class with a Start() function and an Update() function. We won't need Start() in this guide.
3. Add iGazeReceiver after MonoBehaviour to indicate that TestObject will implement the iGazeReceiver interface and add one public variable after the curly bracket of the class declaration:
public class TestObject : MonoBehaviour, iGazeReceiver { private bool isGazingUpon;
4. Add an if statement to the Update() method, between the curly braces that checks whether the player is gazing upon this object and if so, rotates the object:
void Update () { if (isGazingUpon) { // Do anything you want here, we'll rotate for this demo transform.Rotate(0, 3, 0); } }
5. Create methods for GazingUpon() and NotGazingUpon() which set the isGazingUpon variable accordingly.
public void GazingUpon() { isGazingUpon = true; } public void NotGazingUpon() { isGazingUpon = false; }
Tie it all together
1. Select the "Player" Game Object. Drag the GazeManager script into the Inspector pane to attach it.
2. Drag the "Main Camera" object into the "View Camera" slot.
3. Select the "Cube" Game Object. Drag the TestObject script into the Inspector pane to attach it.
4. Hit "Play" and watch your Cube spin when you look at it in Virtual Reality!
Now go forth and make the virtual world a better place!