In this codelab, you create a simple bow and arrow like object that the user can interact with. The bow and arrow will be controllable using the Daydream controller's rotation, touchpad position and click events.

What you'll learn

Tell us a little about yourself

How will you use this codelab?

Only read through it Read it and complete the exercises

How would you rate your experience with building Unity applications?

Never tried Novice Intermediate Proficient

How would you rate your experience with Google Daydream?

Never tried Novice Intermediate Proficient

How would you rate your experience writing code?

Never tried Novice Intermediate Proficient

Hardware you'll need

Software you'll need

Enable Android USB Debugging

In order to run samples, you'll need to enable debugging over USB on your device. To do so:

  1. Go to Settings
  2. Go to About phone (usually at the bottom)
  3. Tap Build number (usually at the bottom) seven times. You should see a toast saying you are now a developer.
  4. Return to the previous screen to find Developer options at the bottom.
  5. From the Developer options menu, find USB debugging and turn it on.
  6. When you connect your phone to your development platform, you will need to authenticate debugging from the phone. Allowing this will enable your machine to control your device.

Download The Google VR SDK for Unity

You will need this SDK to enable Daydream development in Unity. To do so, download it as a .unitypackage to your computer.

Download the latest SDK release here

Starter Project

We've also provided a .unitypackage to help you check your work along the way. You can download it from GitHub.

Get the project

You can either download the starter project to your computer...

Download Project .unitypackage

...or clone the GitHub repository from the command line.

git clone https://github.com/googlecodelabs/daydream-controller.git

To get started, we'll need to create a new project from scratch. Go ahead a launch Unity.

  1. After unity boots up, you will be greeted with a welcome dialog. Click the New button at the top to setup a new project.
  2. Set the Project name to "daydream-controller"
  3. You can set the Location to whatever you prefer.

If this is your first time using Unity, or you are still a beginner, let's walk through some important parts of Unity.

Using the Default Layout

First off let's use the default layout for the editor. At the top right of the editor, you will see several drop downs. Click the Layout dropdown and select Default. This will arrange the editor windows for you.

Unity's Windows

In Unity, there's several windows you should be aware of. Feel free to explore the provided links, but don't worry if it feels like a lot, we'll walk you through some of the first time steps.

Building for Android

First thing we need to do is configure Unity to build the project for Android

  1. Click on File -> Build Settings
  2. Change the target platform by selecting Android under the Platform menu.
  3. Click on Switch Platform.
  4. If everything was setup correctly, it will look like this (note the Unity symbol to the right of Android).

Configuring the Player

  1. Click on Player Settings... at the bottom of the Build Settings window. It will update the Inspector window in the right panel with the PlayerSettings.
  2. In the Inspector window, expand the Other Settings.

  1. Check the box for Virtual Reality Supported.
  2. Click on the + button and select Daydream SDK.
  3. Set the Package Name attribute to "com.example.daydream.controller"
  4. Finally set the Minimum API Level to 24 or greater.

Importing the Google VR SDK

If you haven't done so already, grab the the latest version of the Google VR SDK for unity. The latest .unitypackage is available to download from GitHub here.

  1. Open the .unitypackage from Windows Explorer or MacOS Finder.
  2. Unity will extract the package and prepare it's files for import. You'll get a confirmation dialog first that list all the files. Click Import to proceed.

  1. After the import operation, you will now see a GoogleVR option appear in the system Menu.

Importing the Codelab Project

Now we'll repeat the process for the provided Codelab project. Importing this project into Unity will set up the editor with the proper setting and include some assets we'll use later on in the Codelab. If you haven't done so already, grab the .unitypackage from GitHub here.

  1. Open the daydream-controller.unitypackage from Windows Explorer or MacOS Finder.
  2. Click Import to proceed.

Open the starter scene

  1. Now that we've imported all the assets, we'll setup our main scene. In the Project Window, select the Scene folder
  2. Double click the Starter scene to open it. You can use the zoom slider at the bottom right to switch between icons and a list view of the assets in the folder.

  1. In the Hierarchy window, you should just have the Main Scene with an Environment Object and a Main Camera.

