Whether it’s grabbing and throwing objects or, more specifically, programming a VR FPS app and shooting objects, we include and explain the code you need to program your own FPS with the HTC Vive.

First-person shooters have been a cornerstone in the gaming industry since the beginning: the 1970s gave us Maze War and Spasim; the 1990s Wolfenstein 3D and Doom; and later came 2000s Halo and Call of Duty. The world of VR is taking the FPS genre even further with games like The Brookhaven Experiment and Onward.

We know the demand is out there, and all you have to do is start building. To make things easier, we’ve outlined the code you need to create a gun and start shooting. The rest is up to you — but we’re certainly able to help! Our 1:1 mentorship for our 10-week VR course means that, whatever your vision is, together we can make it a (virtual) reality.

Download our 10-week VR Development with Unity Course Syllabus


 

Skip to the Technical Steps

 

Getting Started

 
We’ve written before about grabbing an object with your Vive controllers here, but that code is based on parent-child relationships between your controller and object. This article works based on a physics relationship. You don’t want to mix and match, so it’s good to be clear about this going in!

To start, make sure you have the SteamVR plugin loaded into your assets. We cover this here, as well as the required code needed to start programming with HTC Vive, in this article here. Go take a quick read if you need to — don’t worry, we’ll wait!

Welcome back! Let’s continue that new C# script. Name it something original (for example, ViveGrabShoot). When you open the script in Visual Studio Code, make sure the script name is reflected below:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class ViveGrabShoot : MonoBehaviour {

Again, just confirm that your script is loaded as a component into both of your controllers.

Next, we’re going to label these controllers as trigger zones. Head back to Unity and open up the controller’s Inspector settings on the right. Add a new component, head to Physics, select Sphere Collider, and click the Trigger checkmark:

Now, to program your controllers! Head back into Visual Stduio Code and add:

    private SteamVR_TrackedObject trackedObj;
 
    private SteamVR_Controller.Device Controller
    {
        get
        {
            return SteamVR_Controller.Input((int)trackedObj.index);
        }
    }
 
    void Awake()
    {
        trackedObj = GetComponent();
    }

 

Setting Up The Physics

 
We’ll need the next following functions so Unity will understand labels of GameObjects:

    public float shootForce;
 
    public GameObject collidingObject;
    public GameObject objectHeld;
 
    public GameObject prefabBullet;

You’ll need to add what happens upon your controller’s trigger zones entering an object:

  public void OnTriggerEnter(Collider other)
    {
        if (collidingObject || !other.GetComponent())
        {
            return;
        }
        collidingObject = other.gameObject;
    }
 
    public void OnTriggerStay(Collider other)
    {
        if (collidingObject || !other.GetComponent())
        {
            return;
        }
        collidingObject = other.gameObject;
    }
 
    public void OnTriggerExit(Collider other)
    {
        if (other.gameObject != collidingObject)
        {
            return;
        }
 
        collidingObject = null;
    }

Now you can grab and manipulate an object:

We want our system refreshing as frequently as possible in order to be responsive to pushed buttons, etc. When updating, we’ll want to include these functions as well.

Basically, these next few lines make it so that if the grip button is held down and you are touching an object, Unity knows you are grabbing that object. If the grip button is released, you drop that object. If your controller is reading movement and velocity when releasing buttons (as if to throw), that movement and velocity will transfer to the object upon release, like so:

