Oculus Lab 2: Building the Gun
Table of Contents
In this lab, we’ll focus completely on the gun. By the end of the lab, you should be able to pick up the gun, drop it, and fire it with the appropriate effects attached.
How can we interact with our gun, anyway? If you hold an Oculus controller in your hand, you’ll notice that there are two triggers that we can press. One is with the middle finger, called the Hand Trigger, and the other is with the index finger, called the Index Trigger. Pictured below is the control scheme for any Oculus Controller.
For our gun, we want to implement the following functionality:
- Hovering over the gun and holding down the hand trigger / grip picks up the gun.
- While picked up, the gun no longer collides with objects and follows the hand.
- While picked up, pressing the index trigger fires the gun.
- When the hand trigger is released, the gun is dropped and becomes a physics object.
After initializing the new project with the skeleton, you will need to install the XR packages again through Window > Package Manager > (on the top) Packages: Unity Registry:
- Oculus XR Plugin
- XR Plugin Management
- XR Interaction Toolkit (if not there: on the top left of the package manager, click the plus and add by name “com.unity.xr.interaction.toolkit”)
Grabbing the Gun with XR Interaction Toolkit
Because of the XR Interaction Toolkit package, we can easily grab objects only using the component system. First, go to the LeftHandController (XR Origin > Camera Offset > LeftHand Controller) and in the inspector, click Add Component, and add XR Direct Interactor with Hide Controller on Select box ticked and Sphere Collider with a radius of 0.2 and check the “Is Trigger” box. Repeat this with the RightHandController.
Next, go to the Gun in the hierarchy and add the XR Grab Interactable component. Under Movement Type, select Kinematic.
You can try to grab the gun with the grip buttons and feel free to move the boxes in the editor if you cannot reach it. You will immediately notice that there is a rotation problem upon grabbing the gun which prevents accurate shooting. Let’s fix that by adding an offset to the attach transform field on the gun XR Grab Interactable.
Create an empty GameObject with the name “Attach Transform” with rotation values x = -10, y = -165, z = 0, and drag this child GameObject to the Attach Transform field in the XR Grab Interactable Component.
Ok! Now we can grab and aim the gun correctly. Now let us implement the firing of the gun. Because there are no available The outline of this is as follows:
- A controller script to read the trigger input from any controller and call a function from the Gun.
- A gun script with a function called Fire() that fires the gun.
Under Action Maps, select “XRI LeftHand Interaction”. To the right Actions window, we can see that there are actions such as Select that read the button press from the corresponding inputs (this is what let us grab earlier). Click the plus on the Actions window and name it Trigger. Next, click on “" and on the Binding Properties window, select XR Controller > XR Controller (Left Hand) > Optional Controls > triggerPressed which will set this action to correspond with the controller's respective trigger button. Lastly, go back to the Trigger action and on the properties window, select Action Type as "Button". It should look like the image below for the left controller.
Do the same for the right controller (ensure the action is for the right controller) and we now have Input Action References that correspond to trigger buttons!
Now that we can read the trigger, let’s make our first script. Go to the Scripts folder and right-click in the folder and Create > C# Script with the name Hand.cs.
We’ll put in a public variable that represents the controller this hand is attached to (that’ll be set in the editor).
using UnityEngine.InputSystem;
using UnityEngine.XR.Interaction.Toolkit;
public class Hand : MonoBehaviour {
[SerializeField] public InputActionReference controllerActionTrigger;
void Update()
{
if (controllerActionTrigger.action.ReadValue<float>() != 0)
{
Debug.Log("Trigger from " + this.gameObject.name.ToString());
}
}
}
Here, the update function reads the value from the editor-specified InputActionReference and if pressed (!=0), then print the name of the GameObject for that frame. The public and private keywords have the same meanings they do in other programming languages, like Java. However, public variables can be seen and set in the Unity editor, while private ones cannot.
Now that we have this public value which we can edit in the Unity Editor, go to the Left Hand and Right Hand GameObject in the XR Origin and add the Hand script as a component. We can see a field for “Controller Action Trigger” where we can select the corresponding action reference (click the circle on the right of the field and search for “Trigger”; select the one for the corresponding controller).
Press play and try pressing the triggers. Something like “Trigger from LeftHand/RightHand Controller” should be printed into the console whenever the trigger is pressed.
Firing the Gun (Part 1)
Now that we’re successfully reading in input, let’s implement our first bit of functionality: picking up the gun. Before we do anything, we’ll add two variables to our Hand class: XRDirectInteractor interactor which references the controller interactable in the same GameObject. We can access the gun and other variables from this interactor.
private XRDirectInteractor interactor;
private void Start()
{
interactor = GetComponent<XRDirectInteractor>();
}
There are three requirements that must be met for us to try and pick up the gun:
- Our hand must be touching/inside the gun.
- We must hold down on the hand trigger for that hand’s controller.
- We must not already be holding a gun.
You might remember from the roll-a-ball tutorial that triggers are a type of collider. Objects with trigger colliders do not collide with other things normally. They freely pass through them and this can be detected via code. For our purposes, we already have a trigger collider when we were setting up the grab interactable where we can detect collisions in the script and check if the other collider is a gun.
Switch back to Hand.cs in Visual Studio. Add the following function to the class:
if (other.CompareTag("Gun")) {
}
}
This function automatically gets called every frame our hand touches a valid object. To delve into what counts as a “valid” object, see the chart below:
Our hand has no rigidbody (making it static) and has a trigger collider, so it counts as a Static Trigger Collider. By the chart, it’ll send trigger messages upon colliding with our gun, which is a Rigidbody Collider.
So now we have a function that gets called when we touch the gun. However, this function could get called when it collides with other things as well, so we need a way to identify whether parameter other is actually the collider attached to our gun. We’ll use tags to do so.
}
Now that we know we’re dealing with a gun, let’s check if requirements two and three are met by checking if the Interactor has a grab selection (if the gun is grabbed by the interactor). If they are, we can certify we can grab the gun via a script! You might want to comment out or delete the Debug.Log statements in Update() so that your Log message can be seen clearly.
if (other.CompareTag("Gun")) {
if (interactor.hasSelection) {
print("Grabbing gun.");
}
}
}
Our code is now ready to test, but there’s one thing we need to do in Unity first: set the tag on the gun. Select your Gun object in the hierarchy, and in the Inspector view go to Tag > Add Tag. Press the plus button and create a new tag called “Gun”. Then select your Gun again and tag it as “Gun”.
Try it out! Stick your hand into the gun and hold down the grab trigger, and check that you’re repeatedly printing out “Grabbing gun.” when you do so. Now with this verified via script, we can now access the exact Gun’s GameObject through the other collider passed through OnTriggerStay.
Note: This exercise doesn’t actually need the OnTriggerStay since we can verify grabbing and also access the gun through the XR Interactor. We found it necessary to practice the trigger collider system since it is ubiquitous in Unity development.
Firing the Gun (Part 2)
We’ve finally reached the most exciting part of the lab: firing the gun. As always, let’s first consider the requirements that need to be met for the gun to fire:
- We must be holding the gun.
- We must press down on the index trigger
- But it shouldn’t constantly fire as we hold it down.
}
void OnTriggerStay(Collider other) {
if (other.CompareTag("Gun")) {
if (interactor.hasSelection) {
float trigger = controllerActionTrigger.action.ReadValue<float>();
Debug.Log("Grabbing gun.");