The scene provided has a trio of targets atop an icy island. The Game view should look like this, which will be the perspective of the player when it is run on the headset.

The Arm Model

When using Daydream, it's best to set up an arm model to mimic where the user's hand is. An arm model is a mathematical model for approximating the location of the user's hand in virtual space. The Daydream controller only has rotational values, but we can use that in combination with the headset's information to create an accurate estimate of where the user's hand is.

Working with Prefabs

In Unity, a Prefab is a form of a template. You can create prefabs and then re-use them multiple times in a scene without having to create them over and over again. Through the course of this Codelab, we will provide some prefabs already made for you to use, and walk through the construction of one later on.

Using the SDK Prefabs

In order to do this, there are some foundational prefabs that are needed in order to set up a Daydream Arm Model.

You can read more about the Daydream controller and the provided prefabs here and here. We'll add them to the scene now.

Adding the Prefabs to the Scene

  1. In the Project window, type "t:prefab GVR" in the search bar, which will find all prefabs with the phrase "GVR".
  2. If you see a lot of blue cubes and can't see the names, use the zoom slider to make them larger, or shrink them down to a list.
  3. Select the four prefabs we are using.

  1. Drag and drop them on the Hierarchy window. Don't worry about order, it is not important in this context. In the end your hierarchy will look something like this.

Setup the Player

When creating Daydream applications, you'll want to have a Player GameObject in every scene. The Player will consist of the two elements that the user will use to interact with the scene: the camera and the controller. The camera will be used to position the viewpoint of the headset, ie- the root/head. The controller will be a hierarchy that represents the user's arm in the scene.

  1. From the Menu, Choose GameObject -> Create Empty. You will see a new GameObject in the Hierarchy window.

  1. In the Inspector window, rename the object "Player".
  2. Set Position to (0, 1, 0)

  1. In the Hierarchy window, find the Main Camera GameObject
  2. Make the Main Camera GameObject a child of the Player GameObject by dragging onto the Player GameObject. The Player GameObject will be highlighted when it is properly dragged.

  1. In the Hierarchy window, select the Main Camera GameObject

  1. In the Inspector window, set Position to (0, 0, 0)
  2. Set Clipping Planes/Near to 0.01

  1. We also want to make the GvrControllerPointer GameObject a child of player. Drag it onto player as well.

  1. Set it's Position to (0, 0, 0)

Your hierarchy will look like this when you are done:

Now we are ready to build and run our first Daydream APK!

Building

  1. Connect your phone to your computer via USB.
  2. From the menu, select File > Build Settings. In the Build Settings window, click On the Build and Run button at the bottom right.
  3. In the file selection dialog, specify your desired location for the resulting APK file. Unity will build, install, and run the apk on your device.

Running

If you have not yet put your phone in your headset, do so now. Ensure the USB cable stays connected, and that the volume/power buttons are on the top. If you just see a black screen, double check to make sure the device is on!

Controllers

Controllers are a required component of Daydream apps, so if your device does not have a controller paired to it, you will see something like this:

Every Daydream View comes with a remote included. If you don't have access to a Daydream view, you can emulate the remote using the Controller Emulator APK.

Initial Orientation

If everything is going smoothly, you should see a landscape with a controller floating in front of you. Follow the instructions and you should then see your application.

If you don't see anything try pressing and holding the home button at the bottom of the controller. It will recenter your view based on your current orientation. You can hold the home button at anytime to recenter.

Check your Work

After you have calibrated your controller, you will see the Daydream controller being held in your hand.

Open the main scene

If you have ran into any issues, don't fret! We've supplied another scene for this point in the codelab. Even if everything is running smoothly, it's good to use at this point as we've organized the scene a little more.

  1. Now that we've imported all the assets, we'll setup our main scene. In the Project Window, select the Scene folder
  2. Double click the Main scene to open it. You can use the zoom slider at the bottom right to switch between icons and a list view of the assets in the folder.

  1. In the Hierarchy window, your hierarchy should look like this:

