Opinions on the emerging Virtual Reality market vary. One one end, there are those who say it’s just a gimmick and will pass shortly. On the other end, there are those who herald it as the birth pangs of a paradigm shift in how we interact with technology.
I sit somewhere in the middle. I own an HTC Vive and it is pretty dang awesome. The experience is not perfect, of course, but with interest growing and prices coming down, it’s just going to get better.
One expression of VR’s growing popularity is that Unity3D supports VR programming. And, with the free VRTK (Virtual Reality Tool Kit) framework, it’s becoming much easier to get started programming for VR. VRTK implements many basic components, such as:
- various movement implementations
- object interaction
- projectiles
Working on a fun little side project, I noticed one implementation it did not have: a virtual reality bomb. So, I set upon to create one myself. This post details the process I went through for creating a timed, throwable, explosive object for virtual reality using Unity3D & VRTK. I include highly-commented code samples at the end of this tutorial.
A caveat…unlike many tutorials I see online, I’m going to skip what I assume are some basic knowledge requirements, such as what Unity3D is and how to navigate it and its store
interface. It’s pretty easy to educate yourself on this elsewhere and it’s actually pretty intuitive.
Let’s Get Started
So, given that you have already installed or know how to install at least the free version of Unity3D, start a new 3D project.
At the very least, import the VRTK from the Unity Store, and the VR headset API (in my case, SteamVR), if applicable. Otherwise, VRTK implements a “fake” VR view to test your changes (basically a standard flat screen FPS view). VRTK will prompt you on import to keep a bunch of default settings; yes, keep them all.
There are a few pieces of existing functionality we need: grabbing an object and using an object. I used project 008_Controller_UsingAGrabbedObject
as my base, as it contains exactly what we need and little else.
First thing we want to do is to get rid of all unnecessary objects in the scene. In the Hierarchy view, under ExampleWorldObjects
, delete the Gun
object and every thing under and including the TypeTests
object.
Delete the highlighted objects.
Drop the attached Bomb.cs
script (included in the download) into the ExampleResources/Scripts
folder. Remove the Whirlygig
script from the Whirlygig
object and drop the Bomb.cs
script in its place.
As opposed to instructing the code in the Bomb.cs
script line by line, I have instead included a highly commented version of it at the end of this article. Also, go ahead and add an empty
game object to the scene and name it Wall
. We’ll need this later when we create something for the bomb to blow up. I’m including it now so we can just be done with editing the hierarchy.
Delete the Capsule shape from the Whirlygig
object. Replace the cube(mesh filter) on the Whirlygig
object with a sphere mesh filter. Remove the box collider from the Whirlygig
object and add a sphere collider in its place. Finally, rename the Whirlygig
object to Bomb
. You should end up with this:
Visual
We will need to create an explosion Particle System to visually represent the explosion by following the instructions here. Note that this page describes using fire add
image, which does not appear to exist. I used a particle called Firecloud
instead. You will need to be sure to also uncheck Play on Awake
and Looping
in the Inspector
view for the ParticleSystem
.
Once you are satisfied with the way the particle looks, drag it from the Scene
view into the Prefab
folder in Project
view. This converts the particle into a prefab, so that we can instantiate it on-demand via script. Once the prefab is created, delete the instance you created in the Scene view. The Bomb.cs
script will have an explosion
attribute you can see in the Inspector
view. Select the explosion prefab you created and drop it onto the associated empty field.
Sound
We will also need an explosion sound. Add an Audio Source component to the Bomb object. Drop the attached audio file (included in the download) onto the AudioClip attribute. Ensure both Play on Awake
and Is Looping
are unchecked.
A few items of clean up. Ensure that on the Bomb.cs script attached to the Bomb object, that Is Useable
and Is Grabbable
are both checked. I have both Hold button to grab
and Hold button to use
unchecked, but that is up to personal preference. The effect of each should be fairly evident; the actions are toggled when these are unchecked.
Damage
An explosion wouldn’t be much fun without something to blow up. In this case, I’m not talking about damage, just a concussion effect on physical objects; makes the explosion a little more exciting.
Let’s create a wall of blocks to blow up. Drop the attached InstantiateWall.cs
(included in the download) into the scripts folder (as before, I didn’t go line by line, but provide the commented source below). Drop this script onto the empty game object Wall
we created earlier.
We will also need to go the the Tags and Layers
view and add a new tag called Wall
. This is to ensure that when the explosion force is determining which game objects to affect, it only affects the wall blocks (and not the floor, etc). Tagging objects is good practice for organization as well.
Bomb.cs Code
A few notes about the Bomb.cs
code. The isActivated
attribute was added since clicking on the use
button calls the StartUsing
method, but the responsibility of updating the object is in the Update
method. A state needed to be set to communicate between the two methods, and isActivated
accomplishes this.
And since we did not want to call the explosion immediately upon use, we check this status and use the Invoke
method, which waits a length of time defined by its second parameter before calling the method defined by its first parameter.
using System.Collections; using System;<span data-mce-type="bookmark" id="mce_SELREST_start" data-mce-style="overflow:hidden;line-height:0" style="overflow:hidden;line-height:0" ></span> namespace VRTK.Examples { using UnityEngine; public class Bomb : VRTK_InteractableObject { public float fuseTime;//time between clicking the use button and detonation private bool isActivated = false;//true when use button is clicked, indicates that the "fuse has been lit" public ParticleSystem explosion;//explosion visual effects public float radius = 1000.0F;//radius to check for objects affected by explosion public float power = 1000.0F;//energy of explosion public override void StartUsing(VRTK_InteractUse usingObject) { base.StartUsing(usingObject); isActivated = true;//bomb state set to "lit" } public override void StopUsing(VRTK_InteractUse usingObject) { base.StopUsing(usingObject); isActivated = false;//bomb no longer "lit" } protected void Start() { } protected override void Update() { base.Update(); if (isActivated) {//if use button has been clicked Invoke ("Detonate", fuseTime); isActivated = false; } } void Detonate(){ var explosionSound = GetComponent<AudioSource> (); if (!explosion.IsAlive ()) {//to keep it from looping, since update gets called every "tick" Vector3 centerOfExplosion = gameObject.transform.position; AudioSource.PlayClipAtPoint (explosionSound.clip, centerOfExplosion); Destroy (gameObject);//destroy "bomb" first so it disappears at point of explosion Instantiate( explosion, centerOfExplosion, Quaternion.identity ); //This instantiates the explosion prefab we created and places it at the current position of the bomb object ImpactObjects (centerOfExplosion); } } void ImpactObjects(Vector3 explosionPosition) { /* * Find all "Wall" objects within range of explosion and apply the explosive force to each * */ Vector3 explosionPos = explosionPosition; Collider[] colliders = Physics.OverlapSphere(explosionPos, radius); foreach (Collider hit in colliders) { if (hit.gameObject.CompareTag("Wall")) { Rigidbody rb = hit.GetComponent<Rigidbody>(); if (rb != null) { rb.AddExplosionForce (power, explosionPos, radius, 3.0F, ForceMode.Impulse); } } } } } }
Quick look at the InstantiateWall
script. Basically it just creates a set of cubes with RigidBody
components, which are need to exert physical force on. Note we also set each cube with the tag value Wall
. Both the RigidBody
and tag are needed for our explosion code to affect the cubes.
using System.Collections; using System.Collections.Generic; using UnityEngine; public class InstatiateWall : MonoBehaviour { // Use this for initialization void Start () { for (int y = 0; y < 5; y++) { for (int x = 0; x < 5; x++) { GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube); cube.AddComponent<Rigidbody>(); cube.tag = "Wall"; cube.transform.position = new Vector3(x, y, 5); cube.transform.localScale = new Vector3(0.5f,0.5f,0.5f); } } } // Update is called once per frame void Update () { } }
Ready To Go
Now it’s ready to go. Run your program (and slap on your headset if you have one).
Pick up the bomb (ball) and use it. Five seconds later it should explode. Toss it at the wall before that happens for the most spectacular effect. Here’s what you should see:
This is, of course, just a rudimentary implementation of a bomb. This example could be improved in several ways, like:
- A better bomb model
- A countdown effect like ticking, or color flashing
- Damage to physical objects
Here’s a link to the Project Resources.
Thanks for reading!