Lastly, if that object is labeled as a gun, then you shoot!

 void Update()
    {
        //detecting input
        if (Controller.GetPressDown(SteamVR_Controller.ButtonMask.Grip))
        {
            if (collidingObject)
            {
                GrabObject();
            }
        }
 
        if (Controller.GetPressUp(SteamVR_Controller.ButtonMask.Grip))
        {
            if (objectHeld)
            {
                ReleaseObject();
            }
        }
 
        if (Controller.GetHairTriggerDown () && objectHeld)
        {
            if (objectHeld.name == "Gun")
            {
                Shoot();
            }
        }

 

Arming Yourself

 
As mentioned earlier, this article goes through grabbing objects according to a physics-based relationship. To do this, we need to add a fixed joint between your body (the controller) and the held object:

  private void GrabObject()
    {
        objectHeld = collidingObject;
        collidingObject = null;
        var joint = AddFixedJoint();
        joint.connectedBody = objectHeld.GetComponent ();
    }

Great! Now we have a fixed join between your hand and the object. When holding the object and colliding it with another, you’ll continue holding the object:

Now, to release the object, we need to include a line of code that ‘destroys’ the joint when releasing the grip buttons:

    private void ReleaseObject()
    {
        // 1
        if (GetComponent())
        {
            // 2
            GetComponent().connectedBody = null;
            Destroy(GetComponent());

These next lines release the object with the same velocity as your controller at the same angle you were holding it with. In other words, if you’re holding a baseball and were to throw it by releasing the grip buttons, the ball would follow your velocity and angle like a real pitcher:

            // 3
            objectHeld.GetComponent().velocity = Controller.velocity;
            objectHeld.GetComponent().angularVelocity = Controller.angularVelocity;
        }

And, of course, we’ll want this last bit of code in the case that you want to continue holding the object:

        // 4
        objectHeld = null;
    }

The transfer of velocity is made possible thanks to the physics-based relationship between the controller and object. However, this does mean there’s the potential to break that joint if you collide with other objects. So, to avoid this (or enable it if you want to have fun) you’ll want this next function:

    private FixedJoint AddFixedJoint()
    {
        FixedJoint fx = gameObject.AddComponent();
        fx.breakForce = 20000;
        fx.breakTorque = 20000;
        return fx;
    }

And now, you can have collisions happen without objects moving through each other, or potentially breaking your fixedjoint:

Shoot!

 
You’re ready to add the last lines that allow your gun to shoot a bullet!

    private void Shoot()
    {
        GameObject bullet = Instantiate (prefabBullet, objectHeld.transform.position + objectHeld.transform.forward * 0.3f , objectHeld.transform.rotation);
        bullet.GetComponent().AddForce(objectHeld.transform.forward * shootForce);
    }
}

Huzzah! You’re well on your way to developing the next FPS with Vive! Next you’ll have to design what objects you are aiming at, the maps to maneuver through, etc. We can help with that and more in our 10-week VR/AR course. Whether you’re a seasoned expert looking to branch into VR/AR or a beginner unsure where to start, we’ll give you the tips and tricks you need to start building.

Download VR Course Syllabus

Technical Steps:

 

[csharp]

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class ViveGrabShoot : MonoBehaviour {
 
    //==Required code to use vive controllers==
    private SteamVR_TrackedObject trackedObj;
 
    private SteamVR_Controller.Device Controller
    {
        get
        {
                return SteamVR_Controller.Input((int)trackedObj.index);
        }
    }
 
    void Awake()
    {
        trackedObj = GetComponent();
    }
    //=========================================
 
    public float shootForce;
 
    public GameObject collidingObject;
    public GameObject objectHeld;
 
    public GameObject prefabBullet;
 
    public void OnTriggerEnter(Collider other)
    {
        if (collidingObject || !other.GetComponent())
        {
            return;
        }
        collidingObject = other.gameObject;
    }
    public void OnTriggerStay(Collider other)
    {
        if (collidingObject || !other.GetComponent())
        {
            return;
        }
        collidingObject = other.gameObject;
    }
 
    public void OnTriggerExit(Collider other)
    {
        if (other.gameObject != collidingObject)
        {
            return;
        }
 
        collidingObject = null;
    }
 
 
    //==========
    void Update()
    {
        //detecting input
        if (Controller.GetPressDown(SteamVR_Controller.ButtonMask.Grip))
        {
            if (collidingObject)
            {
                GrabObject();
            }
        }
 
        if (Controller.GetPressUp(SteamVR_Controller.ButtonMask.Grip))
        {
            if (objectHeld)
            {
                ReleaseObject();
            }
        }
 
        //======part 2========
        if (Controller.GetHairTriggerDown () && objectHeld)
        {
            if (objectHeld.name == "Gun")
            {
                Shoot();
            }
        }
        //====================
    }
    //grab and releasing objects
    private void GrabObject()
    {
        objectHeld = collidingObject;
        collidingObject = null;
        var joint = AddFixedJoint();
        joint.connectedBody = objectHeld.GetComponent ();
    }
 
    private void ReleaseObject()
    {
        // 1
        if (GetComponent())
        {
            // 2
            GetComponent().connectedBody = null;
            Destroy(GetComponent());
            // 3
            objectHeld.GetComponent().velocity = Controller.velocity;
            objectHeld.GetComponent().angularVelocity = Controller.angularVelocity;
        }
        // 4
        objectHeld = null;
    }
 
    private FixedJoint AddFixedJoint()
    {
        FixedJoint fx = gameObject.AddComponent();
        fx.breakForce = 20000;
        fx.breakTorque = 20000;
        return fx;
    }
 
    //shoot!
    private void Shoot()
    {
        GameObject bullet = Instantiate (prefabBullet, objectHeld.transform.position + objectHeld.transform.forward * 0.3f , objectHeld.transform.rotation);
        bullet.GetComponent().AddForce(objectHeld.transform.forward * shootForce);
    }
}

[/csharp]