For this codelab, we will be making a bow and arrow like component the user can interact with. First up we are going to create an arrow object that we can use over and over.

Creating an Arrow Object

In Unity, we need to create our Arrow object. First we'll create the geometry.

  1. From the Menu, Choose GameObject -> 3D Object -> Cylinder. There will now be a new Cylinder GameObject in the Hierarchy window.

  1. In the Inspector window, rename the object Arrow
  2. Set Position to (0, 1, 3)
  3. Set Scale to (0.1, 0.5, 0.1)

  1. At the bottom of the Inspector window, click the Add Component button
  2. Type in "Rigidbody" and hit enter or click it to add it

  1. On the Rigidbody component, Set Collision Detection to Continuous.

  1. Check your work by clicking the Play Button on the Toolbar.

You will see an arrow in front of you in the Game view of Unity.

Creating a Prefab out of the Arrow

We're not going to use this object directly in the scene. Instead we will have another object create it later. To do so, we need to make this object a prefab.

  1. In the Project window, right click the Assets folder -> Create -> Folder.
  2. Name the folder "Prefabs"

  1. In the Hierarchy window, begin to drag the Arrow GameObject

  1. Drop the Arrow GameObject to the Prefabs folder in the Project window.
  2. You will now see an Arrow prefab in the project. We can use this over and over again at runtime.

  1. We're going to use this prefab later, so we don't need the one floating in the scene. Delete the Arrow object from the Hierarchy window. In the end, your hierarchy should look like this.

Creating our scripts

This codelab will have one main class, Quiver. It will read input events from the Daydream controller, and create and fire arrows based on the user's behavior. Since we are going to be shooting arrows from the location of the Daydream controller, we'll add behavior to the provided GvrControllerPointer prefab. First thing we need to do is create some scripts which will allow us to create custom components that will change the behavior of our objects.

  1. In the Project window, right click the Assets folder -> Create -> Folder.
  2. Name the folder "Scripts"

  1. Right click again on the Scripts folder -> Create -> C# Script
  2. Name the script "Quiver"

  1. In the Inspector window, verify your script was created.

  1. Later on, we'll implement an Arrow component, so we'll create that script now as well. Repeat the steps above to add a script called "Arrow"

Adding the Quiver script to the Controller

Now that we have our script, we'll add it to the Controller GameObject.

  1. In the Hierarchy window, select the Player/GvrControllerPointer/ControllerVisual GameObject.

  1. In the Inspector window, click the Add Component button
  2. Type in "Quiver" and hit enter or click it to add it

  1. In the Quiver component double click the Script field to open up the scripting editor.

Now that you are in the coding editor, you will see an initial class template like so:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Quiver : MonoBehaviour {

// Use this for initialization
void Start () {

}

// Update is called once per frame
void Update () {

  }
}

Now we can get down to work! First thing we need to do is to add a reference to the arrow prefab.

  1. In Quiver.cs, add a field of type GameObject named arrowPrefab to the Quiver class. This will be used to create an arrow when the user clicks the touchpad.
public GameObject arrowPrefab;
  1. Next we will create an arrow when the user clicks the touchpad. This can be done by using the GvrController's ClickButtonDown field. It will be true one frame after the user clicks the touchpad. If this happens, we'll create an arrow. Flesh out the Update method.
// Update is called once per frame
void Update() {

  // listen for click events
  if (GvrControllerInput.ClickButtonDown) {
    CreateArrow();
  }
}
  1. Next, add the CreateArrow method. It just instantiates a arrow using the provided prefab. It doesn't do much right now, but there's more to come!
private void CreateArrow() {
  Instantiate(arrowPrefab);
}

Let's switch back to the Unity to setup the Quiver object in the scene.

  1. In the Hierarchy window, select the Player/GvrControllerPointer/ControllerVisual GameObject.

  1. In the Quiver component, note the new Arrow Prefab field.
  2. We are going to assign this field using the Object Picker. To open it, click the small circle next to the field.

  1. You will now see the Object Picker. Similar to the Add Component menu, you can find the object you are looking for by typing it in. Ensure you are in the Assets tab.
  2. Type "Arrow" to find our Arrow Prefab. Hit enter or click it to select it.

  1. Note the updated value of the field.

Now that we are using the controller, you'll need to check your work by building and running the game. You will be able to click the touchpad and an arrow will appear for each click.

Unfortunately they are out in space, not in our hand, and you can create a bunch of arrows and make a mess. Let's fix that!

Next up we're going to hold an arrow, which will make it snap to the user's arm until they release the touchpad button.

  1. Add a new method HoldGameObject, which will store the reference and attach the GameObject to the users arm.
private GameObject _heldArrow;

private void HoldArrow(GameObject arrow) {
  _heldArrow = arrow;

  // make a child of this object
  _heldArrow.transform.SetParent(transform, false);
  _heldArrow.transform.localPosition = new Vector3(0, 0, 1);
  _heldArrow.transform.localEulerAngles = new Vector3(90, 0, 0);
}
  1. Update CreateArrow to use HoldGameObject. Instantiate will return a reference to the created object. Pass that value to the method.
private void CreateArrow() {
  // create the arrow
  GameObject arrow = Instantiate(arrowPrefab);

  // position and orient the arrow near the arm
  HoldArrow(arrow);
}

Releasing the Arrow

Next we'll need to add some code to release the object when the user releases the touchpad. This can be done by using the GvrController's ClickButtonUp field. It will be true one frame after the user releases the touchpad. If this happens, we'll let go of the arrow being held.

  1. Add handling for this event to the Update method. Note that we only want to call this when we have an arrow, so we'll also add an early exit condition.
// Update is called once per frame
void Update() {
  // listen for click events
  if (GvrControllerInput.ClickButtonDown) {
    CreateArrow();
  }
 
  if (_heldArrow == null) return;
 
  if (GvrControllerInput.ClickButtonUp) {
    ReleaseArrow();
  }
}
  1. Next add the Release Arrow method, which will make the arrow a child of the world and clear the reference to the held arrow.
private void ReleaseArrow() {
  // change the parent to the world
  _heldArrow.transform.SetParent(null, true);
  _heldArrow = null;
}

Back in Unity, if you run, you'll notice that the arrow falls through your virtual fingers!

This is because the prefab has physics enabled by default. We can disable it in the prefab by using the Is Kinematic property, which determines if an object will be moving or not.

  1. In the Project window, select the Assets folder
  2. Select the Arrow prefab

  1. In the Inspector window, under the Arrow's Rigidbody component, ensure Is Kinematic is checked.

Now if you run, the arrows will follow your hand, but they float in the air when released!

This of course, is because we disabled physics at all times. We need to re-enable it when the user releases the touchpad.

  1. Add the following lines to ReleaseArrow, after the SetParent call.
// change the parent to the world
_heldArrow.transform.SetParent(null, true);

// nullify the current velocity
Rigidbody arrowRigidbody = _heldArrow.GetComponent<Rigidbody>();
arrowRigidbody.velocity = Vector3.zero;
arrowRigidbody.isKinematic = false;

_heldArrow = null;

Now the Arrows will stay in your hands until you release the touchpad.

Better, but not very interactive as all you can do is create and drop arrows. Let's make it better.

Now that we can hold an arrow and wave it around, let's add some interactivity to it. When the user releases the arrow, we will calculate the velocity of the arrow and apply it to make it a more natural release motion.

  1. First up, let's calculate and store our expected value. To do this we'll need to also store the previous position. Using the current and previous position along with the time between frames, we can calculate and implicit velocity. Add the following code to Quiver.cs.
private Vector3 _throwVelocity;
private Vector3 _previousPosition;

private void CalculateThrowVelocity() {
  // the velocity is based on the previous position
  _throwVelocity = (_heldArrow.transform.position - _previousPosition) / Time.deltaTime;

  // update previous position
  _previousPosition = _heldArrow.transform.position;
}
  1. Make a call to the method near the end of Update. We only want to call it if we're holding an arrow, and before we release it, so insert it right before the release check.
// Update is called once per frame
void Update() {
  // listen for click events
  if (GvrControllerInput.ClickButtonDown) {
    CreateArrow();
  }
  if (_heldArrow == null) return;

  CalculateThrowVelocity();

  if (GvrControllerInput.ClickButtonUp) {
   ReleaseArrow();
  }
}
  1. Finally, we'll want to apply the velocity as a force when we release the arrow. Insert this code at the bottom of ReleaseArrow, right before clearing _heldArrow:
// throw the object when releasing while held
arrowRigidbody.AddForce(_throwVelocity, ForceMode.VelocityChange);

_heldArrow = null;

Now when you run in Unity, the arrows will move a little more naturally when you release them.

You can try throwing them at the targets, but these are arrows, so let's start firing them!

We're going to use the touchpad touch surface to determine if an arrow will be fired or not when the user releases the touchpad. If the user has their thumb on the top half of the touchpad, we will treat the release as a throw. If it is on the bottom half, we we'll treat it as a loaded arrow and fire it.

First up, let's start monitoring the position of the users thumb on the touchpad. This can be accessed from the GvrControllerInput.TouchPos field. The touch position is given as a Vector2 where X and Y range from 0 to 1. (0, 0) is the top left of the touchpad and (1, 1) is the bottom right of the touchpad. We only care about the Y value, so we'll store that as our pullback amount.

  1. Add a field to store the amount the user is pulling back. We're also going to add a calculated property, IsFiring, for convenience.
private float _pullBackAmount = 0.0f;

private bool IsFiring {
  get { return _pullBackAmount > 0.5f; }
}
  1. Next add a method to poll the Daydream controller for the current value. Additionally we'll call PositionArrow, which will update the position of the held arrow based on where the user's thumb is located on the touchpad.
private void PollTouchpad() {
  _pullBackAmount = GvrControllerInput.TouchPos.y;

  PositionArrow();
}
  1. PositionArrow takes the value of the touchpad and interpolates a new position for the held arrow.
private void PositionArrow() {
  // update the position of the arrow locally based on the pullback amount.
  // Since the touchpad ranges from 0(top)..1(bottom), we need to invert the amount it's coming in
  const float initialOffset = 0.25f;
  Vector3 transformLocalPosition = _heldArrow.transform.localPosition;
  transformLocalPosition.z = initialOffset + 1.0f - _pullBackAmount;
  _heldArrow.transform.localPosition = transformLocalPosition;
}
  1. In Update, make sure to call the new PollTouchpad method before the Velocity calculation
if (_heldArrow == null) return;

PollTouchpad();
CalculateThrowVelocity();

Now when you run in Unity, as you hold the arrow, it will move when you slide your thumb up and down the touchpad.

Now we'll turn the pullback amount into a firing velocity.

  1. Next, add a method to calculate the firing velocity. It will be similar to calculating the throw velocity. It's possible to combine the two velocities into one, but we're separating them out to make things easier to follow. Add the following code:
public Vector3 maxFireVelocity = new Vector3(0, 30, 0);
private Vector3 _fireVelocity;

private void CalculateFireVelocity() {
  _fireVelocity = maxFireVelocity * _pullBackAmount;
}
  1. In Update, add a call to the calculation after the throw velocity calculation:
if (_heldArrow == null) return;
PollTouchpad();

CalculateThrowVelocity();
CalculateFireVelocity();
  1. Finally, we'll want to change the velocity application when the user releases the touchpad. In ReleaseArrow, change the velocity application to throw or fire based on the IsFiring property.
// nullify the current velocity
Rigidbody arrowRigidbody = _heldArrow.GetComponent<Rigidbody>();
arrowRigidbody.velocity = Vector3.zero;
arrowRigidbody.isKinematic = false;

if (IsFiring) {
  // fire the object when releasing while aiming
  arrowRigidbody.AddRelativeForce(_fireVelocity, ForceMode.VelocityChange);
}
else {
  // throw the object when releasing while held
  arrowRigidbody.AddForce(_throwVelocity, ForceMode.VelocityChange);
}

_heldArrow = null;

Now in Unity, when you pull back the arrow and release, it will fly from your hands! However. It's a little odd to have arrows just manifest in your hands. You can also rapid fire a bunch of arrows just by clicking the bottom of the track pad. It's fun, but not exactly arrow like.

Next we'll add some conditional code to ensure that the user can only fire an arrow after ‘drawing' one from behind their back, like grabbing arrows from a quiver. To do so, we will use the rotation value from the Daydream controller to see if it's approximately (90, 0 0). We'll also allow some wiggle room because hitting it exactly is impossible.

  1. First we're going to implement a calculated property, ArmIsInPosition. This will read the Daydream controller's rotation and allow for some wiggle room. If all is correct, it will be set to true.
private bool ArmIsInPosition {
  get {
    // The rotation to test against
    const float compareAgainstRotation = 90.0f;

    // The wiggle room afforded to the rotation check.
    const float compareEpsilon = 65.0f;

    // get the rotation from the GameObject's transform, which is set by the arm controller
    float observedRotation = transform.parent.localEulerAngles.x;

    return Mathf.Abs(compareAgainstRotation - observedRotation) < compareEpsilon;
  }
}
  1. Now all we have to do is check this in our Update method. Simply add this to our ClickButtonDown check:
if (GvrControllerInput.ClickButtonDown && ArmIsInPosition) {
  CreateArrow();
}

Now it feels more like a quiver and arrows! But there's still some polish to be done.

Simulating the Trajectory

  1. Next up we're going to add some visual feedback when aiming, as well as how an object moves. First up, we'll draw a trajectory line to estimate where the arrow will travel when fired. We'll do this with Unity's LineRenderer, which we will create in the empty Start method.
private LineRenderer _trajectoryLineRenderer;

// Use this for initialization
void Start() {
  // get the line renderer for the trajectory simulation
  _trajectoryLineRenderer = GetComponent<LineRenderer>();
}
  1. We'll want to simulate the path of the object and store the result as a list of points in the LineRenderer. We'll add a method called SimulateTrajectory which performs this calculation. It takes the position and rotation of the arrow and the fire velocity to estimate where the arrow will travel. It does this by stepping forward in time and calculating the new position and velocity.
private void SimulateTrajectory() {
  // only show if the arrow is being fired
  _trajectoryLineRenderer.enabled = IsFiring;

  Vector3 initialPosition = _heldArrow.transform.position;
  Vector3 initialVelocity = _heldArrow.transform.rotation * _fireVelocity;

  const int numberOfPositionsToSimulate = 50;
  const float timeStepBetweenPositions = 0.2f;

  // setup the initial conditions
  Vector3 simulatedPosition = initialPosition;
  Vector3 simulatedVelocity = initialVelocity;

  // update the position count
  _trajectoryLineRenderer.positionCount = numberOfPositionsToSimulate;

  for (int i = 0; i < numberOfPositionsToSimulate; i++) {
    // set each position of the line renderer
    _trajectoryLineRenderer.SetPosition(i, simulatedPosition);

    // change the velocity based on Gravity and the time step.
    simulatedVelocity += Physics.gravity * timeStepBetweenPositions;

    // change the position based on Gravity and the time step.
    simulatedPosition += simulatedVelocity * timeStepBetweenPositions;
  }
}
  1. You'll need to call this from Update, near the calculate code after polling the touchpad.
if (_heldArrow == null) return;

PollTouchpad();
SimulateTrajectory();

CalculateThrowVelocity();
CalculateFireVelocity();
  1. Finally, we'll need to hide the trajectory line after the arrow is fired. At the bottom of ReleaseArrow, before nullifying _heldArrow, hide the trajectory line.
_trajectoryLineRenderer.enabled = false;
_heldArrow = null;

Adding the Line Renderer in Unity

Back in Unity, we need to add the LineRenderer to our Quiver component:

  1. In the Hierarchy window, select the Player/GvrControllerPointer/ControllerVisual GameObject.

  1. In the Inspector window, click the Add Component button
  2. Type in "Line Renderer" and hit enter or click it to add it

  1. We don't want the laser to render all the time, so we're going disable it by default. Do this by clicking the Checkbox next to Line Renderer.
  2. Set the Materials/Element 0 to ControllerLaser. Use the Object picker by clicking on the small circle similar to how you did before.
  3. Set Width to 0.2
  4. Next, click on the white box next to Color to edit the gradient.

Creating a Color Gradient

Now we're going to make the laser look nice by using a Color Gradient.

  1. The last step will have brought up the Gradient editor. The bottom markers are for colors, and the top markers are for alpha values.
  2. Click on the left side color marker to edit that color
  3. Click on the Color box, which defaults to white.

  1. This will bring up the color editor

  1. Set the (R, G, B) values to (0, 255, 0), which is pure green (or any color you'd like!).

  1. Next, we'll make the color fade out as time passes. Close the color editor and your gradient will have changed. Click on the top right marker to edit the end alpha value.
  2. The color field will have been replaced with an alpha slider. Set the alpha value to zero and you will see a nice gradient fade out the green color.

  1. In the end, your component should look like this. Double check your Color field!

If you run now, you will see a nice green laser render when you pull back on the arrow.

Adding a Trail Renderer to Arrows

When you fire an arrow it's hard to keep track of it. We can add a TrailRenderer component rather easily to help out with that. A TrailRenderer is just a LineRenderer that updates over time based on the object's position. We'll add one to the Arrow prefab. A lot of these steps will be similar to the trajectory renderer.

  1. In the Project window, select the Assets folder.
  2. Select the Arrow prefab.

  1. In the Inspector window, click the Add Component button
  2. Type in "Trail Renderer" and hit enter or click it to add it

  1. We don't want the laser to render all the time, so we're going disable it by default. Do this by clicking the Checkbox next to Trail Renderer.
  2. Set the Materials/Element 0 to ControllerLaser. Use the Object picker by clicking on the small circle similar to how you did before.
  3. Set Time to 2.
  4. Set Width to 0.2
  5. Make the Color field a blue gradient similar to the one we just created for the cannon. Use RGB value (0, 0, 255) for the color. In the end, your component should look like this:

  1. Back in the code we'll want to enable the renderer on Release. In ReleaseArrow, add the following lines before hiding the trajectory line.
TrailRenderer trailRenderer = _heldArrow.GetComponent<TrailRenderer>();
trailRenderer.enabled = true;

_trajectoryLineRenderer.enabled = false;
_heldArrow = null;

Now that you have a trail renderer, you will see more clearly where the arrow is moving!

Almost done! A couple more polish items.

Orienting Arrows

First off, the arrows don't seem to move like arrows. This is because they are not pointing in the direction they are moving. The rotation stays constant to when it was released. We can fix this by using the velocity to calculate an orientation. Let's add a new script called Arrow and attach it to our prefab.

  1. In the Project window, select the Assets folder.
  2. Select the Arrow prefab.

  1. In the Inspector window, click the Add Component button.
  2. Type in "Arrow" and hit enter or click it to add it.

  1. In the Arrow component double click the Script field to open up the scripting editor.

In Arrow.cs, we'll flesh out the behavior of the arrow.

  1. First we need a couple of variables. One to track if the arrow should orient towards its velocity. The other is a reference to the Rigidbody component. Since we are updating this every frame, we don't want to call GetComponent each frame, so we'll store the result of that in Start.
private bool _orientToVelocity;
private Rigidbody _rigidbody;

private void Start() {
  // since we update the Rigidbody every frame, cache the reference
  _rigidbody = GetComponent<Rigidbody>();
}
  1. Next, in Update, we'll do the orientation calculation. Unity provides a helper function to do so, Quaternion.LookRotation. Note we must add an additional 90 degrees. Without it, the arrows will fly perpendicular to their velocity.
// Update is called once per frame
void Update() {
  if (!_orientToVelocity) return;

  // set the rotation to the velocity
  transform.rotation = Quaternion.LookRotation(_rigidbody.velocity);

  // rotate 90 additional degress to make the cyliner orient along the longer side
  Vector3 transformEulerAngles = transform.eulerAngles;
  transformEulerAngles.x += 90;
  transform.eulerAngles = transformEulerAngles;
}
  1. Next we need to tell the arrow that it has been fired. Add a public method OnFired.
public void OnFired() {
  _orientToVelocity = true;
}
  1. Finally, call the method when we create the Arrow in Quiver.cs::ReleaseArrow, in the IsFiring if check:
if (IsFiring) {
  // fire the object when releasing while aiming
  arrowRigidbody.AddRelativeForce(_fireVelocity, ForceMode.VelocityChange);

  Arrow arrow = _heldArrow.GetComponent<Arrow>();
  arrow.OnFired();
}

Sticking an Arrow

Now the arrow will orient towards its velocity. However, if you've fired an arrow at a target, you'll notice it bounces right off! We will make a fired arrow stop when it hits an object. First we need to know that an arrow has been fired(verses being thrown).

  1. In Arrow.cs, add a field to track it.
  2. Set the field in OnFired
private bool _wasFired;

public void OnFired() {
  _wasFired = true;
  _orientToVelocity = true;
}
  1. Next add the OnCollisionEnter handler. This is called by Unity when a Collider component enters the bounding box of another collider.
  2. Since we only want this to happen if the arrow was fired, add an early exit condition.
  3. If it was fired, we want to disable physics on the arrow. Do this by setting the rigidbody isKinematic property to true.
  4. Finally, set _orientToVelocity to false. Otherwise when the arrow stops, its velocity will be zero, and all arrows will pop to the same rotation.
private void OnCollisionEnter(Collision collision) {
  if (!_wasFired) return;

  // make the object 'stick' when it hits an object
  _rigidbody.isKinematic = true;
  _orientToVelocity = false;
}

Now the arrow will stick to those targets!

Hiding the Raycaster

Over the course of the codelab, you may have noticed a white dot and laser coming from the controller in the scene. This is the raycaster visual, which shows you where the raycaster is pointing. Since we are not using it at all and it conflicts with the trajectory line, we'll hide the raycaster in the scene. In more complex scenarios, you may wish to toggle this on and off depending on what the user is doing.

  1. In the Hierarchy window, select the Player/GvrControllerPointer/Laser GameObject.

  1. In the Inspector window, disable the GvrLaserPointer component by ensuring the checkbox by it's name is cleared.
  2. Disable the LineRenderer component as well.

  1. In the Hierarchy window, select the Player/GvrControllerPointer/Laser/Reticle GameObject.

  1. In the Inspector window, disable the Reticle GameObject by ensuring the checkbox by it's name is cleared.

You did it! You've finished the codelab and successfully created a scene in Unity with some interesting interactions for the player using the Daydream controller.

Extra Credit

If you are looking for some more interesting things to do try:

Additionally, if you want to explore the Daydream platform further, check out our documentation here.

Tell us how it went!

Were you able to complete the codelab?

Yes! Yes, with some issues. Nope!

What did you think about the amount of code?

Just Right Too Little Too Much

What did you think about the amount of Unity setup?

Just Right Too Little Too Much

Overall, are you glad you did this codelab?

Yes! No...