Explorar o código

VR Starter Tutorial rewrite/refactor (#2774)

* Initial VR starter tutorial rewrite

* More refactoring work on the VR starter tutorial. Moved the VR starter tutorial into its own sub directory and broke the tutorial into two parts

* Finished the first draf on most of the first part of the VR starter tutorial

* First draft for the first half of the VR tutorial is done! Now all that is left is the second half, which should be a tad easier to do

* About a fourth of the way through the refactor of the VR Starter tutorial part 2

* Almost finished part 2 of the VR starter tutorial rewrite. Just a little bit left

* Finished the second part of the VR starter tutorial rewrite

* Made small changes to the VR starter tutorial based on feedback from BastiaanOlij. Fixed merge conflict in PR
TwistedTwigleg %!s(int64=5) %!d(string=hai) anos
pai
achega
dfb5cabbd7

BIN=BIN
tutorials/vr/files/VR_Starter_Tutorial_Complete.zip


BIN=BIN
tutorials/vr/files/VR_Starter_Tutorial_Start.zip


+ 1 - 1
tutorials/vr/index.rst

@@ -6,4 +6,4 @@ VR
    :name: toc-tutorials-vr
 
    vr_primer
-   vr_starter_tutorial
+   vr_starter_tutorial/index

+ 0 - 1423
tutorials/vr/vr_starter_tutorial.rst

@@ -1,1423 +0,0 @@
-.. _doc_vr_starter_tutorial:
-
-VR starter tutorial
-===================
-
-Introduction
-------------
-
-.. image:: img/starter_vr_tutorial_sword.png
-
-This tutorial will show you how to make a beginner VR game project in Godot.
-
-Keep in mind, **one of the most important things when making VR content is getting the scale of your assets correct**!
-It can take lots of practice and iterations to get this right, but there are a few things you can do to make it easier:
-
-- In VR, 1 unit is typically considered 1 meter. If you design your assets around that standard, you can save yourself a lot of headache.
-- In your 3D modeling program, see if there is a way to measure and use real world distances. In Blender, you can use the MeasureIt add-on; in Maya, you can use the Measure Tool.
-- You can make rough models using a tool like `Google Blocks <https://vr.google.com/blocks/>`_, and then refine in another 3D modelling program.
-- Test often, as the assets can look dramatically different in VR than on a flat screen!
-
-Throughout the course of this tutorial, we will cover:
-
-- How to tell Godot to run in VR.
-- How to make a teleportation system for moving the player.
-- How to make a directional movement system (locomotion) for moving the player.
-- How to make a :ref:`RigidBody <class_RigidBody>`-based pick up and drop system.
-- How to make various items that can be used in VR.
-
-.. note:: While this tutorial can be completed by beginners, it is highly
-          advised to complete :ref:`doc_your_first_game`,
-          if you are new to Godot and/or game development and have some experience with making 3D games
-          **before** going through this tutorial series.
-
-          This tutorial assumes you have experience working with the Godot editor,
-          have basic programming experience in GDScript, and have basic 3D game development experience.
-
-          Also, it is assumed you have both an OpenVR-ready headset and two OpenVR-ready controllers! This tutorial was written using a Windows Mixed Reality headset on Windows 10,
-          so the tutorial is written to work on that headset. It has also been tested on the HTC Vive. You may need to adjust the code to work with other VR headsets, such as the Oculus Rift.
-
-You can find the start assets for this tutorial here: :download:`VR_Starter_Tutorial_Start.zip <files/VR_Starter_Tutorial_Start.zip>`
-
-The provided starter assets contain some 3D models, sounds, and a few scenes already set up and configured for this tutorial.
-
-Feel free to use these assets however you want! All original assets belong to the Godot community, with the other assets belonging to those listed below:
-
-.. note:: The sky panorama was created by **CGTuts** (`original source <https://cgi.tutsplus.com/articles/freebie-8-awesome-ocean-hdris--cg-5684>`_).
-
-          The font used is **Titillium-Regular**, and is licensed under the SIL Open Font License, Version 1.1.
-
-          The audio used are from several different sources, all downloaded from the **Sonnis #GameAudioGDC Bundle** (`license in PDF format <https://sonniss.com/gdc-bundle-license/>`_).
-          The folders where the audio files are stored have the same name as folders in the bundle.
-
-          The **OpenVR addon** was created by Bastiaan Olij and is released under the MIT license. It can be found both `on the Asset Library <https://godotengine.org/asset-library/asset/150>`_ and `on GitHub <https://github.com/GodotVR/godot-openvr-asset>`_.
-
-          Everything else is original and created solely for this tutorial by TwistedTwigleg. They are released under the MIT license, so feel free to use them however you see fit!
-
-.. tip:: You can find the finished project at the bottom of this page.
-
-Getting everything ready
-------------------------
-
-Launch Godot and open up the project included in the starter assets.
-
-.. note:: While these assets are not necessarily required to use the scripts provided in this tutorial,
-          they will make the tutorial much easier to follow, as there are several premade scenes we
-          will be using throughout the tutorial series.
-
-First, you may notice there is already quite a bit set up. This includes a pre-built level, several instanced scenes placed around,
-some background music, and several GUI-related :ref:`MeshInstances <class_MeshInstance>` nodes.
-
-You may also notice that the GUI-related meshes already have a script attached to them. This is used to show whatever is inside the :ref:`Viewport <class_Viewport>`
-on the mesh. Feel free to take a look if you want, but this tutorial will not be going over how to use the :ref:`Viewport <class_Viewport>` nodes for making 3D GUI
-:ref:`MeshInstance <class_MeshInstance>` nodes.
-
-The other thing to notice, before we jump into writing the code, is how the :ref:`ARVROrigin <class_ARVROrigin>` node works. How it works is kind of hard to explain,
-especially if you have never used VR before, but here is the gist of it:
-The :ref:`ARVROrigin <class_ARVROrigin>` node is the center point of the room. If there is no room-scale tracking, then the :ref:`ARVROrigin <class_ARVROrigin>` will
-be directly below the player, but if there is room-scale tracking, then the :ref:`ARVROrigin <class_ARVROrigin>` will be the center of the tracked room.
-
-.. note:: This is a bit of a simplification, and honestly, I do not know enough about the various different VR headsets and how they work to give a more detailed
-          and complete explanation. Consider it like this: The :ref:`ARVROrigin <class_ARVROrigin>` is the center of the VR world. If there is
-          room tracking, the player can move away from the center point, the :ref:`ARVROrigin <class_ARVROrigin>` node, but only as far as the room scaling tracks.
-
-If you select the :ref:`ARVROrigin <class_ARVROrigin>` node, you may notice that the world scale is set to ``1.4``. This is because I originally made the world too big,
-and so I needed to scale the VR player slightly so they better fit the world. As mentioned earlier, keeping the scale relatively constant is very important!
-
-Another thing to notice here is how we have everything set up under the :ref:`ARVROrigin <class_ARVROrigin>` node. The player camera is an :ref:`ARVRCamera <class_ARVRCamera>`
-that represents the player's head in the game. The :ref:`ARVRCamera <class_ARVRCamera>` will be offset by the player's height, and if there is room tracking, then the camera
-can move around 3D space as well, relative to the :ref:`ARVROrigin <class_ARVROrigin>`. This is important to note, especially for later when we add teleporting.
-
-Notice how there is a :ref:`ColorRect <class_ColorRect>` node called ``Movement_Vignette``. This will be a vignette shader that will only be visible when the player is moving.
-We are going to use the vignette shader to help reduce motion sickness while moving in VR.
-The reason it is a child of :ref:`ARVROrigin <class_ARVROrigin>` is because we want it to easily access the VR controllers.
-
-The final thing to note is that there are two :ref:`ARVRController <class_ARVRController>` nodes, and these will represent the left and right controllers in 3D space.
-An :ref:`ARVRController <class_ARVRController>` with an ID of 1 is the left hand, while an :ref:`ARVRController <class_ARVRController>` with an ID of 2 is the right hand.
-
-Starting VR
------------
-
-First, let's get the VR up and going! While ``Game.tscn`` is open, select the ``Game`` node and make a new script called ``Game.gd``. Add the following code:
-
-.. tabs::
- .. code-tab:: gdscript GDScript
-
-    extends Spatial
-
-    func _ready():
-        var VR = ARVRServer.find_interface("OpenVR")
-        if VR and VR.initialize():
-            get_viewport().arvr = true
-            get_viewport().hdr = false
-
-            OS.vsync_enabled = false
-            Engine.target_fps = 90
-            # Also, the physics FPS in the project settings is also 90 FPS. This makes the physics
-            # run at the same frame rate as the display, which makes things look smoother in VR!
-
- .. code-tab:: csharp
-
-    using Godot;
-    using System;
-
-    public class Game : Spatial
-    {
-        public override void _Ready()
-        {
-            var vr = ARVRServer.FindInterface("OpenVR");
-            if (vr != null && vr.Initialize())
-            {
-                GetViewport().Arvr = true;
-                GetViewport().Hdr = false;
-
-                OS.VsyncEnabled = false;
-                Engine.TargetFps = 90;
-                // Also, the physics FPS in the project settings is also 90 FPS. This makes the physics
-                // run at the same frame rate as the display, which makes things look smoother in VR!
-            }
-        }
-    }
-
-For this to work, you will need to have the `OpenVR asset from the Asset Library <https://godotengine.org/asset-library/asset/150>`_. The OpenVR asset is included in the starter assets, but there may be newer
-versions that work better, so I would highly suggest deleting the ``addons`` folder, then going to the Asset Library and downloading the newest
-version.
-
-With that done, let's quickly go over what this script does.
-
-First, we find a VR interface from the ARVR server. We do this because by default Godot does not include any VR interfaces, but rather exposes an API so anyone can make
-AR/VR interfaces with GDNative/C++. Next, we check to see if an OpenVR interface was found, and then we initialize it.
-
-Assuming nothing went wrong with initializing, we then turn the main :ref:`Viewport <class_Viewport>` into an AR/VR viewport, by setting ``arvr`` to ``true``.
-We also set HDR to ``false``, since you cannot use HDR in OpenVR.
-
-Then, we disable V-Sync and set the target FPS to 90 frames per second. Most VR headsets run at 90 Hz, and since the game will display
-on both the VR headset and the computer's monitor, we want to disable V-Sync and set the target FPS manually, so the computer's monitor does not drag the VR display down to 60 FPS.
-
-.. note:: One thing to notice as well is that the physics FPS is also set to 90! This makes the physics run at the same frame rate as the display, which makes
-          things look smoother in VR.
-
-.. image:: img/starter_vr_tutorial_hands.png
-
-With that done, go ahead and give the game a try! If everything goes well, you will now be able to look around the world! If you have a VR headset with room tracking,
-you will be able to move around as far as the room tracking allows.
-
-Coding the controllers
-----------------------
-
-While perhaps interesting if we were making a VR film, we really want to do more than stand around and look. Currently, we cannot move outside of the room tracking boundaries
-(assuming your VR headset has room tracking) and we cannot interact with anything! Let's change that!
-
-You may have noticed that you have a pair of green and black hands following the controllers. Let's write the code for those controllers, which will allow the player to teleport
-around the world and allow the player to grab and release :ref:`RigidBody <class_RigidBody>` nodes.
-
-Open either ``Left_Controller.tscn`` or ``Right_Controller.tscn``. Feel free to look at how the scene is set up; there are only a couple things of note to point out.
-
-First, notice how there are a couple :ref:`Raycast <class_Raycast>` nodes. We will be using one :ref:`Raycast <class_Raycast>` to teleport around the game world (``Raycast``) and
-we will use the other for picking up objects (``GrabCast``) if the player is using :ref:`Raycast <class_Raycast>` nodes to pick up objects.
-
-The other thing to note is how there is an :ref:`Area <class_Area>` called ``Area``, that is a small sphere in the palm of the hand. This will be used to detect
-objects the player can pick up with that hand if the player is using :ref:`Area <class_Area>` nodes to pick up objects.
-
-We also have a larger :ref:`Area <class_Area>` called ``Sleep_Area``, which will be used to wake :ref:`RigidBody <class_RigidBody>` nodes when the hands get close.
-
-Select the root node, either ``Left_Controller`` or ``Right_Controller`` depending on which scene you chose, and create a new script called ``VR_Controller.gd``.
-Add the following to ``VR_Controller.gd``:
-
-.. tabs::
- .. code-tab:: gdscript GDScript
-
-    extends ARVRController
-
-    onready var grab_area = $Area
-    onready var grab_raycast = $GrabCast
-    onready var grab_pos_node = $Grab_Pos
-    onready var hand_mesh = $Hand
-    onready var teleport_raycast = $RayCast
-
-    var controller_velocity = Vector3(0, 0, 0)
-    var prior_controller_position = Vector3(0, 0, 0)
-    var prior_controller_velocities = []
-
-    var held_object = null
-    var held_object_data = {"mode":RigidBody.MODE_RIGID, "layer":1, "mask":1}
-
-    var grab_mode = "AREA"
-    var teleport_pos
-    var teleport_mesh
-    var teleport_button_down
-
-    const CONTROLLER_DEADZONE = 0.65
-
-    const MOVEMENT_SPEED = 1.5
-
-    var directional_movement = false
-
-    func _ready():
-        teleport_mesh = get_tree().root.get_node("Game/Teleport_Mesh")
-        teleport_button_down = false
-
-        grab_mode = "AREA"
-        get_node("Sleep_Area").connect("body_entered", self, "sleep_area_entered")
-        get_node("Sleep_Area").connect("body_exited", self, "sleep_area_exited")
-
-        connect("button_pressed", self, "button_pressed")
-        connect("button_release", self, "button_released")
-
-
-    func _physics_process(delta):
-
-        if teleport_button_down:
-            teleport_raycast.force_raycast_update()
-            if teleport_raycast.is_colliding():
-                if teleport_raycast.get_collider() is StaticBody:
-                    if teleport_raycast.get_collision_normal().y >= 0.85:
-                        teleport_pos = teleport_raycast.get_collision_point()
-                        teleport_mesh.global_transform.origin = teleport_pos
-
-
-        # Controller velocity
-        # --------------------
-        if get_is_active():
-            controller_velocity = Vector3(0, 0, 0)
-
-            if prior_controller_velocities.size() > 0:
-                for vel in prior_controller_velocities:
-                    controller_velocity += vel
-
-                # Get the average velocity, instead of just adding them together.
-                controller_velocity = controller_velocity / prior_controller_velocities.size()
-
-            prior_controller_velocities.append((global_transform.origin - prior_controller_position) / delta)
-
-            controller_velocity += (global_transform.origin - prior_controller_position) / delta
-            prior_controller_position = global_transform.origin
-
-            if prior_controller_velocities.size() > 30:
-                prior_controller_velocities.remove(0)
-
-        # --------------------
-
-        if held_object:
-            var held_scale = held_object.scale
-            held_object.global_transform = grab_pos_node.global_transform
-            held_object.scale = held_scale
-
-
-        # Directional movement
-        # --------------------
-        # NOTE: you may need to change this depending on which VR controllers
-        # you are using and which OS you are on.
-        var trackpad_vector = Vector2(-get_joystick_axis(1), get_joystick_axis(0))
-        var joystick_vector = Vector2(-get_joystick_axis(5), get_joystick_axis(4))
-
-        if trackpad_vector.length() < CONTROLLER_DEADZONE:
-            trackpad_vector = Vector2(0, 0)
-        else:
-            trackpad_vector = trackpad_vector.normalized() * ((trackpad_vector.length() - CONTROLLER_DEADZONE) / (1 - CONTROLLER_DEADZONE))
-
-        if joystick_vector.length() < CONTROLLER_DEADZONE:
-            joystick_vector = Vector2(0, 0)
-        else:
-            joystick_vector = joystick_vector.normalized() * ((joystick_vector.length() - CONTROLLER_DEADZONE) / (1 - CONTROLLER_DEADZONE))
-
-        var forward_direction = get_parent().get_node("Player_Camera").global_transform.basis.z.normalized()
-        var right_direction = get_parent().get_node("Player_Camera").global_transform.basis.x.normalized()
-
-        var movement_vector = (trackpad_vector + joystick_vector).normalized()
-
-        var movement_forward = forward_direction * movement_vector.x * delta * MOVEMENT_SPEED
-        var movement_right = right_direction * movement_vector.y * delta * MOVEMENT_SPEED
-
-        movement_forward.y = 0
-        movement_right.y = 0
-
-        if movement_right.length() > 0 or movement_forward.length() > 0:
-            get_parent().translate(movement_right + movement_forward)
-            directional_movement = true
-        else:
-            directional_movement = false
-        # --------------------
-
-
-    func button_pressed(button_index):
-
-        # If the trigger is pressed...
-        if button_index == 15:
-            if held_object:
-                if held_object.has_method("interact"):
-                    held_object.interact()
-
-            else:
-                if not teleport_mesh.visible and not held_object:
-                    teleport_button_down = true
-                    teleport_mesh.visible = true
-                    teleport_raycast.visible = true
-
-
-        # If the grab button is pressed...
-        if button_index == 2:
-            if teleport_button_down:
-                return
-
-            if not held_object:
-
-                var rigid_body = null
-
-                if grab_mode == "AREA":
-                    var bodies = grab_area.get_overlapping_bodies()
-                    if len(bodies) > 0:
-                        for body in bodies:
-                            if body is RigidBody:
-                                if not "NO_PICKUP" in body:
-                                    rigid_body = body
-                                    break
-
-                elif grab_mode == "RAYCAST":
-                    grab_raycast.force_raycast_update()
-                    if grab_raycast.is_colliding():
-                        if grab_raycast.get_collider() is RigidBody and not "NO_PICKUP" in grab_raycast.get_collider():
-                            rigid_body = grab_raycast.get_collider()
-
-
-                if rigid_body:
-
-                    held_object = rigid_body
-
-                    held_object_data["mode"] = held_object.mode
-                    held_object_data["layer"] = held_object.collision_layer
-                    held_object_data["mask"] = held_object.collision_mask
-
-                    held_object.mode = RigidBody.MODE_STATIC
-                    held_object.collision_layer = 0
-                    held_object.collision_mask = 0
-
-                    hand_mesh.visible = false
-                    grab_raycast.visible = false
-
-                    if held_object.has_method("picked_up"):
-                        held_object.picked_up()
-                    if "controller" in held_object:
-                        held_object.controller = self
-
-
-            else:
-
-                held_object.mode = held_object_data["mode"]
-                held_object.collision_layer = held_object_data["layer"]
-                held_object.collision_mask = held_object_data["mask"]
-
-                held_object.apply_impulse(Vector3(0, 0, 0), controller_velocity)
-
-                if held_object.has_method("dropped"):
-                    held_object.dropped()
-
-                if "controller" in held_object:
-                    held_object.controller = null
-
-                held_object = null
-                hand_mesh.visible = true
-
-                if grab_mode == "RAYCAST":
-                    grab_raycast.visible = true
-
-
-            get_node("AudioStreamPlayer3D").play(0)
-
-
-        # If the menu button is pressed...
-        if button_index == 1:
-            if grab_mode == "AREA":
-                grab_mode = "RAYCAST"
-
-                if not held_object:
-                    grab_raycast.visible = true
-            elif grab_mode == "RAYCAST":
-                grab_mode = "AREA"
-                grab_raycast.visible = false
-
-
-    func button_released(button_index):
-
-        # If the trigger button is released...
-        if button_index == 15:
-
-            if teleport_button_down:
-
-                if teleport_pos and teleport_mesh.visible:
-                    var camera_offset = get_parent().get_node("Player_Camera").global_transform.origin - get_parent().global_transform.origin
-                    camera_offset.y = 0
-
-                    get_parent().global_transform.origin = teleport_pos - camera_offset
-
-                teleport_button_down = false
-                teleport_mesh.visible = false
-                teleport_raycast.visible = false
-                teleport_pos = null
-
-
-    func sleep_area_entered(body):
-        if "can_sleep" in body:
-            body.can_sleep = false
-            body.sleeping = false
-
-    func sleep_area_exited(body):
-        if "can_sleep" in body:
-            body.can_sleep = true
-
-This is quite a bit of code to go through, so let's break it down bit by bit. Let's start with the class variables, which are
-variables outside of any/all functions.
-
-- ``controller_velocity``: The velocity the controller is moving at. We will calculate this by changes in position every physics frame.
-- ``prior_controller_position``: The controller's previous position. We will use this to calculate the controller's velocity.
-- ``prior_controller_velocities``: The last 30 calculated velocities (1/3 of a second worth of velocities, assuming the game is running at 90 FPS).
-- ``held_object``: The currently-held object, a :ref:`RigidBody <class_RigidBody>`, if there is one.
-- ``held_object_data``: The data of the currently-held object, used to reset the object when it is no longer being held.
-- ``grab_area``: The :ref:`Area <class_Area>` node used to grab objects.
-- ``grab_pos_node``: The position where held objects stay.
-- ``hand_mesh``: The hand mesh, used to represent the player's hand when they are not holding anything.
-- ``teleport_pos``: The position at which the teleport :ref:`Raycast <class_Raycast>` is aimed.
-- ``teleport_mesh``: The mesh used to represent the teleport position.
-- ``teleport_button_down``: A variable for tracking whether the teleport button is being held down or not.
-- ``teleport_raycast``: The teleport :ref:`Raycast <class_Raycast>` node, used for calculating the teleportation position.
-- ``CONTROLLER_DEADZONE``: The dead zone for both the trackpad and the joystick.
-- ``MOVEMENT_SPEED``: The speed the player moves at when moving using the trackpad and/or the joystick.
-- ``directional_movement``: A boolean to track whether the player is moving using this controller.
-
-_________
-
-Next, let's go through ``_ready``.
-
-Firstly, we get the teleport :ref:`Raycast <class_Raycast>` node and assign it to ``teleport_raycast``.
-
-Next, we get the teleport mesh; notice how we are getting it from ``Game/Teleport_Mesh`` using ``get_tree().root``. This is because we need the teleport mesh
-to be separate from the controller, so moving and rotating the controller does not affect the position and rotation of the teleportation mesh.
-
-Then we get the grab area, grab :ref:`Raycast <class_Raycast>`, and position node and assign them to the proper variables.
-
-We set the default grab mode to ``AREA`` so it uses the :ref:`Area <class_Area>` node to grab objects by default.
-
-Then we connect the ``body_entered`` and ``body_exited`` signals from the sleep area node, we get the hand mesh and assign it the proper variable, and finally
-we connect the ``button_pressed`` and ``button_released`` signals from the :ref:`ARVRController <class_ARVRController>`.
-
-_________
-
-Now let's go through ``_physics_process``.
-
-Firstly, we check to see if the teleportation button is down or not. If the teleportation button is down, we then force the teleportation :ref:`Raycast <class_Raycast>`
-to update, which will give us frame perfect collision detection. We then check to see if the :ref:`Raycast <class_Raycast>` is colliding with anything.
-
-Next, we check to see if the collision body the :ref:`Raycast <class_Raycast>` is colliding with is a :ref:`StaticBody <class_StaticBody>`. We do this to ensure the player
-can only teleport on :ref:`StaticBody <class_StaticBody>` nodes. We then check to see if the ``Y`` value returned by the :ref:`Raycast <class_Raycast>`'s
-``get_collision_normal`` function is more than ``0.85``, which is mostly pointing straight up. This allows the player only to teleport on fairly flat faces pointing upwards.
-
-If all those checks for the teleport :ref:`Raycast <class_Raycast>` return ``true``, we then set ``teleport_pos`` to the collision point, and we move the teleportation
-mesh to ``teleport_pos``.
-
-The next thing we check is to see if the :ref:`ARVRController <class_ARVRController>` is active or not. If the :ref:`ARVRController <class_ARVRController>` is active, then
-that means there is a controller and it is being tracked. If the controller is active, we then reset ``controller_velocity`` to an empty :ref:`Vector3 <class_Vector3>`.
-
-We then add all of the prior velocity calculations in ``prior_controller_velocities`` to ``controller_velocity``. By using the prior calculations, we get a smoother
-throwing/catching experience, although it is not perfect. We want to get the average of these velocities, as otherwise we'd get crazy high velocity numbers that are not realistic.
-
-Next, we calculate the velocity from the position where the controller is currently, from the position the controller was at. We can use this difference in position to help track
-the controller's velocity.
-
-We then add the velocity from the controller this physics frame and the last physics frame to ``controller_velocity``. We then update ``prior_controller_position`` to the
-current position, so we can use it in the calculations in the velocity next physics frame.
-
-.. note:: The way we are calculating velocity is not perfect by any means, since it relies on a consistent amount of frames per second.
-          Ideally, we would be able to find the velocity directly from the VR controller, but currently in OpenVR, there is no way to access the controller's velocity.
-          We can get pretty close to the real velocity by comparing positions between frames though, and this will work just fine for this project.
-
-Then, we check to see if we have more than 30 stored velocities (more than a third of a second). If there are more than 30, we remove the oldest velocity
-from ``prior_controller_velocities``.
-
-
-Next, we check to see whether there is a held object. If there is, we update the position and rotation of the held object to the
-position and rotation of ``grab_pos_node``. Because of how scale works, we need to temporarily store the scale and then reset the scale once we have updated the transform;
-otherwise, the scale would always be the same as the controller, which would ruin immersion if the player grabbed a scaled object.
-
-
-The last thing we are going to do in ``_physics_process`` is move the player if they are moving the trackpad/joystick on the VR controller.
-
-Firstly, we convert the axis values into :ref:`Vector2 <class_Vector2>` variables, so we can process them. We invert the X axis, so moving the trackpad/joystick left
-will move the player left.
-
-.. note:: Depending on your VR controller and OS, you may need to change the code so it gets the proper axis values!
-
-Next, we account for dead zones on both the trackpad and the joystick. The code for doing this is adapted from the link below, and I would highly recommend looking at it.
-
-.. tip:: You can find a great article explaining joystick dead zones `on Third Helix <http://www.third-helix.com/2013/04/12/doing-thumbstick-dead-zones-right.html>`_.
-
-One thing to note is how large we are making the dead zones. The reason we are using such large dead zones is so the player cannot move themselves accidentally by placing their
-finger on the center of the touchpad/joystick, which can make players experience motion sickness if they are not expecting it.
-
-Next, we get the forward and right directional vectors from the VR camera. We need these so we can move the player forward/backwards and right/left based on where
-they are currently looking.
-
-Then, we calculate how much the player will be moving by adding both the trackpad and the joystick vectors together and normalizing them.
-
-Next, we calculate how far the player will go forwards/backwards and right/left by multiplying the VR camera's directional vectors by the combined trackpad/joystick vector.
-
-We then remove movement on the Y axis so the player cannot fly/fall by moving using the trackpad/joystick.
-
-And finally, we move the player if there is any movement forwards/backwards or right/left. If we are moving the player, we set ``directional_movement`` accordingly.
-
-_________
-
-Now, let's look at ``button_pressed``.
-
-If the button pressed is button 15, which for the Windows Mixed Reality controllers is the trigger button, we will interact with the held object assuming the
-controller is holding one, and if the player is not holding an object, we will try to start teleporting.
-
-If the controller is holding an object, and the held object has a method/function called ``interact``, we call the ``interact`` function
-on the held object.
-
-If the controller is not holding an object, we then check to make sure the teleportation mesh is not visible. This check ensures the player cannot teleport with
-both hands/controllers at the same time. If the teleportation mesh is not visible, we set ``teleport_button_down`` to ``true``, make ``teleport_mesh`` visible,
-and make the teleportation raycast visible. This makes it where the teleportation mesh will follow the :ref:`Raycast <class_Raycast>` coming from the pointer
-finger of the hand.
-
-If the button pressed is button 2, which, for the Windows Mixed Reality controllers, is the grab/grip button, we will grab/throw an object.
-
-Firstly, we make sure the player is not trying to teleport, as we do not want the player to be able to grab something while in the middle of trying to teleport.
-
-Then, we check to see whether the controller is already holding a object or not.
-
-If the controller is not holding an object, we check to see which grab mode the player is using.
-
-If the player is using the ``AREA`` grab mode, we then get all the bodies overlapping the grab :ref:`Area <class_Area>`. We go through all the bodies in the
-grab :ref:`Area <class_Area>` and see if there is a :ref:`RigidBody <class_RigidBody>`. We also check to make sure any :ref:`RigidBody <class_RigidBody>` nodes in
-the :ref:`Area <class_Area>` do not have a variable called ``NO_PICKUP``, since we do not want to be able to pick up nodes with that variable.
-
-Assuming there is a :ref:`RigidBody <class_RigidBody>` node inside the grab :ref:`Area <class_Area>` that does not have a variable called ``NO_PICKUP``,
-we assign it to ``rigid_body`` for additional processing.
-
-If the player is using the ``RAYCAST`` grab mode, we first force the :ref:`Raycast <class_Raycast>` to update. We then check to see whether the :ref:`Raycast <class_Raycast>`
-is colliding with something.
-
-If the :ref:`Raycast <class_Raycast>` is colliding with something, we then check to see if what it is colliding with is a :ref:`RigidBody <class_RigidBody>`, and that it does not have
-a variable called ``NO_PICKUP``. If the :ref:`Raycast <class_Raycast>` is colliding with a :ref:`RigidBody <class_RigidBody>`, and it does not have a
-variable called ``NO_PICKUP``, we assign it to ``rigid_body`` for additional processing.
-
-If ``rigid_body`` is not ``null``, meaning we found a :ref:`RigidBody <class_RigidBody>` in the grab :ref:`Area <class_Area>`, we assign ``held_object`` to it.
-Then we store the now held :ref:`RigidBody <class_RigidBody>`'s information in ``held_object_data``. We are storing the :ref:`RigidBody <class_RigidBody>` mode, layer,
-and mask so later, when we drop it, we can reset all those variables back to what they were before we picked up the :ref:`RigidBody <class_RigidBody>`.
-
-We then set the held object's :ref:`RigidBody <class_RigidBody>` mode to ``MODE_STATIC`` and set the collision layer and mask to 0 so it cannot collide with any
-other physic bodies.
-
-We make the hand mesh invisible so it does not get in the way of the object we are holding (and also because I did not feel like animating the hand). We also make the
-grab :ref:`Raycast <class_Raycast>` invisible so the mesh used for showing the :ref:`Raycast <class_Raycast>` is no longer visible.
-
-If the :ref:`RigidBody <class_RigidBody>` we picked up has the ``picked_up`` method/function, we call it. If the :ref:`RigidBody <class_RigidBody>` we picked up has a
-variable called ``controller``, we set it to this controller.
-
-If the controller is not holding an object, and the button pressed is 2, we want to drop/throw the held object.
-
-Firstly, we set the held :ref:`RigidBody <class_RigidBody>`'s mode, layer, and mask back to what they were when we picked the object up.
-We then apply an impulse to the held object, using the controller's velocity as the force.
-
-If the previously held :ref:`RigidBody <class_RigidBody>` has a function called ``dropped``, we call it. If the :ref:`RigidBody <class_RigidBody>` has a variable
-called ``controller``, we set it to ``null``.
-
-Then, we set ``held_object`` to ``null``, since we are no longer holding any objects, and we make the hand mesh visible again.
-
-If we are using the ``RAYCAST`` grab mode, we make the :ref:`Raycast <class_Raycast>` visible so we can see the mesh used for showing the grab :ref:`Raycast <class_Raycast>`.
-
-Finally, regardless of whether we are grabbing an object or releasing it, we play the sound loaded into ``AudioStreamPlayer3D``, which is a pick-up/drop noise.
-
-The last thing we are doing in ``button_pressed`` is checking to see if the button pressed is 1, which, for the Windows Mixed Reality controllers, is the menu button.
-
-If the menu button is pressed, we change grab modes, and set the visibility of the grab :ref:`Raycast <class_Raycast>` so it is only visible when using ``RAYCAST`` as the grab mode.
-
-_________
-
-Let's look at ``button_released`` next.
-
-If the button released is button 15, the trigger, then we potentially want to teleport.
-
-Firstly, we check to see whether ``teleport_button_down`` is ``true``. If it is, that means the player is intending to teleport, while if it is ``false``, the player
-has released the trigger while holding an object.
-
-We then check to see if this controller has a teleport position, and we check to make sure the teleport mesh is visible.
-
-If both of those conditions are ``true``, we then calculate the offset the :ref:`ARVRCamera <class_ARVRCamera>` has from the :ref:`ARVROrigin <class_ARVROrigin>`. We do this
-because of how :ref:`ARVRCamera <class_ARVRCamera>` and :ref:`ARVROrigin <class_ARVROrigin>` work with room-scale tracking.
-
-Because we want to teleport the player in their current position to the teleport position, and remember, because of room-scale tracking, their current position can be offset from
-the origin, we have to figure out that offset so when we teleport, we can remove it so that player's current position is teleported to the teleport position.
-
-We set the Y value of the camera_offset to zero because we do not want to account for offsets in the player's height.
-
-Then, we teleport the :ref:`ARVROrigin <class_ARVROrigin>` to the teleport position, applying the camera offset.
-
-Regardless of whether we teleported or not, we reset all the teleport-related variables so the controller has to get new ones before teleporting again.
-
-_________
-
-Finally, let's look at ``sleep_area_entered`` and ``sleep_area_exited``.
-
-When a body enters or exists the sleep area, we check whether it has a variable called ``can_sleep``. If it does, we then set it to ``false`` and wake the body if it has entered
-the sleep area, while if it has exited, we set it to ``true`` so the :ref:`RigidBody <class_RigidBody>` nodes can sleep (which can decrease CPU usage).
-
-_________
-
-Okay, whew! That was a lot of code! Add the same script, ``VR_Controller.gd`` to the other controller so both controllers have the same script.
-
-Now go ahead and try the game again, and you should find you can teleport around by pressing the touch pad, and can grab and throw objects
-using the grab/grip buttons.
-
-Now, you may want to try moving using the trackpads and/or joysticks, but **it may make you motion sick!**
-
-One of the main reasons this can make you feel motion sick is because your vision tells you that you are moving, while your body is not moving.
-This conflict of signals makes the body feel sick, so let's fix it!
-
-Reducing motion sickness
-------------------------
-
-.. note:: There are plenty of ways to reduce motion sickness in VR, and there is no one perfect way to reduce motion sickness. See
-          `this page on the Oculus Developer Center <https://developer.oculus.com/design/latest/concepts/bp-locomotion/>`_
-          for more information on how to implement locomotion and reducing motion sickness.
-
-To help reduce motion sickness while moving, we are going to add a vignette effect that will only be visible while the player moves.
-
-Open up ``Movement_Vignette.tscn``, which you can find in the ``Scenes`` folder. Notice how it is just a :ref:`ColorRect <class_ColorRect>` node with a custom
-shader. Feel free to look at the custom shader if you want, it is just a slightly modified version of the vignette shader you can find in the Godot demo repository.
-
-With ``Movement_Vignette`` selected, make a new script called ``Movement_Vignette.gd``. Add the following code to ``Movement_Vignette.gd``:
-
-.. tabs::
- .. code-tab:: gdscript GDScript
-
-    extends ColorRect
-
-    var controller_one
-    var controller_two
-
-    func _ready():
-        yield(get_tree(), "idle_frame")
-        yield(get_tree(), "idle_frame")
-        yield(get_tree(), "idle_frame")
-        yield(get_tree(), "idle_frame")
-
-        var interface = ARVRServer.get_primary_interface()
-
-        rect_size = interface.get_render_targetsize()
-        rect_position = Vector2(0, 0)
-
-        controller_one = get_parent().get_node("Left_Controller")
-        controller_two = get_parent().get_node("Right_Controller")
-
-        visible = false
-
-
-    func _process(delta):
-
-        if not controller_one or not controller_two:
-            return
-
-        if controller_one.directional_movement or controller_two.directional_movement:
-            visible = true
-        else:
-            visible = false
-
-Because this script is fairly brief, let's quickly go over what it does.
-
-In ``_ready``, we wait for four frames. We do this to ensure the VR interface is ready and going.
-
-Next, we get the current VR interface, and resize the :ref:`ColorRect <class_ColorRect>` node's size and position so that it covers the entire view in VR.
-
-Then, we get the left and right controllers, assigning them to ``controller_one`` and ``controller_two``.
-
-We then make the vignette invisible by default.
-
-In ``_process``, we check to see if either of the controllers are moving the player by checking ``directional_movement``. If either controller is moving the player,
-we make the vignette visible, while if neither controller is moving the player, we make the vignette invisible.
-
-_________
-
-With that done, go ahead and try moving around with the joystick and/or the trackpad. You should find it is much less motion sickness-inducing than before!
-
-Let's add some special :ref:`RigidBody <class_RigidBody>` nodes we can interact with next.
-
-Adding destroyable targets
---------------------------
-
-Firstly, let's start by making some targets we will destroy in various ways with various special :ref:`RigidBody <class_RigidBody>` nodes.
-
-Open up ``Sphere_Target.tscn``, which you can find in the ``Scenes`` folder. ``Sphere.tscn`` is just a :ref:`StaticBody <class_StaticBody>`
-with a :ref:`CollisionShape <class_CollisionShape>`, a mesh, and a audio player.
-
-Select the ``Sphere_Target`` root node, the :ref:`StaticBody <class_StaticBody>` node, and make a new script called ``Sphere_Target.gd``. Add the following
-to ``Sphere_Target.gd``:
-
-.. tabs::
- .. code-tab:: gdscript GDScript
-
-    extends StaticBody
-
-    var destroyed = false
-    var destroyed_timer = 0
-    const DESTROY_WAIT_TIME = 80
-
-    var health = 80
-
-    const RIGID_BODY_TARGET = preload("res://Assets/RigidBody_Sphere.scn")
-
-    func _ready():
-        set_physics_process(false)
-
-    func _physics_process(delta):
-        destroyed_timer += delta
-        if destroyed_timer >= DESTROY_WAIT_TIME:
-            queue_free()
-
-
-    func damage(bullet_global_transform, damage):
-
-        if destroyed:
-            return
-
-        health -= damage
-
-        if health <= 0:
-
-            $CollisionShape.disabled = true
-            $Sphere_Target.visible = false
-
-            var clone = RIGID_BODY_TARGET.instance()
-            add_child(clone)
-            clone.global_transform = global_transform
-
-            destroyed = true
-            set_physics_process(true)
-
-            $AudioStreamPlayer.play()
-            get_tree().root.get_node("Game").remove_sphere()
-
-Let's go over how this script works, starting with the class variables.
-
-- ``destroyed``: A variable to track whether this target is destroyed or not.
-- ``destroyed_timer``: A variable to track how long the target has been destroyed.
-- ``DESTROY_WAIT_TIME``: A constant to tell the sphere target how long to wait before destroying/deleting itself.
-- ``health``: The amount of health the target has.
-- ``RIGID_BODY_TARGET``: The target broken into several smaller :ref:`RigidBody <class_RigidBody>` nodes.
-
-________
-
-Let's go over ``_ready``.
-
-All we are doing in ``_ready`` is setting ``_physics_process`` to ``false``. This is because we will only use ``_physics_process``
-for destroying the target, so we do not want to call it until the target is broken.
-
-________
-
-Next, let's go over ``_physics_process``.
-
-Firstly, we add time to ``destroyed_timer``. Then we check to see whether enough time has passed and we can destroy the target. If enough time has
-passed, we free/destroy the target using ``queue_free``.
-
-________
-
-Finally, let's go over ``damage``.
-
-Firstly, we check to make sure the target has not already been destroyed.
-
-Then, we remove however much damage the target has taken from the target's health.
-
-If the target has zero or less health, then it has taken enough damage to break.
-
-Firstly, we disable the collision shape and make the whole target mesh invisible.
-Next, we spawn/instance the :ref:`RigidBody <class_RigidBody>` version of the target, and instance it at this target's position.
-
-Then, we set ``destroyed`` to ``true`` and start processing ``_physics_process``.
-Finally, we play a sound, and remove a sphere from ``Game.gd`` by calling ``remove_sphere``.
-
-________
-
-Now, you may have noticed we are calling a function in ``Game.gd`` we have not made yet, so let's fix that!
-
-Firstly, open up ``Game.gd`` and add the following additional class variables:
-
-.. tabs::
- .. code-tab:: gdscript GDScript
-
-    var spheres_left = 10
-    var sphere_ui = null
-
-- ``spheres_left``: The amount of sphere targets left in the game world.
-- ``sphere_ui``: A reference to the sphere UI. We will use this later!
-
-Next, we need to add the ``remove_sphere`` function. Add the following to ``Game.gd``:
-
-.. tabs::
- .. code-tab:: gdscript GDScript
-
-    func remove_sphere():
-        spheres_left -= 1
-
-        if sphere_ui:
-            sphere_ui.update_ui(spheres_left)
-
-What this function does is it subtracts one from ``spheres_left``.
-
-Then, it checks to see whether ``sphere_ui`` is not ``null``, and if it is not, then it calls its ``update_ui`` function, passing in the amount of spheres left.
-We'll add the UI code later in this part.
-
-Now that we have destroyable targets, we need a way to destroy them!
-
-Adding a pistol
----------------
-
-Okay, let's add a pistol. Open up ``Pistol.tscn``, which you will find in the ``Scenes`` folder.
-
-There are a few things to note here. The first thing to note is how everything is rotated. This is to make the pistol rotate correctly when the player grabs it. The other thing to notice is
-how there is a laser sight mesh, and a flash mesh; both of these do what you'd expect: act as a laser pointer and muzzle flash, respectively.
-
-The other thing to notice is how there is a :ref:`Raycast <class_Raycast>` node at the end of the pistol. This is what we will be using to calculate where the bullets impact.
-
-Now that we have looked at the scene, let's write the code. Select the ``Pistol`` root node, the :ref:`RigidBody <class_RigidBody>` node, and make a new
-script called ``Pistol.gd``. Add the following code to ``Pistol.gd``:
-
-.. tabs::
- .. code-tab:: gdscript GDScript
-
-    extends RigidBody
-
-    onready var flash_mesh = $Pistol_Flash
-    onready var laser_sight_mesh = $LaserSight
-    onready var raycast = $RayCast
-
-    const FLASH_TIME = 0.25
-    var flash_timer = 0
-
-    var BULLET_DAMAGE = 20
-
-    func _ready():
-        flash_mesh.visible = false
-        laser_sight_mesh.visible = false
-
-    func _physics_process(delta):
-        if flash_timer > 0:
-            flash_timer -= delta
-            # If the flash has been visible enough, then make the flash mesh invisible.
-            if flash_timer <= 0:
-                flash_mesh.visible = false
-
-
-    # Called when the interact button is pressed while the object is held.
-    func interact():
-
-        if flash_timer <= 0:
-
-            flash_timer = FLASH_TIME
-            flash_mesh.visible = true
-
-            raycast.force_raycast_update()
-            if raycast.is_colliding():
-
-                var body = raycast.get_collider()
-
-                if body.has_method("damage"):
-                    body.damage(raycast.global_transform, BULLET_DAMAGE)
-                elif body.has_method("apply_impulse"):
-                    var direction_vector = raycast.global_transform.basis.z.normalized()
-                    body.apply_impulse((raycast.global_transform.origin - body.global_transform.origin).normalized(), direction_vector * 1.2)
-
-            $AudioStreamPlayer3D.play()
-
-
-    # Called when the object is picked up.
-    func picked_up():
-        laser_sight_mesh.visible = true
-
-
-    # Called when the object is dropped.
-    func dropped():
-        laser_sight_mesh.visible = false
-
-Let's go over what this script does, starting with the class variables:
-
-- ``flash_mesh``: The mesh used to make the muzzle flash.
-- ``FLASH_TIME``: The length of time the muzzle flash is visible.
-- ``flash_timer``: A variable to track how long the muzzle flash has been visible.
-- ``laser_sight_mesh``: A long rectangular mesh used for the laser sight.
-- ``raycast``: The raycast node used for the pistol firing.
-- ``BULLET_DAMAGE``: The amount of damage a single bullet does.
-
-________
-
-Let's go over ``_ready``.
-
-All we are doing here is getting the nodes and assigning them to the proper variables. We also make sure the flash and laser
-sight meshes are invisible.
-
-________
-
-Next, let's look at ``_physics_process``.
-
-Firstly, we check to see if the flash is visible. We do this by checking to see if ``flash_timer`` is more than zero. This is because ``flash_timer`` will be an inverted timer,
-a timer that counts down instead of counting up.
-
-If ``flash_timer`` is more than zero, we subtract ``delta`` from it and check to see whether it is equal to zero or less.
-If it is, we make the flash mesh invisible.
-
-This makes it where the flash mesh becomes invisible after ``FLASH_TIME`` many seconds have gone by.
-
-________
-
-Now, let's look at ``interact``, which is called when the trigger button on the VR controller is pressed and the pistol is being held.
-
-Firstly, we check to see if the flash timer is less than or equal to zero. This check makes it where we cannot fire when the flash is visible, limiting how often
-the pistol can fire.
-
-If we can fire, we reset ``flash_timer`` by setting it to ``FLASH_TIME``, and we make the flash mesh visible.
-
-We then update the :ref:`Raycast <class_Raycast>` and check to see if it is colliding with anything.
-
-If the :ref:`Raycast <class_Raycast>` is colliding with something, we get the collider. We check to see if the collider has the ``damage`` function, and if it does, we call it.
-If it does not, we then check to see if the collider has the ``apply_impulse`` function, and if it does, we call it after calculating the direction from the
-:ref:`Raycast <class_Raycast>` to the collider.
-
-Finally, regardless of whether the pistol hit something or not, we play the pistol firing sound.
-
-________
-
-Finally, let's look at ``picked_up`` and ``dropped``, which are called when the pistol is picked up and dropped, respectively.
-
-All we are doing in these functions is making the laser pointer visible when the pistol is picked up, and making it invisible when the pistol is dropped.
-
-________
-
-.. image:: img/starter_vr_tutorial_pistol.png
-
-With that done, go ahead and give the game a try! If you climb up the stairs and grab the pistols, you should be able to fire at the spheres and they will break!
-
-Adding a shotgun
-----------------
-
-Let's add a different type of weapon :ref:`RigidBody <class_RigidBody>`: a shotgun. This is fairly straightforward, as almost everything is the same as the pistol.
-
-Open up ``Shotgun.tscn``, which you can find in ``Scenes``. Notice how everything is more or less the same, but instead of a single :ref:`Raycast <class_Raycast>`,
-there are five, and there is no laser pointer.
-This is because a shotgun generally fires in a cone shape, and so we are going to emulate that by having several :ref:`Raycast <class_Raycast>` nodes, all rotated randomly
-in a cone shape, and I removed the laser pointer so the player has to aim without knowing for sure where the shotgun is pointing.
-
-Alright, select the ``Shotgun`` root node, the :ref:`RigidBody <class_RigidBody>` and make a new script called ``Shotgun.gd``. Add the following to ``Shotgun.gd``:
-
-.. tabs::
- .. code-tab:: gdscript GDScript
-
-    extends RigidBody
-
-    onready var flash_mesh = $Shotgun_Flash
-    onready var raycasts = $Raycasts
-
-    const FLASH_TIME = 0.25
-    var flash_timer = 0
-
-    var BULLET_DAMAGE = 30
-
-    func _ready():
-        flash_mesh.visible = false
-
-    func _physics_process(delta):
-        if flash_timer > 0:
-            flash_timer -= delta
-            if flash_timer <= 0:
-                flash_mesh.visible = false
-
-
-    # Called when the interact button is pressed while the object is held.
-    func interact():
-
-        if flash_timer <= 0:
-
-            flash_timer = FLASH_TIME
-            flash_mesh.visible = true
-
-            for raycast in raycasts.get_children():
-
-                raycast.rotation_degrees = Vector3(90 + rand_range(10, -10), 0, rand_range(10, -10))
-
-                raycast.force_raycast_update()
-                if raycast.is_colliding():
-
-                    var body = raycast.get_collider()
-
-                    # If the body has the damage method, then use that; otherwise, use apply_impulse.
-                    if body.has_method("damage"):
-                        body.damage(raycast.global_transform, BULLET_DAMAGE)
-                    elif body.has_method("apply_impulse"):
-                        var direction_vector = raycast.global_transform.basis.z.normalized()
-                        body.apply_impulse((raycast.global_transform.origin - body.global_transform.origin).normalized(), direction_vector * 4)
-
-            $AudioStreamPlayer3D.play()
-
-
-    func picked_up():
-        pass
-
-
-    func dropped():
-        pass
-
-You may have noticed this is almost exactly the same as the pistol, and indeed it is, so let's only go over what has changed.
-
-- ``raycasts``: The node that holds all of the five :ref:`Raycast <class_Raycast>` nodes used for the shotgun's firing.
-
-In ``_ready``, we get the ``Raycasts`` node, instead of just a single :ref:`Raycast <class_Raycast>`.
-
-The only other change, besides there being nothing in ``picked_up`` and ``dropped``, is in ``interact``.
-
-Now we go through each :ref:`Raycast <class_Raycast>` in ``raycasts``. We then rotate it on the X and Z axes, making within a 10 to ``-10`` cone.
-From there, we process each :ref:`Raycast <class_Raycast>` like we did the single :ref:`Raycast <class_Raycast>` in the pistol, nothing changed at all,
-we are just doing it five times, once for each :ref:`Raycast <class_Raycast>` in ``raycasts``.
-
-________
-
-Now you can find and fire the shotgun too! The shotgun is located around the back behind one of the walls (not in the building though!).
-
-Adding a bomb
--------------
-
-While both of those are well and good, let's add something we can throw next — a bomb!
-
-Open up ``Bomb.tscn``, which you will find in the ``Scenes`` folder.
-
-First, notice how there is a rather large :ref:`Area <class_Area>` node. This is the explosion radius for the bomb. Anything within this :ref:`Area <class_Area>` will be
-effected by the explosion when the bomb explodes.
-
-The other thing to note is how there are two sets of :ref:`Particles <class_Particles>`: one for smoke coming out of the fuse, and another for the explosion itself.
-Feel free to take a look at the :ref:`Particles <class_Particles>` nodes if you want!
-
-The only thing to notice is how long the explosion :ref:`Particles <class_Particles>` node will last, their lifetime, which is 0.75 seconds. We need to know this so we can time
-the removal of the bomb with the end of the explosion :ref:`Particles <class_Particles>`.
-
-Alright, now let's write the code for the bomb. Select the ``Bomb`` :ref:`RigidBody <class_RigidBody>` node and make a new script called ``Bomb.gd``. Add the following code to
-``Bomb.gd``:
-
-.. tabs::
- .. code-tab:: gdscript GDScript
-
-    extends RigidBody
-
-    onready var bomb_mesh = $Bomb
-    onready var explosion_area = $Area
-    onready var fuse_particles = $Fuse_Particles
-    onready var explosion_particles = $Explosion_Particles
-
-    const FUSE_TIME = 4
-    var fuse_timer = 0
-
-    var EXPLOSION_DAMAGE = 100
-    var EXPLOSION_TIME = 0.75
-    var explosion_timer = 0
-    var explode = false
-    var controller = null
-
-    func _ready():
-        set_physics_process(false)
-
-    func _physics_process(delta):
-
-        if fuse_timer < FUSE_TIME:
-
-            fuse_timer += delta
-
-            if fuse_timer >= FUSE_TIME:
-
-                fuse_particles.emitting = false
-                explosion_particles.one_shot = true
-                explosion_particles.emitting = true
-                bomb_mesh.visible = false
-
-                collision_layer = 0
-                collision_mask = 0
-                mode = RigidBody.MODE_STATIC
-
-                for body in explosion_area.get_overlapping_bodies():
-                    if body == self:
-                        pass
-                    else:
-                        if body.has_method("damage"):
-                            body.damage(global_transform.looking_at(body.global_transform.origin, Vector3(0, 1, 0)), EXPLOSION_DAMAGE)
-                        elif body.has_method("apply_impulse"):
-                            var direction_vector = body.global_transform.origin - global_transform.origin
-                            body.apply_impulse(direction_vector.normalized(), direction_vector.normalized() * 1.8)
-
-                explode = true
-                $AudioStreamPlayer3D.play()
-
-
-        if explode:
-
-            explosion_timer += delta
-            if explosion_timer >= EXPLOSION_TIME:
-
-                explosion_area.monitoring = false
-
-                if controller:
-                    controller.held_object = null
-                    controller.hand_mesh.visible = true
-
-                    if controller.grab_mode == "RAYCAST":
-                        controller.grab_raycast.visible = true
-
-                queue_free()
-
-
-    func interact():
-        set_physics_process(true)
-        fuse_particles.emitting = true
-
-
-    func picked_up():
-        pass
-
-    func dropped():
-        pass
-
-Let's go through what this script does, starting with the class variables:
-
-- ``bomb_mesh``: The :ref:`MeshInstance <class_MeshInstance>` used for the bomb mesh.
-- ``FUSE_TIME``: The length of time for which the fuse burns.
-- ``fuse_timer``: A variable for tracking how long the fuse has been burning.
-- ``explosion_area``: The :ref:`Area <class_Area>` node used for detecting what nodes are inside the explosion.
-- ``EXPLOSION_DAMAGE``: The amount of damage the explosion does.
-- ``EXPLOSION_TIME``: The length of time the explosion :ref:`Particles <class_Particles>` take (you can calculate this number by multiplying the particles ``lifetime`` by its ``speed scale``)
-- ``explosion_timer``: A variable for tracking how long the explosion has lasted.
-- ``explode``: A boolean for tracking whether the bomb has exploded.
-- ``fuse_particles``: The fuse :ref:`Particles <class_Particles>` node.
-- ``explosion_particles``: The explosion :ref:`Particles <class_Particles>` node.
-- ``controller``: The controller that is currently holding the bomb, if there is one. This is set by the controller, so we do not need to check anything outside of checking if it is ``null``.
-
-________
-
-Let's go through ``_ready``.
-
-Firstly, we get all the nodes and assign them to the proper variables for later use.
-
-Then, we make sure ``_physics_process`` is not going to be called. We do this since we will be using ``_physics_process`` only for the fuse and
-for destroying the bomb, so we do not want to trigger that early, we only want the fuse to start when the player interacts while holding a bomb.
-
-________
-
-Now, let's look at ``_physics_process``.
-
-Firstly we check to see whether ``fuse_timer`` is less than ``FUSE_TIME``. If ``fuse_timer`` is less than ``FUSE_TIME``, then the bomb must be burning down the fuse.
-
-We then add time to ``fuse_timer``, and check to see whether the bomb has waited long enough and has burned through the entire fuse.
-
-If the bomb has waited long enough, then we need to explode the bomb. We do this first by stopping the smoke :ref:`Particles <class_Particles>` from emitting, and
-making the explosion :ref:`Particles <class_Particles>` emit. We also hide the bomb mesh so it is no longer visible.
-
-Next, we make the set the collision layer and mask to zero, and set the :ref:`RigidBody <class_RigidBody>` mode to static. This makes it where the now exploded bomb cannot
-interact with the physics world, and so it will stay in place.
-
-Then, we go through everything inside the explosion :ref:`Area <class_Area>`. We make sure the bodies inside the explosion :ref:`Area <class_Area>` are not the bomb itself, since we
-do not want to explode the bomb with itself. We then check to see whether the bodies have the ``damage`` method/function, and if it does, we call that, while if it does not, we check to
-see if it has the ``apply_impulse`` method/function, and call that instead.
-
-Then, we set ``explode`` to ``true`` since the bomb has exploded, and we play a sound.
-
-Next, we check to see if the bomb has exploded, as we need to wait until the explosion :ref:`Particles <class_Particles>` are done.
-
-If the bomb has exploded, we add time to ``explosion_timer``. We then check to see if the explosion :ref:`Particles <class_Particles>` are done. If they are, we set the explosion
-:ref:`Area <class_Area>`'s monitoring property to ``false`` to ensure we do not get any bugs in the debugger, we make the controller drop the bomb if it is holding onto it,
-we make the grab :ref:`Raycast <class_Raycast>` visible if the grab mode is ``RAYCAST``, and we free/destroy the bomb using ``queue_free``.
-
-________
-
-Finally, let's look at ``interact``.
-
-All we are doing here is making it where ``_physics_process`` will be called, which will start the fuse.
-We also make the fuse :ref:`Particles <class_Particles>` start emitting, so smoke comes out the top of the bomb.
-
-________
-
-With that done, the bombs are ready to go! You can find them in the orange building. Because of how we are calculating velocity, it is easiest to throw bombs in a trusting-like
-motion as opposed to a more natural throwing like motion. The smooth curve of a throwing-like motion is harder to track, and the because of how we are tracking velocity, it does
-not always work.
-
-Adding a sword
---------------
-
-Finally, let's add a sword so we can slice through things!
-
-Open up ``Sword.tscn``, which you will find in ``Scenes``.
-
-There is not a whole lot to note here, but there is just one thing, and that is how the length of the blade of the sword is broken into several small :ref:`Area <class_Area>` nodes.
-This is because we need to roughly know where on the blade the sword collided, and this is the easiest (and only) way I could figure out how to do this.
-
-.. tip:: If you know how to find the point where an :ref:`Area <class_Area>` and a :ref:`CollisionObject <class_CollisionObject>` meet, please let me know and/or make a PR on the
-         Godot documentation! This method of using several small :ref:`Area <class_Area>` nodes works okay, but it is not ideal.
-
-Other than that, there really is not much of note, so let's write the code. Select the ``Sword`` root node, the :ref:`RigidBody <class_RigidBody>` and make a new script called
-``Sword.gd``. Add the following code to ``Sword.gd``:
-
-.. tabs::
- .. code-tab:: gdscript GDScript
-
-    extends RigidBody
-
-    const SWORD_DAMAGE = 20
-
-    var controller
-
-    func _ready():
-        $Damage_Area_01.connect("body_entered", self, "body_entered_sword", ["01"])
-        $Damage_Area_02.connect("body_entered", self, "body_entered_sword", ["02"])
-        $Damage_Area_03.connect("body_entered", self, "body_entered_sword", ["03"])
-        $Damage_Area_04.connect("body_entered", self, "body_entered_sword", ["04"])
-
-
-    # Called when the interact button is pressed while the object is held.
-    func interact():
-        pass
-
-
-    # Called when the object is picked up.
-    func picked_up():
-        pass
-
-
-    # Called when the object is dropped.
-    func dropped():
-        pass
-
-
-    func body_entered_sword(body, number):
-        if body == self:
-            pass
-        else:
-
-            var sword_part = null
-            if number == "01":
-                sword_part = get_node("Damage_Area_01")
-            elif number == "02":
-                sword_part = get_node("Damage_Area_02")
-            elif number == "03":
-                sword_part = get_node("Damage_Area_03")
-            elif number == "04":
-                sword_part = get_node("Damage_Area_04")
-
-            if body.has_method("damage"):
-                body.damage(sword_part.global_transform.looking_at(body.global_transform.origin, Vector3(0, 1, 0)), SWORD_DAMAGE)
-
-                get_node("AudioStreamPlayer3D").play()
-
-           elif body.has_method("apply_impulse"):
-
-                var direction_vector = sword_part.global_transform.origin - body.global_transform.origin
-
-                if not controller:
-                    body.apply_impulse(direction_vector.normalized(), direction_vector.normalized() * self.linear_velocity)
-                else:
-                    body.apply_impulse(direction_vector.normalized(), direction_vector.normalized() * controller.controller_velocity)
-
-                $AudioStreamPlayer3D.play()
-
-Let's go over what this script does, starting with the two class variables:
-
-- ``SWORD_DAMAGE``: The amount of damage a single sword slice does.
-- ``controller``: The controller that is holding the sword, if there is one. This is set by the controller, so we do not need to set it here, in ``Sword.gd``.
-
-________
-
-Let's go over ``_ready`` next.
-
-All we are doing here is connecting each of the :ref:`Area <class_Area>` nodes ``body_entered`` signal to the ``body_entered_sword`` function, passing in an additional argument,
-which will be the number of the damage :ref:`Area <class_Area>`, so we can figure out where on the sword the body collided.
-
-________
-
-Now let's go over ``body_entered_sword``.
-
-Firstly, we make sure the body the sword has collided with is not itself.
-
-Then we figure out which part of the sword the body collided with, using the passed-in number.
-
-Next, we check to see whether the body the sword collided with has the ``damage`` function, and if it does, we call it and play a sound.
-
-If it does not have the damage function, we then check to see whether it has the ``apply_impulse`` function. If it does, we then calculate the direction from the sword part the
-body collided with to the body. We then check to see whether the sword is being held or not.
-
-If the sword is not being held, we use the :ref:`RigidBody <class_RigidBody>`'s velocity as the force in ``apply_impulse``, while if the sword is being held, we use the
-controller's velocity as the force in the impulse.
-
-Finally, we play a sound.
-
-________
-
-.. image:: img/starter_vr_tutorial_sword.png
-
-With that done, you can now slice through the targets! You can find the sword in the corner in between the shotgun and the pistol.
-
-Updating the target UI
-----------------------
-
-Okay, let's update the UI as the sphere targets are destroyed.
-
-Open up ``Game.tscn`` and then expand the ``GUI`` :ref:`MeshInstance <class_MeshInstance>`. From there, expand the ``GUI`` :ref:`Viewport <class_Viewport>` node
-and then select the ``Base_Control`` node. Add a new script called ``Base_Control``, and add the following:
-
-.. tabs::
- .. code-tab:: gdscript GDScript
-
-    extends Control
-
-    var sphere_count_label = $Label_Sphere_Count
-
-    func _ready():
-        get_tree().root.get_node("Game").sphere_ui = self
-
-    func update_ui(sphere_count):
-        if sphere_count > 0:
-            sphere_count_label.text = str(sphere_count) + " Spheres remaining"
-        else:
-            sphere_count_label.text = "No spheres remaining! Good job!"
-
-Let's go over what this script does.
-
-First, in ``_ready``, we get the :ref:`Label <class_Label>` that shows how many spheres are left and assign it to the ``sphere_count_label`` class variable.
-Next, we get ``Game.gd`` by using ``get_tree().root`` and assign ``sphere_ui`` to this script.
-
-In ``update_ui``, we change the sphere :ref:`Label <class_Label>`'s text. If there is at least one sphere remaining, we change the text to show how many spheres are still
-left in the world. If there are no more spheres remaining, we change the text and congratulate the player.
-
-Adding the final special RigidBody
-----------------------------------
-
-Finally, before we finish this tutorial, let's add a way to reset the game while in VR.
-
-Open up ``Reset_Box.tscn``, which you will find in ``Scenes``. Select the ``Reset_Box`` :ref:`RigidBody <class_RigidBody>` node and make a new script called ``Reset_Box.gd``.
-Add the following code to ``Reset_Box.gd``:
-
-.. tabs::
- .. code-tab:: gdscript GDScript
-
-    extends RigidBody
-
-    var start_transform
-
-    var reset_timer = 0
-    const RESET_TIME = 120
-
-
-    func _ready():
-        start_transform = global_transform
-
-
-    func _physics_process(delta):
-        reset_timer += delta
-        if reset_timer >= RESET_TIME:
-            global_transform = start_transform
-            reset_timer = 0
-
-
-    # Called when the interact button is pressed while the object is held.
-    func interact():
-        get_tree().change_scene("res://Game.tscn")
-
-
-    # Called when the object is picked up.
-    func picked_up():
-        pass
-
-
-    # Called when the object is dropped.
-    func dropped():
-        global_transform = start_transform
-        reset_timer = 0
-
-Let's go over what this does.
-
-First, we get the starting global :ref:`Transform <class_Transform>` in ``_ready``, and assign it to ``start_transform``. We will use this to reset the position of the reset box every so often.
-
-In ``_physics_process``, we check to see if enough time has passed to reset. If it has, we reset the box's :ref:`Transform <class_Transform>` and then reset the timer.
-
-If the player interacts while holding the reset box, we reset the scene by calling ``get_tree().change_scene`` and passing in the path to the current scene. This resets/restarts
-the scene completely.
-
-When the reset box is dropped, we reset the :ref:`Transform <class_Transform>` and timer.
-
-________
-
-With that done, when you grab and interact with the reset box, the entire scene will reset/restart and you can destroy all the targets again!
-
-Final notes
------------
-
-.. image:: img/starter_vr_tutorial_sword.png
-
-Whew! That was a lot of work. Now you have a VR project!
-
-.. warning:: If you ever get lost, be sure to read over the code again!
-
-             You can download the finished project for this part here: :download:`VR_Starter_Tutorial_Complete.zip <files/VR_Starter_Tutorial_Complete.zip>`
-
-This will hopefully serve as an introduction to making fully-featured VR games in Godot! The code written here can be expanded to make puzzle games, action games,
-story-based games, and more!

+ 0 - 0
tutorials/vr/img/starter_vr_tutorial_hands.png → tutorials/vr/vr_starter_tutorial/img/starter_vr_tutorial_hands.png


+ 0 - 0
tutorials/vr/img/starter_vr_tutorial_pistol.png → tutorials/vr/vr_starter_tutorial/img/starter_vr_tutorial_pistol.png


+ 0 - 0
tutorials/vr/img/starter_vr_tutorial_sword.png → tutorials/vr/vr_starter_tutorial/img/starter_vr_tutorial_sword.png


+ 9 - 0
tutorials/vr/vr_starter_tutorial/index.rst

@@ -0,0 +1,9 @@
+VR Starer tutorial
+==================
+
+.. toctree::
+   :maxdepth: 1
+   :name: doc_vr_starter_tutorial
+
+   vr_starter_tutorial_part_one
+   vr_starter_tutorial_part_two

+ 1151 - 0
tutorials/vr/vr_starter_tutorial/vr_starter_tutorial_part_one.rst

@@ -0,0 +1,1151 @@
+.. _doc_vr_starter_tutorial_part_one:
+
+VR Starter Tutorial Part One
+============================
+
+Introduction
+------------
+
+.. image:: img/starter_vr_tutorial_sword.png
+
+This tutorial will show you how to make a beginner VR game project in Godot.
+
+Keep in mind, **one of the most important things when making VR content is getting the scale of your assets correct**!
+It can take lots of practice and iterations to get this right, but there are a few things you can do to make it easier:
+
+- In VR, 1 unit is typically considered 1 meter. If you design your assets around that standard, you can save yourself a lot of headache.
+- In your 3D modeling program, see if there is a way to measure and use real world distances. In Blender, you can use the MeasureIt add-on; in Maya, you can use the Measure Tool.
+- You can make rough models using a tool like `Google Blocks <https://vr.google.com/blocks/>`_, and then refine in another 3D modelling program.
+- Test often, as the assets can look dramatically different in VR than on a flat screen!
+
+Throughout the course of this tutorial, we will cover:
+
+- How to tell Godot to run in VR.
+- How to make a teleportation locomotion system that uses the VR controllers.
+- How to make a artificial movement locomotion system that uses the VR controllers.
+- How to create a :ref:`RigidBody <class_RigidBody>`-based system that allows for picking up, dropping, and throwing RigidBody nodes using the VR controllers.
+- How to create simple destroyable target.
+- How to create some special :ref:`RigidBody <class_RigidBody>`-based objects that can destroy the targets.
+
+.. tip:: While this tutorial can be completed by beginners, it is highly
+          advised to complete :ref:`doc_your_first_game`,
+          if you are new to Godot and/or game development.
+          
+          **Some experience with making 3D games is required** before going through this tutorial series.
+          This tutorial assumes you have experience with the Godot editor, GDScript, and basic 3D game development.
+          A OpenVR-ready headset and two OpenVR-ready controllers are required.
+          
+          This tutorial was written and tested using a Windows Mixed Reality headset and controllers. This project has also been tested on the HTC Vive. Code adjustments may be required
+          for other VR Headsets, such as the Oculus Rift.
+
+The Godot project for this tutorial is found on the `OpenVR GitHub repository <https://github.com/GodotVR/godot_openvr_fps>`_. The starter assets for this tutorial can be found in the releases
+section on the GitHub repository. The starter assets contain some 3D models, sounds, scripts, and scenes that are configured for this tutorial.
+
+.. note:: **Credits for the assets provided**:
+          
+          - The sky panorama was created by `CGTuts <https://cgi.tutsplus.com/articles/freebie-8-awesome-ocean-hdris--cg-5684>`_.
+          
+          - The font used is Titillium-Regular 
+          - - The font is licensed under the SIL Open Font License, Version 1.1
+          
+          - The audio used are from several different sources, all downloaded from the Sonniss #GameAudioGDC Bundle (`License PDF <https://sonniss.com/gdc-bundle-license/>`_) 
+          - - The folders where the audio files are stored have the same name as folders in the Sonniss audio bundle.
+          
+          - The OpenVR addon was created by `Bastiaan Olij <https://github.com/BastiaanOlij>`_ and is released under the MIT license. It can be found both on the `Godot Asset Library <https://godotengine.org/asset-library/asset/150>`_ and on `GitHub <https://github.com/GodotVR/godot-openvr-asset>`_. *3rd party code and libraries used in the OpenVR addon may be under a different license.*
+          
+          - The initial project, 3D models, and scripts were created by `TwistedTwigleg <https://github.com/TwistedTwigleg>`_ and is released under the MIT license.
+
+.. tip:: You can find the finished project on the `OpenVR GitHub repository <https://github.com/GodotVR/godot_openvr_fps>`_.
+
+
+Getting everything ready
+------------------------
+
+If you have not already, go to the `OpenVR GitHub repository <https://github.com/GodotVR/godot_openvr_fps>`_ and download the "Starter Assets" file from the releases. Once you have the
+starter assets downloaded, open up the project in Godot.
+
+.. note:: The starter assets are not required to use the scripts provided in this tutorial.
+          The starter assets include several premade scenes and scripts that will be used throughout the tutorial.
+
+When the project is first loaded, the Game.tscn scene will be opened. This will be the main scene used for the tutorial. It includes several nodes and scenes already placed
+throughout the scene, some background music, and several GUI-related :ref:`MeshInstance <class_MeshInstance>` nodes.
+
+_________________
+
+The GUI-related :ref:`MeshInstance <class_MeshInstance>` nodes already have scripts attached to them. These scripts will set the texture of a :ref:`Viewport <class_Viewport>`
+node to the albedo texture of the material of the :ref:`MeshInstance <class_MeshInstance>` node. This is used to display text within the VR project. Feel free to take a look
+at the script, ``GUI.gd``, if you want. We will not be going over how to to use :ref:`Viewport <class_Viewport>` nodes for displaying UI on :ref:`MeshInstance <class_MeshInstance>`
+nodes in this tutorial .
+
+If you are interested in how to use :ref:`Viewport <class_Viewport>` nodes for displaying UI on :ref:`MeshInstance <class_MeshInstance>` nodes, see the :ref:`doc_viewport_as_texture`
+tutorial. It covers how to use a :ref:`Viewport <class_Viewport>` as a render texture, along with how to apply that texture onto a :ref:`MeshInstance <class_MeshInstance>` node.
+
+_________________
+
+Before we jump into the tutorial, let's take a moment to talk about how the nodes used for VR work.
+
+The :ref:`ARVROrigin <class_ARVROrigin>` node is the center point of the VR tracking system. The position of the :ref:`ARVROrigin <class_ARVROrigin>` is the position
+the VR system considers the 'center' point on the floor. The :ref:`ARVROrigin <class_ARVROrigin>` has a `world scale` property that effects the size of the user within
+the VR scene. For this tutorial, it is set to `1.4`, as the world was originally just a tad to big. As mentioned earlier, keeping the scale relatively consistent is
+important in VR.
+
+The :ref:`ARVRCamera <class_ARVRCamera>` is the player's headset and view into the scene. The :ref:`ARVRCamera <class_ARVRCamera>` is offset on the Y axis by the VR user's height,
+which will be important later when we add teleportation locomotoin. If the VR system supports room tracking, then the :ref:`ARVRCamera <class_ARVRCamera>` will move as the player moves.
+This means that the :ref:`ARVRCamera <class_ARVRCamera>` is not guaranteed to be in the same position as the :ref:`ARVROrigin <class_ARVROrigin>` node.
+
+The :ref:`ARVRController <class_ARVRController>` node represents a VR controller. The :ref:`ARVRController <class_ARVRController>` will follow the position and rotation of the VR
+controller relative to the :ref:`ARVROrigin <class_ARVROrigin>` node. All of the input for the VR controllers happens through the :ref:`ARVRController <class_ARVRController>` node.
+An :ref:`ARVRController <class_ARVRController>` node with an ``ID`` of ``1`` represents the left VR controller, while an :ref:`ARVRController <class_ARVRController>` controller with an
+``ID`` of ``2`` represents the right VR controller.
+
+To summerize: 
+
+- The :ref:`ARVROrigin <class_ARVROrigin>` node is the center of the VR tracking system and is positioned on the floor.
+
+- The :ref:`ARVRCamera <class_ARVRCamera>` is the player's VR headset and view into the scene.
+
+- The :ref:`ARVRCamera <class_ARVRCamera>` node is offset on the Y axis by the user's height.
+
+- If the VR system supports room tracking, then the :ref:`ARVRCamera <class_ARVRCamera>` node may be offset on the X and Z axises as the player moves.
+
+- The :ref:`ARVRController <class_ARVRController>` nodes represent the VR controllers and handle all of the input from the VR controllers.
+
+
+Starting VR
+-----------
+
+Now that we have gone over the VR nodes, let's start working on the project. While in ``Game.tscn``, select the ``Game`` node and make a new script called ``Game.gd``.
+In the ``Game.gd`` file, add the following code:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    extends Spatial
+
+    func _ready():
+        var VR = ARVRServer.find_interface("OpenVR")
+        if VR and VR.initialize():
+            get_viewport().arvr = true
+            get_viewport().hdr = false
+
+            OS.vsync_enabled = false
+            Engine.target_fps = 90
+            # Also, the physics FPS in the project settings is also 90 FPS. This makes the physics
+            # run at the same frame rate as the display, which makes things look smoother in VR!
+
+ .. code-tab:: csharp
+
+    using Godot;
+    using System;
+
+    public class Game : Spatial
+    {
+        public override void _Ready()
+        {
+            var vr = ARVRServer.FindInterface("OpenVR");
+            if (vr != null && vr.Initialize())
+            {
+                GetViewport().Arvr = true;
+                GetViewport().Hdr = false;
+
+                OS.VsyncEnabled = false;
+                Engine.TargetFps = 90;
+                // Also, the physics FPS in the project settings is also 90 FPS. This makes the physics
+                // run at the same frame rate as the display, which makes things look smoother in VR!
+            }
+        }
+    }
+
+Let's go over what this code does.
+
+_________________
+
+In the ``_ready`` function, we first get the OpenVR VR interface using the ``find_interface`` function in the :ref:`ARVRServer <class_ARVRServer>` and assign it to a variable
+called `VR`. If the :ref:`ARVRServer <class_ARVRServer>` finds an interface with the name OpenVR, it will return it, otherwise it will return ``null``.
+
+.. note:: The OpenVR VR interface is not included with Godot by default. You will need to download the OpenVR asset from the
+          `Asset Library <https://godotengine.org/asset-library/asset/150>`_ or `GitHub <https://github.com/GodotVR/godot-openvr-asset>`_.
+
+The code then combines two conditionals, one to check if the `VR` variable is NOT null (``if VR``) and another calls the initialize function, which returns a boolean based on
+whether the OpenVR interface was able to initialize or not. If both of these conditionals return true, then we can turn the main Godot :ref:`Viewport <class_Viewport>` into
+an ARVR viewport.
+
+If the VR interface initialized successfully, we then get the root :ref:`Viewport <class_Viewport>` and set the `arvr` property to ``true``. This will tell Godot to use the initialized
+ARVR interface to drive the :ref:`Viewport <class_Viewport>` display. After setting the ``arvr`` property to ``true``, we set the ``hdr`` property to ``false``. We do this because
+most of the VR headsets do not currently support HDR rendering.
+
+.. note:: HDR support will be available for VR in Godot 3.2.
+
+Finally, we disable VSync so the Frames Per Second (FPS) is not capped by the computer monitor. After this we tell Godot to render at ``90`` frames per second, which is the
+standard for most VR headsets. Without disabling VSync, the normal computer monitor may limit the frame rate of the VR headset to the frame rate of the computer monitor.
+
+.. note:: In the project settings, under the ``Physics->Common`` tab, the physics FPS has been set to ``90``. This makes the physics engine run at the same frame rate as
+          the VR display, which makes physics reactions look smoother when in VR.
+
+_________________
+
+That is all we need to do for Godot to launch OpenVR within the project! Go ahead and give it a try if you want. Assuming everything works, you will be able to look around
+the world. If you have a VR headset with room tracking, then you will be able to move around the scene within the limits of the room tracking.
+
+Creating the controllers
+------------------------
+
+.. image:: img/starter_vr_tutorial_hands.png
+
+Right now all that the VR user can do is stand around, which isn't really what we are going for unless we are working on a VR film. Lets write the code for the
+VR controllers. We are going to write all of the code for the VR controllers in one go, so the code is rather long. That said, once we are finished you will be
+able to teleport around the scene, artificially move using the touchpad/joystick on the VR controller, and be able to pick up, drop, and throw
+:ref:`RigidBody <class_RigidBody>`-based nodes.
+
+First we need to open the scene used for the VR controllers. ``Left_Controller.tscn`` or ``Right_Controller.tscn``. Let's briefly go over how the scene is setup.
+
+How the VR controller scene is setup
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+In both scenes the root node is a ARVRController node. The only difference is that the ``Left_Controller`` scene has the ``Controller Id`` property set to ``1`` while
+the ``Right_Controller`` has the ``Controller Id`` property set to ``2``.
+
+.. note:: The :ref:`ARVRServer <class_ARVRServer>` attempts to use these two IDs for the left and right VR controllers. For VR systems that support more than 2
+          controllers/tracked-objects, these IDs may need adjusting.
+
+Next is the ``Hand`` :ref:`MeshInstance <class_MeshInstance>` node. This node is used to display the hand mesh that will be used when the VR controller is not holding onto a
+:ref:`RigidBody <class_RigidBody>` node. The hand in the ``Left_Controller`` scene is a left hand, while the hand on the ``Right_Controller`` scene is a right hand.
+
+The node named ``Raycast`` is a :ref:`Raycast <class_Raycast>` node that is used for aiming where to teleport to when the VR controller is teleporting.
+The length of the :ref:`Raycast <class_Raycast>` is set to ``-16`` on the Y axis and is rotated so that it points out of the pointer finger of the hand. The ``Raycast`` node has
+a single child node, ``Mesh``, that is a :ref:`MeshInstance <class_MeshInstance>`. This is used for visually showing where the teleportation :ref:`Raycast <class_Raycast>` is aiming.
+
+The node named ``Area`` is a :ref:`Area <class_Area>` node will be used for grabbing :ref:`RigidBody <class_RigidBody>`-based nodes when the VR controller grab mode is set to ``AREA``.
+The ``Area`` node has a single child node, ``CollisionShape``, that defines a sphere :ref:`CollisionShape <class_CollisionShape>`. When the VR controller is not holding any objects and the grab button is pressed,
+the first :ref:`RigidBody <class_RigidBody>`-based node within the ``Area`` node will be picked up.
+
+Next is a :ref:`Position3D <class_Position3D>` node called ``Grab_Pos``. This is used to define the position that grabbed :ref:`RigidBody <class_RigidBody>` nodes will follow then
+they are held by the VR controller.
+
+A large :ref:`Area <class_Area>` node called ``Sleep_Area`` is used to disable sleeping for any RigidBody nodes within its :ref:`CollisionShape <class_CollisionShape>`,
+simple called ``CollisionShape``. This is needed because if a :ref:`RigidBody <class_RigidBody>` node falls asleep, then the VR controller will be unable to grab it.
+By using ``Sleep_Area``, we can write code that makes any :ref:`RigidBody <class_RigidBody>` node within it not able to sleep, therefore allowing the VR controller to grab it.
+
+An :ref:`AudioStreamPlayer3D <class_AudioStreamPlayer3D>` node called ``AudioStreamPlayer3D`` has a sound loaded that we will use when an object has been picked up, dropped
+or thrown by the VR controller. While this is not necessary for the functionality of the VR controller, it makes grabbing and dropping objects feel more natural.
+
+Finally, the last nodes are the ``Grab_Cast`` node and it's only child node, ``Mesh``. The ``Grab_Cast`` node will be used for grabbing :ref:`RigidBody <class_RigidBody>`-based
+nodes when the VR controller grab mode is set to ``RAYCAST``. This will allow the VR controller to grab objects that are just slightly out of reach using a Raycast. The ``Mesh``
+node is used for visually showing where the teleportation :ref:`Raycast <class_Raycast>` is aiming.
+
+That is a quick overview of how the VR controller scenes are setup, and how we will be using the nodes to provide the functionality for them. Now that we have looked at the
+VR controller scene, let's write the code that will drive them.
+
+The code for the VR controllers
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Select the root node of the scene, either ``Right_Controller`` or ``Left_Controller``, and make a new script called ``VR_Controller.gd``. Both scenes will be using
+the same script, so it doesn't matter which you use first. With ``VR_Controller.gd`` opened, add the following code:
+
+.. tip:: You can copy and paste the code from this page directly into the script editor.
+         
+         If you do this, all of the code copied will be using spaces instead of tabs.
+
+         To convert the spaces to tabs in the script editor, click the ``Edit`` menu and select ``Convert Indent To Tabs``.
+         This will convert all the spaces into tabs. You can select ``Convert Indent To Spaces`` to convert tabs back into spaces.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+    extends ARVRController
+
+    var controller_velocity = Vector3(0,0,0)
+    var prior_controller_position = Vector3(0,0,0)
+    var prior_controller_velocities = []
+
+    var held_object = null
+    var held_object_data = {"mode":RigidBody.MODE_RIGID, "layer":1, "mask":1}
+
+    var grab_area
+    var grab_raycast
+    
+    var grab_mode = "AREA"
+    var grab_pos_node
+
+    var hand_mesh
+    var hand_pickup_drop_sound
+
+    var teleport_pos = Vector3.ZERO
+    var teleport_mesh
+    var teleport_button_down
+    var teleport_raycast
+
+    # A constant to define the dead zone for both the trackpad and the joystick.
+    # See (http://www.third-helix.com/2013/04/12/doing-thumbstick-dead-zones-right.html)
+    # for more information on what dead zones are, and how we are using them in this project.
+    const CONTROLLER_DEADZONE = 0.65
+
+    const MOVEMENT_SPEED = 1.5
+
+    const CONTROLLER_RUMBLE_FADE_SPEED = 2.0
+
+    var directional_movement = false
+
+
+    func _ready():
+        # Ignore the warnings the from the connect function calls.
+        # (We will not need the returned values for this tutorial)
+        # warning-ignore-all:return_value_discarded
+
+        teleport_raycast = get_node("RayCast")
+        
+        teleport_mesh = get_tree().root.get_node("Game/Teleport_Mesh")
+        
+        teleport_button_down = false
+        teleport_mesh.visible = false
+        teleport_raycast.visible = false
+        
+        grab_area = get_node("Area")
+        grab_raycast = get_node("Grab_Cast")
+        grab_pos_node = get_node("Grab_Pos")
+        
+        grab_mode = "AREA"
+        grab_raycast.visible = false
+        
+        get_node("Sleep_Area").connect("body_entered", self, "sleep_area_entered")
+        get_node("Sleep_Area").connect("body_exited", self, "sleep_area_exited")
+        
+        hand_mesh = get_node("Hand")
+        hand_pickup_drop_sound = get_node("AudioStreamPlayer3D")
+        
+        connect("button_pressed", self, "button_pressed")
+        connect("button_release", self, "button_released")
+
+
+    func _physics_process(delta):
+        if rumble > 0:
+            rumble -= delta * CONTROLLER_RUMBLE_FADE_SPEED
+            if rumble < 0:
+                rumble = 0
+        
+        if teleport_button_down == true:
+            teleport_raycast.force_raycast_update()
+            if teleport_raycast.is_colliding():
+                if teleport_raycast.get_collider() is StaticBody:
+                    if teleport_raycast.get_collision_normal().y >= 0.85:
+                        teleport_pos = teleport_raycast.get_collision_point()
+                        teleport_mesh.global_transform.origin = teleport_pos
+        
+        
+        if get_is_active() == true:
+            _physics_process_update_controller_velocity(delta)
+        
+        if held_object != null:
+            var held_scale = held_object.scale
+            held_object.global_transform = grab_pos_node.global_transform
+            held_object.scale = held_scale
+        
+        _physics_process_directional_movement(delta);
+
+
+    func _physics_process_update_controller_velocity(delta):
+        controller_velocity = Vector3(0,0,0)
+
+        if prior_controller_velocities.size() > 0:
+            for vel in prior_controller_velocities:
+                controller_velocity += vel
+            
+            controller_velocity = controller_velocity / prior_controller_velocities.size()
+        
+        var relative_controller_position = (global_transform.origin - prior_controller_position)
+        
+        controller_velocity += relative_controller_position
+        
+        prior_controller_velocities.append(relative_controller_position)
+        
+        prior_controller_position = global_transform.origin
+        
+        controller_velocity /= delta;
+        
+        if prior_controller_velocities.size() > 30:
+            prior_controller_velocities.remove(0)
+
+
+    func _physics_process_directional_movement(delta):
+        var trackpad_vector = Vector2(-get_joystick_axis(1), get_joystick_axis(0))
+        var joystick_vector = Vector2(-get_joystick_axis(5), get_joystick_axis(4))
+        
+        if trackpad_vector.length() < CONTROLLER_DEADZONE:
+            trackpad_vector = Vector2(0,0)
+        else:
+            trackpad_vector = trackpad_vector.normalized() * ((trackpad_vector.length() - CONTROLLER_DEADZONE) / (1 - CONTROLLER_DEADZONE))
+        
+        if joystick_vector.length() < CONTROLLER_DEADZONE:
+            joystick_vector = Vector2(0,0)
+        else:
+            joystick_vector = joystick_vector.normalized() * ((joystick_vector.length() - CONTROLLER_DEADZONE) / (1 - CONTROLLER_DEADZONE))
+        
+        var forward_direction = get_parent().get_node("Player_Camera").global_transform.basis.z.normalized()
+        var right_direction = get_parent().get_node("Player_Camera").global_transform.basis.x.normalized()
+        
+        # Because the trackpad and the joystick will both move the player, we can add them together and normalize
+        # the result, giving the combined movement direction
+        var movement_vector = (trackpad_vector + joystick_vector).normalized()
+        
+        var movement_forward = forward_direction * movement_vector.x * delta * MOVEMENT_SPEED
+        var movement_right = right_direction * movement_vector.y * delta * MOVEMENT_SPEED
+        
+        movement_forward.y = 0
+        movement_right.y = 0
+        
+        if (movement_right.length() > 0 or movement_forward.length() > 0):
+            get_parent().global_translate(movement_right + movement_forward)
+            directional_movement = true
+        else:
+            directional_movement = false
+
+
+    func button_pressed(button_index):
+        if button_index == 15:
+            _on_button_pressed_trigger()
+        
+        if button_index == 2:
+            _on_button_pressed_grab()
+            
+        if button_index == 1:
+            _on_button_pressed_menu()
+
+
+    func _on_button_pressed_trigger():
+        if held_object == null:
+            if teleport_mesh.visible == false:
+                teleport_button_down = true
+                teleport_mesh.visible = true
+                teleport_raycast.visible = true
+        else:
+            if held_object is VR_Interactable_Rigidbody:
+                held_object.interact()
+
+
+    func _on_button_pressed_grab():
+        if teleport_button_down == true:
+            return
+        
+        if held_object == null:
+            _pickup_rigidbody()
+        else:
+            _throw_rigidbody()
+        
+        hand_pickup_drop_sound.play()
+
+
+    func _pickup_rigidbody():
+        var rigid_body = null
+        
+        if grab_mode == "AREA":
+            var bodies = grab_area.get_overlapping_bodies()
+            if len(bodies) > 0:
+                for body in bodies:
+                    if body is RigidBody:
+                        if !("NO_PICKUP" in body):
+                            rigid_body = body
+                            break
+        
+        elif grab_mode == "RAYCAST":
+            grab_raycast.force_raycast_update()
+            if (grab_raycast.is_colliding()):
+                var body = grab_raycast.get_collider()
+                if body is RigidBody:
+                    if !("NO_PICKUP" in body):
+                        rigid_body = body
+        
+        
+        if rigid_body != null:
+            
+            held_object = rigid_body
+            
+            held_object_data["mode"] = held_object.mode
+            held_object_data["layer"] = held_object.collision_layer
+            held_object_data["mask"] = held_object.collision_mask
+            
+            held_object.mode = RigidBody.MODE_STATIC
+            held_object.collision_layer = 0
+            held_object.collision_mask = 0
+            
+            hand_mesh.visible = false
+            grab_raycast.visible = false
+            
+            if held_object is VR_Interactable_Rigidbody:
+                held_object.controller = self
+                held_object.picked_up()
+
+
+    func _throw_rigidbody():
+        if held_object == null:
+            return
+        
+        held_object.mode = held_object_data["mode"]
+        held_object.collision_layer = held_object_data["layer"]
+        held_object.collision_mask = held_object_data["mask"]
+        
+        held_object.apply_impulse(Vector3(0, 0, 0), controller_velocity)
+        
+        if held_object is VR_Interactable_Rigidbody:
+            held_object.dropped()
+            held_object.controller = null
+        
+        held_object = null
+        hand_mesh.visible = true
+        
+        if grab_mode == "RAYCAST":
+            grab_raycast.visible = true
+
+
+    func _on_button_pressed_menu():
+        if grab_mode == "AREA":
+            grab_mode = "RAYCAST"
+            if held_object == null:
+                grab_raycast.visible = true
+        
+        elif grab_mode == "RAYCAST":
+            grab_mode = "AREA"
+            grab_raycast.visible = false
+
+
+    func button_released(button_index):
+        if button_index == 15:
+            _on_button_released_trigger()
+
+
+    func _on_button_released_trigger():
+        if teleport_button_down == true:
+            
+            if teleport_pos != null and teleport_mesh.visible == true:
+                var camera_offset = get_parent().get_node("Player_Camera").global_transform.origin - get_parent().global_transform.origin
+                camera_offset.y = 0
+                
+                get_parent().global_transform.origin = teleport_pos - camera_offset
+            
+            teleport_button_down = false
+            teleport_mesh.visible = false
+            teleport_raycast.visible = false
+            teleport_pos = null
+
+
+    func sleep_area_entered(body):
+        if "can_sleep" in body:
+            body.can_sleep = false
+            body.sleeping = false
+
+
+    func sleep_area_exited(body):
+        if "can_sleep" in body:
+            # Allow the CollisionBody to sleep by setting the "can_sleep" variable to true
+            body.can_sleep = true
+
+This is quite a bit of code to go through. Let's go through what the code does step-by-step.
+
+Explaining the VR controller code
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+First, let's go through all of the class variables in the script:
+
+* ``controller_velocity``: A variable to hold a rough approximation of the VR controller's velocity.
+* ``prior_controller_position``: A variable to hold the VR controller's last position in 3D space.
+* ``prior_controller_velocities``: An Array to hold the last 30 calculated VR controller velocities. This is used to smooth the velocity calculations over time.
+* ``held_object``: A variable to hold a reference to the object the VR controller is holding. If the VR controller is not holding any objects, this variable will be ``null``.
+* ``held_object_data``: A dictionary to hold data for the :ref:`RigidBody <class_RigidBody>` node being held by the VR controller. This is used to reset the :ref:`RigidBody <class_RigidBody>`'s data when it is no longer held.
+* ``grab_area``: A variable to hold the :ref:`Area <class_Area>` node used to grab objects with the VR controller.
+* ``grab_raycast``: A variable to hold the :ref:`Raycast <class_Raycast>` node used to grab objects with the VR controller.
+* ``grab_mode``: A variable to define the grab mode the VR controller is using. There are only two modes for grabbing objects in this tutorial, ``AREA`` and ``RAYCAST``.
+* ``grab_pos_node``: A variable to hold the node that will be used to update the position and rotation of held objects.
+* ``hand_mesh``: A variable to hold the :ref:`MeshInstance <class_MeshInstance>` node that contains the hand mesh for the VR controller. This mesh will be shown when the VR controller is not holding anything.
+* ``hand_pickup_drop_sound``: A variable to hold the :ref:`AudioStreamPlayer3D <class_AudioStreamPlayer3D>` node that contains the pickup/drop sound.
+* ``teleport_pos``: A variable to hold the position the player will be teleported to when the VR controller teleports the player.
+* ``teleport_mesh``: A variable to hold the :ref:`MeshInstance <class_MeshInstance>` node used to show where the player is teleporting to.
+* ``teleport_button_down``: A variable used to track whether the controller's teleport button is held down. This will be used to detect if this VR controller is trying to teleport the player.
+* ``teleport_raycast``: A variable to hold the :ref:`Raycast <class_Raycast>` node used to calculate the teleport position. This node also has a :ref:`MeshInstance <class_MeshInstance>` that acts as a 'laser sight' for aiming.
+* ``CONTROLLER_DEADZONE``: A constant to define the deadzone for both the trackpad and the joystick on the VR controller. See the note below for more information.
+* ``MOVEMENT_SPEED``: A constant to define the speed the player moves at when using the trackpad/joystick to move artificially.
+* ``CONTROLLER_RUMBLE_FADE_SPEED``: A constant to define how fast the VR controller rumble fades.
+* ``directional_movement``: A variable to hold whether this VR controller is moving the player using the touchpad/joystick.
+
+.. note:: You can find a great article explaining all about how to handle touchpad/joystick dead zones here: http://www.third-helix.com/2013/04/12/doing-thumbstick-dead-zones-right.html
+          
+          We are using a translated version of the scaled radial dead zone code provided in that article for the VR controller's joystick/touchpad.
+          The article is a great read, and I highly suggest giving it a look!
+
+That is quite a few class variables. Most of them are used to hold references to nodes we will need throughout the code. Next let's start looking at the functions, starting
+with the ``_ready`` function.
+
+_________________
+
+``_ready`` function step-by-step explanation
+""""""""""""""""""""""""""""""""""""""""""""
+
+First we tell Godot to silence the warnings about not using the values returned by the ``connect`` function. We will not need the returned
+values for this tutorial.
+
+Next we get the :ref:`Raycast <class_Raycast>` node we are going to use for determining the position for teleporting and assign it to the ``teleport_raycast`` variable.
+We then get the :ref:`MeshInstance <class_MeshInstance>` node that we will use to show where the player will be teleporting to. The node we are using for teleporting
+is a child of the ``Game`` scene. We do this so the teleport mesh node is not effected by changes in the VR controller, and so the teleport mesh can be used by both VR controllers.
+
+Then the ``teleport_button_down`` variable is set to false, ``teleport_mesh.visible`` is set to ``false``, and ``teleport_raycast.visible`` is set to ``false``. This sets up the variables
+for teleporting the player into their initial, not teleporting the player, state.
+
+The code then gets the ``grab_area`` node, the ``grab_raycast`` node, and the ``grab_pos_node`` node and assigns them all to their respective variables for use later.
+
+Next the ``grab_mode`` is set to ``AREA`` so the VR controller will attempt to grab objects using the :ref:`Area <class_Area>` node defined in ``grab_area`` when the VR controller's
+grab/grip button is pressed. We also set the ``grab_raycast`` node's ``visible`` property to ``false`` so the 'laser sight' child node of ``grab_raycast`` is not visible.
+
+After that we connect the ``body_entered`` and ``body_exited`` signals from the ``Sleep_Area`` node in the VR controller to the ``sleep_area_entered`` and ``sleep_area_exited`` functions.
+The ``sleep_area_entered`` and ``sleep_area_exited`` functions will be used to make :ref:`RigidBody <class_RigidBody>` nodes unable to sleep when nearby the VR controller.
+
+Then the ``hand_mesh`` and ``hand_pickup_drop_sound`` nodes are gotten and assigned them to their respective variables for use later.
+
+Finally, the ``button_pressed`` and ``button_release`` signals in the :ref:`ARVRController <class_ARVRController>` node, which the VR controller extends, are connected to the
+``button_pressed`` and ``button_released`` functions respectively. This means that when a button on the VR controller is pressed or released, the ``button_pressed`` or ``button_released``
+functions defined in this script will be called.
+
+
+``_physics_process`` function step-by-step explanation
+""""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+First we check to see if the ``rumble`` variable is more than zero. If the ``rumble`` variable, which is a property of the :ref:`ARVRController <class_ARVRController>` node, is more
+than zero then the VR controller rumbles.
+
+If the ``rumble`` variable is more than zero, then we reduce the rumble by ``CONTROLLER_RUMBLE_FADE_SPEED`` every second by subtracting ``CONTROLLER_RUMBLE_FADE_SPEED`` multiplied by delta.
+There is then a ``if`` condition to check if ``rumble`` is less than zero, which sets ``rumble`` to zero if its value is less than zero.
+
+This small section of code is all we need for reducing the VR controller's rumble. Now when we set ``rumble`` to a value, this code will automatically make it fade over time.
+
+_________________
+
+The first section of code checks to see if the ``teleport_button_down`` variable is equal to ``true``, which means this VR controller is trying to teleport.
+
+If ``teleport_button_down`` is equal to ``true``, we force the ``teleport_raycast`` :ref:`Raycast <class_Raycast>` node to update using the ``force_raycast_update`` function.
+The ``force_raycast_update`` function will update the properties within the :ref:`Raycast <class_Raycast>` node with the latest version of the physics world.
+
+The code then checks to see if the ``teleport_raycast`` collided with anything by checking of the ``is_colliding`` function in ``teleport_raycast`` is true. If the :ref:`Raycast <class_Raycast>`
+collided with something, we then check to see if the :ref:`PhysicsBody <class_PhysicsBody>` the raycast collided with is a :ref:`StaticBody <class_StaticBody>` or not. We then check to
+see if the collision normal vector returned by the raycast is greater than or equal to ``0.85`` on the Y axis.
+
+.. note:: We do this because we do not want the user to be able to teleport onto RigidBody nodes and we only want the player to be able to teleport on floor-like surfaces.
+
+If all these conditions are met, then we assign the ``teleport_pos`` variable to the ``get_collision_point`` function in ``teleport_raycast``. This will assign ``teleport_pos`` to the
+position the raycast collided at in world space. We then move the ``teleport_mesh`` to the world position stored in ``teleport_pos``.
+
+This section of code will get the position the player is aiming at with the teleportation raycast and update the teleportation mesh, giving a visual update on where the user will be teleporting
+to when the release the teleport button.
+
+_________________
+
+The next section of code first checks to see if the VR controller is active through the ``get_is_active`` function, which is defined by :ref:`ARVRController <class_ARVRController>`. If the
+VR controller is active, then it calls the ``_physics_process_update_controller_velocity`` function.
+
+The ``_physics_process_update_controller_velocity`` function will calculate the VR controller's velocity through changes in position. It is not perfect, but this process gets a rough
+idea of the velocity of the VR controller, which is fine for the purposes of this tutorial.
+
+_________________
+
+The next section of code checks to see if the VR controller is holding an object by checking to see if the ``held_object`` variable is not equal to ``null``.
+
+If the VR controller is holding an object, we first store it's scale in a temporary variable called ``held_scale``. We then set the ``global_transform`` of the held object
+to the ``global_transform`` of the ``held_object`` node. This will make the held object have the same position, rotation, and scale of the ``grab_pos_node`` node in world space.
+
+However, because we do not want the held object to change in scale when it is grabbed, we need to set the ``scale`` property of the ``held_object`` node back to ``held_scale``.
+
+This section of code will keep the held object in the same position and rotation as the VR controller, keeping it synced with the VR controller.
+
+_________________
+
+Finally, the last section of code simply calls the ``_physics_process_directional_movement`` function. This function contains all of the code for moving the player when the
+touchpad/joystick on the VR controller moves.
+
+
+``_physics_process_update_controller_velocity`` function step-by-step explanation
+"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+First this function resets the ``controller_velocity`` variable to zero :ref:`Vector3 <class_Vector3>`.
+
+_________________
+
+Then we check to see if there are any stored/cached VR controller velocities saved in the ``prior_controller_velocities`` array. We do this by checking to see if the ``size()`` function
+returns a value greater than ``0``. If there are cached velocities within ``prior_controller_velocities``, then we iterate through each of the stored velocities using a ``for`` loop.
+
+For each of the cached velocities, we simply add its value to ``controller_velocity``. Once the code has gone through all of the cached velocities in ``prior_controller_velocities``,
+we divide ``controller_velocity`` by the size of the ``prior_controller_velocities`` array, which will give us the combined velocity value. This helps take the previous velocities into
+account, making the direction of the controller's velocity more accurate.
+
+_________________
+
+Next we calculate the change in position the VR controller has taken since the last ``_physics_process`` function call. We do this by subtracting ``prior_controller_position`` from the
+global position of the VR controller, ``global_transform.origin``. This will give us a :ref:`Vector3 <class_Vector3>` that points from the position in ``prior_controller_position`` to
+the current position of the VR controller, which we store in a variable called ``relative_controller_position``.
+
+Next we add the change in position to ``controller_velocity`` so the latest change in position is taken into account in the velocity calculation. We then add ``relative_controller_position``
+to ``prior_controller_velocities`` so it can be taken into account on the next calculation of the VR controller's velocity.
+
+Then ``prior_controller_position`` is updated with the global position of the VR controller, ``global_transform.origin``. We then divide ``controller_velocity`` by ``delta`` so the velocity
+is higher, giving results like those we expect, while still being relative to the amount of time that has passed. It is not a perfect solution, but the results look decent most of the time
+and for the purposes of this tutorial, it is good enough.
+
+Finally, the function checks to see if the ``prior_controller_velocities`` has more than ``30`` velocities cached by checking if the ``size()`` function returns a value greater than ``30``.
+If there are more than ``30`` cached velocities stored in ``prior_controller_velocities``, then we simply remove the oldest cached velocity by calling the ``remove`` function and passing in
+a index position of ``0``.
+
+_________________
+
+What this function ultimately does is that it gets a rough idea of the VR controller's velocity by calculating the VR controller's relative changes in position
+over the last thirty ``_physics_process`` calls. While this is not perfect, it gives a decent idea of how fast the VR controller is moving in 3D space.
+
+
+``_physics_process_directional_movement`` function step-by-step explanation
+"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+First this function gets the axises for the trackpad and the joystick and assigns them to :ref:`Vector2 <class_Vector2>` variables called ``trackpad_vector`` and ``joystick_vector`` respectively.
+
+.. note:: You may need to remap the joystick and/or touchpad index values depending on your VR headset and controller. The inputs in this tutorial are the index values of a
+          Windows Mixed Reality headset.
+
+Then ``trackpad_vector`` and ``joystick_vector`` have their deadzones account for. The code for this is detailed in the article below, with slight changes as the code is converted from
+C# to GDScript.
+
+.. note:: You can find a great article explaining all about how to handle touchpad/joystick dead zones here: http://www.third-helix.com/2013/04/12/doing-thumbstick-dead-zones-right.html
+
+Once the ``trackpad_vector`` and ``joystick_vector`` variables have had their deadzones account for, the code then gets the forward and right direction vectors relative to the
+global transform of the :ref:`ARVRCamera <class_ARVRCamera>`. What this does is that it gives us vectors that point forward and right relative to the rotation of the user camera,
+the :ref:`ARVRCamera <class_ARVRCamera>`, in world space. These vectors point in the same direction of the blue and red arrows when you select an object in the Godot editor with
+the ``local space mode`` button enabled. The forward direction vector is stored in a variable called ``forward_direction``, while the right direction vector is stored in a variable
+called ``right_direction``.
+
+Next the code adds the ``trackpad_vector`` and ``joystick_vector`` variables together and normalizes the results using the ``normalized`` function. This gives us the
+combined movement direction of both input devices, so we can use a single :ref:`Vector2 <class_Vector2>` for moving the user. We assign the combined direction to a variable called ``movement_vector``.
+
+Then we calculate the distance the user will move forward, relative to the forward direction stored in ``forward_direction``. To calculate this, we multiply ``forward_direction`` by ``movement_vector.x``,
+``delta``, and ``MOVEMENT_SPEED``. This will give us the distance the user will move forward when the trackpad/joystick is pushed forward or backwards. We assign this to a variable called
+``movement_forward``.
+
+We do a similar calculation for the distance the user will move right, relative to the right direction stored in ``right_direction``. To calculate the distance the user will move right,
+we multiply ``right_direction`` by ``movement_vector.y``, ``delta``, and ``MOVEMENT_SPEED``. This will give us the distance the user will move right when the trackpad/joystick is pushed right or left.
+We assign this to a variable called ``movement_right``.
+
+Next we remove any movement on the ``Y`` axis of ``movement_forward`` and ``movement_right`` by assigning their ``Y`` values to ``0``. We do this so the user cannot fly/fall simply by moving the trackpad
+or joystick. Without doing this, the player could fly in the direction they are facing.
+
+Finally, we check to see if the ``length`` function on ``movement_right`` or ``movement_forward`` is greater than ``0``. If it is, then we need to move the user. To move the user, we perform a global
+translation to the :ref:`ARVROrigin <class_ARVROrigin>` node using ``get_parent().global_translate`` and pass in the ``movement_right`` variable with the ``movement_forward`` variable added to it. This
+will move the player in the direction the trackpad/joystick is pointing, relative to the rotation of the VR headset. We also set the ``directional_movement`` variable to ``true`` so the code knows this
+VR controller is moving the player.
+
+If the ``length`` function on ``movement_right`` or ``movement_forward`` is less than or equal to ``0``, then we simply set the ``directional_movement`` variable to ``false`` so the code knows this VR
+controller is not moving the player.
+
+
+_________________
+
+What this function ultimately does is takes the input from the VR controller's trackpad and joystick and moves the player in the direction the player is pushing them. Movement is relative to the rotation
+of the VR headset, so if the player pushes forward and turns their head to the left, they will move to the left.
+
+
+``button_pressed`` function step-by-step explanation
+""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+This function checks to see if the VR button that was just pressed is equal to one of the VR buttons used in this project. The ``button_index`` variable is passed in by the
+``button_pressed`` signal in :ref:`ARVRController <class_ARVRController>`, which we connected in the ``_ready`` function.
+
+There are only three buttons we are looking for in this project: the trigger button, the grab/grip button, and the menu button.
+
+.. note:: You may need to remap these button index values depending on your VR headset and controller. The inputs in this tutorial are the index values of a
+          Windows Mixed Reality headset.
+
+First we check if the ``button_index`` is equal to ``15``, which should map to the trigger button on the VR controller. If the button pressed is the trigger button,
+then the ``_on_button_pressed_trigger`` function is called.
+
+If the ``button_index`` is equal to ``2``, then the grab button was just pressed. If the button pressed is the grab button, the ``_on_button_pressed_grab`` function is called.
+
+Finally, if the ``button_index`` is equal to ``1``, then the menu button was just pressed. If the button pressed is the menu button, the ``_on_button_pressed_menu`` function is called.
+
+
+``_on_button_pressed_trigger`` function step-by-step explanation
+""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+First this function checks to see if the VR controller is not holding by checking if ``held_object`` is equal to ``null``. If the VR controller is not holding anything, then
+we assume that the trigger press on the VR controller was for teleporting. We then make sure that ``teleport_mesh.visible`` is equal to ``false``. We use this to tell if
+the other VR controller is trying to teleport or not, as ``teleport_mesh`` will be visible if the other VR controller is teleporting.
+
+If ``teleport_mesh.visible`` is equal to ``false``, then we can teleport with this VR controller. We set the ``teleport_button_down`` variable to ``true``, set
+``teleport_mesh.visible`` to true, and set ``teleport_raycast.visible`` to ``true``. This will tell the code in ``_physics_process`` that this VR controller is going to
+teleport, it will make the ``teleport_mesh`` visible so the user knows where the are teleporting to, and will make ``teleport_raycast`` visible to the player has a
+'laser sight' they can use to aim the teleportation pos.
+
+_________________
+
+If ``held_object`` is not equal to ``null``, then the VR controller is holding something. We then check to see if the object that is being held, ``held_object``, extends
+a class called ``VR_Interactable_Rigidbody``. we have not made ``VR_Interactable_Rigidbody`` yet, but ``VR_Interactable_Rigidbody`` will be a custom class we will use
+on all of the special/custom :ref:`RigidBody <class_RigidBody>`-based nodes in the project.
+
+.. tip:: Don't worry, we will cover ``VR_Interactable_Rigidbody`` after this section!
+
+If the ``held_object`` extends ``VR_Interactable_Rigidbody``, then we call the ``interact`` function, so the held object can do whatever it is supposed to do when
+the trigger is pressed and the object is held by the VR controller.
+
+
+``_on_button_pressed_grab`` function step-by-step explanation
+""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+First this function checks to see if ``teleport_button_down`` is equal to ``true``. If it is, then it calls ``return``. We do this because we do not want the user to be
+able to pick up objects while teleporting.
+
+Then we check to see if the VR controller is currently not holding anything by checking if ``held_object`` is equal to ``null``. If the VR controller is not holding anything,
+then the ``_pickup_rigidbody`` function is called. If the VR controller is holding something, ``held_object`` is not equal to ``null``, then the ``_throw_rigidbody`` function is called.
+
+Finally, the pick-up/drop sound is played by calling the ``play`` function on ``hand_pickup_drop_sound``.
+
+
+``_pickup_rigidbody`` function step-by-step explanation
+"""""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+First the function makes a variable called ``rigid_body``, which we'll be using to store the :ref:`RigidBody <class_RigidBody>` that the VR controller is going to
+pick up, assuming there is a RigidBody to pick up.
+
+_________________
+
+Then the function checks to see if the ``grab_mode`` variable is equal to ``AREA``. If it is, then it gets all of the :ref:`PhysicsBody <class_PhysicsBody>` nodes within the ``grab_area`` using
+the ``get_overlapping_bodies`` functions. This function will return an array of :ref:`PhysicsBody <class_PhysicsBody>` nodes. We assign the array of :ref:`PhysicsBody <class_PhysicsBody>` to a new
+variable called ``bodies``.
+
+We then check to see if the length of the ``bodies`` variable is more than ``0``. If it is, we go through each of the :ref:`PhysicsBody <class_PhysicsBody>` nodes in ``bodies`` using a for loop.
+
+For each :ref:`PhysicsBody <class_PhysicsBody>` node, we check if it is, or extends, a :ref:`RigidBody <class_RigidBody>` node using ``if body is RigidBody``, which will return ``true`` if the
+:ref:`PhysicsBody <class_PhysicsBody>` node is or extends the :ref:`RigidBody <class_RigidBody>` node. If the object is a :ref:`RigidBody <class_RigidBody>`, then we check to make sure there is not
+a variable/constant called ``NO_PICKUP`` defined in the body. We do this because if you want to have :ref:`RigidBody <class_RigidBody>` nodes that cannot be picked up, all you have to do is
+define a constant/variable called ``NO_PICKUP`` and the VR controller will be unable to pick it up. If the :ref:`RigidBody <class_RigidBody>` node does not have a variable/constant defined with
+the name ``NO_PICKUP``, then we assign the ``rigid_body`` variable to the :ref:`RigidBody <class_RigidBody>` node and break the for loop.
+
+What this section of code does is goes through all of the physics bodies within the ``grab_area`` and grabs the first :ref:`RigidBody <class_RigidBody>` node that does not have a
+variable/constant named ``NO_PICKUP`` and assigns it to the ``rigid_body`` variable so we can do some additional post processing later in this function.
+
+_________________
+
+If the ``grab_mode`` variable is not equal to ``AREA``, we then check to see if it is equal to ``RAYCAST`` instead. If it is equal to ``RAYCAST``, we force the ``grab_raycast`` node to update
+using the ``force_raycast_update`` function. The ``force_raycast_update`` function will update the :ref:`Raycast <class_Raycast>` with the latest changes in the physics world. We then check
+to see if the ``grab_raycast`` node collided with something using the ``is_colliding`` function, which will return true if the :ref:`Raycast <class_Raycast>` hit something.
+
+If the ``grab_raycast`` hit something, we get the :ref:`PhysicsBody <class_PhysicsBody>` node hit using the ``get_collider`` function. The code then checks to see if the node hit is
+a :ref:`RigidBody <class_RigidBody>` node using ``if body is RigidBody``, which will return ``true`` if the :ref:`PhysicsBody <class_PhysicsBody>` node is or extends the
+:ref:`RigidBody <class_RigidBody>` node. Then the code checks to see if the :ref:`RigidBody <class_RigidBody>` node does not have a variable named ``NO_PICKUP``, and if it does not,
+then it assigns the :ref:`RigidBody <class_RigidBody>` node to the ``rigid_body`` variable.
+
+What this section of code does is sends the ``grab_raycast`` :ref:`Raycast <class_Raycast>` node out and checks if it collided with a :ref:`RigidBody <class_RigidBody>` node that does
+not have a variable/constant named ``NO_PICKUP``. If it collided with a RigidBody without ``NO_PICKUP``, it assigns the node to the ``rigid_body`` variable so we can do some
+additional post processing later in this function.
+
+_________________
+
+The final section of code first checks to see if ``rigid_body`` is not equal to ``null``. If ``rigid_body`` is not equal to ``null``, then the VR controller found a
+:ref:`RigidBody <class_RigidBody>`-based node that can be picked up.
+
+If there is a VR controller to pickup, we assign ``held_object`` to the :ref:`RigidBody <class_RigidBody>` node stored in ``rigid_body``. We then store the :ref:`RigidBody <class_RigidBody>` node's
+``mode``, ``collision_layer``, and ``collision_mask`` in ``held_object_data`` using ``mode``, ``layer``, and ``mask`` as keys for the respective values. This is so we can reapply them
+later when the object is dropped by the VR controller.
+
+We then set the :ref:`RigidBody <class_RigidBody>`'s mode to ``MODE_STATIC``, it's ``collision_layer`` to zero, and it's ``collision_mask`` to zero. This will make it where the held
+:ref:`RigidBody <class_RigidBody>` cannot interact with other objects in the physics world when held by the VR controller.
+
+Next the ``hand_mesh`` :ref:`MeshInstance <class_MeshInstance>` is made invisible by setting the ``visible`` property to ``false``. This is so the hand does not get in the way of the held object.
+Likewise, the ``grab_raycast`` 'laser sight' is made invisible by setting the ``visible`` property to ``false``.
+
+Then the code checks to see if the held object extends a class called ``VR_Interactable_Rigidbody``. If it does, then sets a variable called ``controller`` on ``held_object`` to ``self``, and
+calls the ``picked_up`` function on ``held_object``. While we haven't made ``VR_Interactable_Rigidbody`` just yet, what this will do is set tell the ``VR_Interactable_Rigidbody`` class that it is
+being held by a VR controller, where the a reference to the controller is stored in the ``controller`` variable, through calling the ``picked_up`` function.
+
+.. tip:: Don't worry, we will cover ``VR_Interactable_Rigidbody`` after this section!
+         
+         The code should make more sense after completing part 2 of this tutorial series, where we will actually be using ``VR_Interactable_Rigidbody``.
+
+What this section of code does is that if a :ref:`RigidBody <class_RigidBody>` was found using the grab :ref:`Area <class_Area>` or :ref:`Raycast <class_Raycast>`, it sets it up so that
+it can be carried by the VR controller.
+
+``_throw_rigidbody`` function step-by-step explanation
+""""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+First the function checks to see if the VR controller is not holding any object by checking if the ``held_object`` variable is equal to ``null``. If it is, then it simply
+calls ``return`` so nothing happens. While this shouldn't be possible, the ``_throw_rigidbody`` function should only be called if an object is held, this check helps ensure
+that if something strange happens, this function will react as expected.
+
+After checking if the VR controller is holding an object, we assume it is and set the stored :ref:`RigidBody <class_RigidBody>` data back to the held object. We take the ``mode``, ``layer`` and
+``mask`` data stored in the ``held_object_data`` dictionary and reapply it to the object in ``held_object``. This will set the :ref:`RigidBody <class_RigidBody>` back to the state it was prior to
+being picked up.
+
+Then we call ``apply_impulse`` on the ``held_object`` so that the :ref:`RigidBody <class_RigidBody>` is thrown in the direction of the VR controller's velocity, ``controller_velocity``.
+
+We then check to see if the object held extends a class called ``VR_Interactable_Rigidbody``. If it does, then we call a function called ``dropped`` in ``held_object`` and set
+``held_object.controller`` to ``null``. While we have not made ``VR_Interactable_Rigidbody`` yet, but what this will do is call the ``droppped`` function so the :ref:`RigidBody <class_RigidBody>`
+can do whatever it needs to do when dropped, and we set the ``controller`` variable to ``null`` so that the :ref:`RigidBody <class_RigidBody>` knows that it is not being held.
+
+.. tip:: Don't worry, we will cover ``VR_Interactable_Rigidbody`` after this section!
+         
+         The code should make more sense after completing part 2 of this tutorial series, where we will actually be using ``VR_Interactable_Rigidbody``.
+
+Regardless of whether ``held_object`` extends ``VR_Interactable_Rigidbody`` or not, we then set ``held_object`` to ``null`` so the VR controller knows it is no longer holding anything.
+Because the VR controller is no longer holding anything, we make the ``hand_mesh`` visible by setting ``hand_mesh.visible`` to true.
+
+Finally, if the ``grab_mode`` variable is set to ``RAYCAST``, we set ``grab_raycast.visible`` to ``true`` so the 'laser sight' for the :ref:`Raycast <class_Raycast>` in ``grab_raycast`` is visible.
+
+
+``_on_button_pressed_menu`` function step-by-step explanation
+"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+First this function checks to see if the ``grab_mode`` variable is equal to ``AREA``. If it is, then it sets ``grab_mode`` to ``RAYCAST``. It then checks to see if the VR controller is not
+holding anything by checking to see if ``held_object`` is equal to ``null``. If the VR controller is not holding anything, then ``grab_raycast.visible`` is set to ``true`` so the
+'laser sight' on the grab raycast is visible.
+
+If the ``grab_mode`` variable is not equal to ``AREA``, then it checks to see if it is equal to ``RAYCAST``. If it is, then it sets the ``grab_mode`` to ``AREA`` and sets ``grab_raycast.visible``
+to ``false`` so the 'laser sight' on the grab raycast is not visible.
+
+This section of code simply changes how the VR controller will grab :ref:`RigidBody <class_RigidBody>`-based nodes when the grab/grip button is pressed. If ``grab_mode`` is set to ``AREA``, then
+the :ref:`Area <class_Area>` node in ``grab_area`` will be used for detecting :ref:`RigidBody <class_RigidBody>` nodes, while if ``grab_mode`` is set to ``RAYCAST`` the :ref:`Raycast <class_Raycast>`
+node in ``grab_raycast`` will be used for detecting :ref:`RigidBody <class_RigidBody>` nodes.
+
+
+``button_released`` function step-by-step explanation
+"""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+The only section of code in this function checks to see if the index of the button that was just released, ``button_index``, is equal to ``15``, which should map to the trigger button
+on the VR controller. The ``button_index`` variable is passed in by the ``button_release`` signal in :ref:`ARVRController <class_ARVRController>`, which we connected in the ``_ready`` function.
+
+If the trigger button was just released, then the ``_on_button_released_trigger`` function is called.
+
+
+``_on_button_released_trigger`` function step-by-step explanation
+"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+The only section of code in this function first checks to see if the VR controller is trying to teleport by checking if the ``teleport_button_down`` variable is equal to ``true``.
+
+If the ``teleport_button_down`` variable is equal to ``true``, the code then checks if there is a teleport position set and whether the teleport mesh is visible. It does this by
+checking to see if ``teleport_pos`` is not equal to ``null`` and if ``teleport_mesh.visible`` is equal to ``true``.
+
+If there is a teleport position set and the teleport mesh is visible, the code then calculates the offset from the camera to the :ref:`ARVROrigin <class_ARVROrigin>` node, which is assumed to be the
+parent node of the VR controller. To calculate the offset, the global position (``global_transform.origin``) of the ``Player_Camera`` node has the global position of the :ref:`ARVROrigin <class_ARVROrigin>`
+subtracted from it. This will result in a vector that points from the :ref:`ARVROrigin <class_ARVROrigin>` to the :ref:`ARVRCamera <class_ARVRCamera>`, which we store in a variable called ``camera_offset``.
+
+The reason we need to know the offset is because some VR headsets use room tracking, where the player's camera can be offset from the :ref:`ARVROrigin <class_ARVROrigin>` node. Because of this, when we teleport we want to
+keep the offset created by room tracking so that when the player teleports, the offset created by the room tracking is not applied. Without this, if you moved in a room and then teleported, instead
+of appearing at the position you wanted to teleport at, your position would be offset by the amount of distance you have from the :ref:`ARVROrigin <class_ARVROrigin>` node.
+
+Now that we know the offset from the VR camera to the VR origin, we need to remove the difference on the ``Y`` axis. We do this because we do not want to offset based on the user's height.
+If we did not do this, when teleporting the player's head would be level with the ground.
+
+Then we can 'teleport' the player by setting the global position (``global_transform.origin``) of the ARVROrigin node to the position stored in ``teleport_pos`` with ``camera_offset`` subtracted from it.
+This will teleport the player and remove the room tracking offset, so the user appears exactly where they want when teleporting.
+
+Finally, regardless of whether the VR controller teleported the user or not, we reset the teleport related variables. ``teleport_button_down`` is set to ``false``, ``teleport_mesh.visible`` is
+set to ``false`` so the mesh is invisible, ``teleport_raycast.visible`` is set to ``false``, and ``teleport_pos`` is set to ``null``.
+
+
+``sleep_area_entered`` function step-by-step explanation
+""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+The only section of code in this function checks to see if the :ref:`PhysicsBody <class_PhysicsBody>` node that entered the ``Sleep_Area`` node
+has a variable called ``can_sleep``. If it does, then it sets the ``can_sleep`` variable to ``false`` and sets the ``sleeping`` variable to ``false``.
+
+Without doing this, sleeping :ref:`PhysicsBody <class_PhysicsBody>` nodes would not be able to be picked up by the VR controller, even if the VR controller
+is at the same position as the :ref:`PhysicsBody <class_PhysicsBody>` node. To work around this, we simply 'wake up' :ref:`PhysicsBody <class_PhysicsBody>` nodes
+that are close to the VR controller.
+
+
+``sleep_area_exited`` function step-by-step explanation
+"""""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+The only section of code in this function checks to see if the :ref:`PhysicsBody <class_PhysicsBody>` node that entered the ``Sleep_Area`` node
+has a variable called ``can_sleep``. If it does, then it sets the ``can_sleep`` variable to ``true``.
+
+This allows :ref:`RigidBody <class_RigidBody>` nodes that leave the ``Sleep_Area`` to sleep again, saving performance.
+
+_________________
+
+Okay, whew! That was a lot of code! Add the same script, ``VR_Controller.gd`` to the other VR controller scene so both VR controllers have the same script.
+
+Now we just need to do one thing before testing the project! Right now we are referencing a class called ``VR_Interactable_Rigidbody``, but we have not defined it yet.
+While we will not be using ``VR_Interactable_Rigidbody`` in this tutorial, let's create it real quick so the project can be run.
+
+
+
+Creating a base class for interactable VR objects
+-------------------------------------------------
+
+With the ``Script`` tab still open, create a new GDScript called ``VR_Interactable_Rigidbody.gd``.
+
+.. tip:: You can create GDScripts in the ``Script`` tab by pressing ``File -> New Script...``.
+
+Once you have ``VR_Interactable_Rigidbody.gd`` open, add the following code:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+    class_name VR_Interactable_Rigidbody
+    extends RigidBody
+    
+    # (Ignore the unused variable warning)
+    # warning-ignore:unused_class_variable
+    var controller = null
+
+
+    func _ready():
+        pass
+
+
+    func interact():
+        pass
+
+
+    func picked_up():
+        pass
+
+
+    func dropped():
+        pass
+
+
+Let's quickly go through what this script.
+
+_________________
+
+First we start the script with ``class_name VR_Interactable_Rigidbody``. What this does is that it tells Godot that this GDScript is a new class that called ``VR_Interactable_Rigidbody``.
+This allows us to compare nodes against the ``VR_Interactable_Rigidbody`` class in other script files without having to load the script directly or do anything special. We can compare
+the class just like all of the built-in Godot classes.
+
+Next is a class variable called ``controller``. ``controller`` will be used to hold a reference to the VR controller that is currently holding the object. If a VR controller is not
+holding the object, then the ``controller`` variable will be ``null``. The reason we need to have a reference to the VR controller is so held objects can access VR controller specific
+data, like ``controller_velocity``.
+
+Finally, we have four functions. The ``_ready`` function is defined by Godot and all we do is simply have ``pass`` as there is nothing we need to do when the object is added to the scene
+in ``VR_Interactable_Rigidbody``.
+
+The ``interact`` function is a stub function that will be called when the interact button on the VR controller, the trigger in this case, is pressed while the object is held.
+
+.. tip:: A stub function is a function that is defined but does not have any code. Stub functions are generally designed to be overwritten or extended. In this project, we are using
+         the stub functions so there is a consistent interface across all interactable :ref:`RigidBody <class_RigidBody>` objects.
+
+The ``picked_up`` and ``dropped`` functions are stub functions that will be called when the object is picked up and dropped by the VR controller.
+
+_________________
+
+That is all we need to do for now! In the next part of this tutorial series, we'll start making special interactable :ref:`RigidBody <class_RigidBody>` objects.
+
+Now that the base class is defined, the code in the VR controller should work. Go ahead and try the game again, and you should find you can teleport around by pressing the touch pad,
+and can grab and throw objects using the grab/grip buttons.
+
+Now, you may want to try moving using the trackpads and/or joysticks, but **it may make you motion sick!**
+
+One of the main reasons this can make you feel motion sick is because your vision tells you that you are moving, while your body is not moving.
+This conflict of signals can make the body feel sick. Let's add a vignette shader to help reduce motion sickness while moving in VR!
+
+
+
+Reducing motion sickness
+------------------------
+
+.. note:: There are plenty of ways to reduce motion sickness in VR, and there is no one perfect way to reduce motion sickness. See
+          `this page on the Oculus Developer Center <https://developer.oculus.com/design/latest/concepts/bp-locomotion/>`_
+          for more information on how to implement locomotion and reducing motion sickness.
+
+To help reduce motion sickness while moving, we are going to add a vignette effect that will only be visible while the player moves.
+
+First, quickly switch back to ``Game.tscn```. Under the :ref:`ARVROrigin <class_ARVROrigin>` node there is a child node called ``Movement_Vignette``. This node is going to apply a simple
+vignette to the VR headset when the player is moving using the VR controllers. This should help reduce motion sickness.
+
+Open up ``Movement_Vignette.tscn``, which you can find in the ``Scenes`` folder. The scene is just a :ref:`ColorRect <class_ColorRect>` node with a custom
+shader. Feel free to look at the custom shader if you want, it is just a slightly modified version of the vignette shader you can find in the
+`Godot demo repository <https://github.com/godotengine/godot-demo-projects>`_.
+
+Let's write the code that will make the vignette shader visible when the player is moving. Select the ``Movement_Vignette`` node and create a new script called ``Movement_Vignette.gd``.
+Add the following code:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    extends Control
+
+    var controller_one
+    var controller_two
+
+
+    func _ready():
+        yield(get_tree(), "idle_frame")
+        yield(get_tree(), "idle_frame")
+        yield(get_tree(), "idle_frame")
+        yield(get_tree(), "idle_frame")
+        
+        var interface = ARVRServer.primary_interface
+        
+        if interface == null:
+            set_process(false)
+            printerr("Movement_Vignette: no VR interface found!")
+            return
+        
+        rect_size = interface.get_render_targetsize()
+        rect_position = Vector2(0,0)
+        
+        controller_one = get_parent().get_node("Left_Controller")
+        controller_two = get_parent().get_node("Right_Controller")
+        
+        visible = false
+
+
+    func _process(_delta):
+        if (controller_one == null or controller_two == null):
+            return
+        
+        if (controller_one.directional_movement == true or controller_two.directional_movement == true):
+            visible = true
+        else:
+            visible = false
+
+Because this script is fairly brief, let's quickly go over what it does.
+
+
+Explaining the vignette code
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+There are two class variables, ``controller_one`` and ``controller_two``. These variables will hold references to the left and right VR controllers.
+
+_________________
+
+In the ``_ready`` function first waits for four frames using ``yield``. The reason we are waiting four frames is because we want to ensure the VR interface is ready
+and accessible.
+
+After waiting the primary VR interface is retrieved using ``ARVRServer.primary_interface``, which is assigned to a variable called ``interface``.
+The code then checks to see if ``interface`` is equal to ``null``. If ``interface`` is equal to ``null``, then ``_process`` is disabled using ``set_process`` with a value of ``false``.
+
+If ``interface`` is not ``null``, then we set the ``rect_size`` of the vignette shader to the render size of the VR viewport so it takes up the entire screen. We need to do this because
+different VR headsets have different resolutions and aspect ratios, so we need to resize the node accordingly. We also set the ``rect_position`` of the vignette shader to zero so it
+is in the correct position relative to the screen.
+
+The left and right VR controllers are then retrieved and assigned to ``controller_one`` and ``controller_two`` respectively. Finally, the vignette shader is made invisible by default
+by setting it's ``visible`` property to ``false``.
+
+_________________
+
+In ``_process`` the code first checks if either ``controller_one`` or ``controller_two`` are equal to ``null``. If either node is equal to ``null``, then ``return`` is called so
+nothing happens.
+
+Then the code checks to see if either of the VR controllers are moving the player using the touchpad/joystick by checking if ``directional_movement`` is equal to ``true`` in
+``controller_one`` or ``controller_two``. If either of the VR controllers are moving the player, then the vignette shader makes itself visible by setting it's ``visible`` property
+to ``true``. If neither VR controller is moving the player, so ``directional_movement`` is ``false`` in both VR controllers, than the vignette shader makes itself invisible by setting
+it's ``visible`` property to ``false``.
+
+_________________
+
+That is the whole script! Now that we have written the code, go ahead and try moving around with the trackpad and/or joystick. You should find that it is less motion sickness-inducing
+then before!
+
+.. note:: As previously mentioned, there are plenty of ways to reduce motion sickness in VR. Check out
+          `this page on the Oculus Developer Center <https://developer.oculus.com/design/latest/concepts/bp-locomotion/>`_
+          for more information on how to implement locomotion and reducing motion sickness.
+
+
+
+Final notes
+-----------
+
+.. image:: img/starter_vr_tutorial_hands.png
+
+Now you have fully working VR controllers that can move around the environment and interact with :ref:`RigidBody <class_RigidBody>`-based objects.
+In the next part of this tutorial series, we will be creating some special :ref:`RigidBody <class_RigidBody>`-based objects for the player to use!
+
+.. warning:: You can download the finished project for this tutorial series on the Godot OpenVR GitHub repository, under the releases tab!

+ 1036 - 0
tutorials/vr/vr_starter_tutorial/vr_starter_tutorial_part_two.rst

@@ -0,0 +1,1036 @@
+.. _doc_vr_starter_tutorial_part_two:
+
+VR Starter Tutorial Part Two
+============================
+
+Introduction
+------------
+
+.. image:: img/starter_vr_tutorial_sword.png
+
+In this part of the VR starter tutorial series, we will be adding a number of special :ref:`RigidBody <class_RigidBody>`-based nodes that can be used in VR.
+
+This continues from where we left on in the last tutorial part, where we just finished getting the VR controllers working and defined a custom
+class called ``VR_Interactable_Rigidbody``.
+
+.. tip:: You can find the finished project on the `OpenVR GitHub repository <https://github.com/GodotVR/godot_openvr_fps>`_.
+
+
+Adding destroyable targets
+--------------------------
+
+Before we make any of the special :ref:`RigidBody <class_RigidBody>`-based nodes, we need something for them to do. Let's make a simple sphere target that will break into a bunch of pieces
+when destroyed.
+
+Open up ``Sphere_Target.tscn``, which is in the ``Scenes`` folder. The scene is fairly simple, with just a :ref:`StaticBody <class_StaticBody>` with a sphere shaped
+:ref:`CollisionShape <class_CollisionShape>`, a :ref:`MeshInstance <class_MeshInstance>` node displaying a sphere mesh, and an :ref:`AudioStreamPlayer3D <class_AudioStreamPlayer3D>` node.
+
+The special :ref:`RigidBody <class_RigidBody>` nodes will handle damaging the sphere, which is why we are using a :ref:`StaticBody <class_StaticBody>` node instead of something like
+an :ref:`Area <class_Area>` or :ref:`RigidBody <class_RigidBody>` node. Outside of that, there isn't really a lot to talk about, so let's move straight into writing the code.
+
+Select the ``Sphere_Target_Root`` node and make a new script called ``Sphere_Target.gd``. Add the following code:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    extends Spatial
+
+    var destroyed = false
+    var destroyed_timer = 0
+    const DESTROY_WAIT_TIME = 80
+
+    var health = 80
+
+    const RIGID_BODY_TARGET = preload("res://Assets/RigidBody_Sphere.scn")
+
+
+    func _ready():
+        set_physics_process(false)
+
+
+    func _physics_process(delta):
+        destroyed_timer += delta
+        if destroyed_timer >= DESTROY_WAIT_TIME:
+            queue_free()
+
+
+    func damage(damage):
+        if destroyed == true:
+            return
+        
+        health -= damage
+        
+        if health <= 0:
+            
+            get_node("CollisionShape").disabled = true
+            get_node("Shpere_Target").visible = false
+            
+            var clone = RIGID_BODY_TARGET.instance()
+            add_child(clone)
+            clone.global_transform = global_transform
+            
+            destroyed = true
+            set_physics_process(true)
+            
+            get_node("AudioStreamPlayer").play()
+            get_tree().root.get_node("Game").remove_sphere()
+
+
+Let's go over how this script works.
+
+Explaining the Sphere Target code
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+First, let's go through all of the class variables in the script:
+
+* ``destroyed``: A variable to track whether the sphere target has been destroyed.
+* ``destroyed_timer``: A variable to track how long the sphere target has been destroyed.
+* ``DESTROY_WAIT_TIME``: A constant to define the length of time the target can be destroyed for before it frees/deletes itself.
+* ``health``: A variable to store the amount of health the sphere target has.
+* ``RIGID_BODY_TARGET``: A constant to hold the scene of the destroyed sphere target.
+
+.. note:: Feel free to check out the ``RIGID_BODY_TARGET`` scene. It is just a bunch of :ref:`RigidBody <class_RigidBody>` nodes and a broken sphere model.
+          
+          We'll be instancing this scene so when the target is destroyed, it looks like it broke into a bunch of pieces.
+
+
+``_ready`` function step-by-step explanation
+""""""""""""""""""""""""""""""""""""""""""""
+
+All the ``_ready`` function does is that it stops the ``_physics_process`` from being called by calling ``set_physics_process`` and passing ``false``.
+The reason we do this is because all of the code in ``_physics_process`` is for destroying this node when enough time has passed, which we only want to
+do when the target has been destroyed.
+
+
+``_physics_process`` function step-by-step explanation
+""""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+First this function adds time, ``delta``, to the ``destroyed_timer`` variable. It then checks to see if ``destroyed_timer`` is greater than or equal to
+``DESTROY_WAIT_TIME``. If ``destroyed_timer`` is greater than or equal to ``DESTROY_WAIT_TIME``, then the sphere target frees/deletes itself by calling
+the ``queue_free`` function. 
+
+``damage`` function step-by-step explanation
+""""""""""""""""""""""""""""""""""""""""""""
+
+The ``damage`` function will be called by the special :ref:`RigidBody <class_RigidBody>` nodes, which will pass the amount of damage done to the target, which is a function argument
+variable called ``damage``. The ``damage`` variable will hold the amount of damage the special :ref:`RigidBody <class_RigidBody>` node did to the sphere target.
+
+First this function checks to make sure the target is not already destroyed by checking if the ``destroyed`` variable is equal to ``true``. If ``destroyed`` is equal to ``true``, then
+the function calls ``return`` so none of the other code is called. This is just a safety check so that if two things damage the target at exactly the same time, the target cannot be
+destroyed twice.
+
+Next the function removes the amount of damage taken, ``damage``, from the target's health, ``health``. If then checks to see if ``health`` is equal to zero or less, meaning that the
+target has just been destroyed.
+
+If the target has just been destroyed, then we disable the :ref:`CollisionShape <class_CollisionShape>` by setting it's ``disabled`` property to ``true``. We then make the ``Sphere_Target``
+:ref:`MeshInstance <class_MeshInstance>` invisible by setting the ``visible`` property to ``false``. We do this so the target can no longer effect the physics world and so the non-broken target mesh is not visible.
+
+After this the function then instances the ``RIGID_BODY_TARGET`` scene and adds it as a child of the target. It then sets the ``global_transform`` of the newly instanced scene, called ``clone``, to the
+``global_transform`` of the non-broken target. This makes it where the broken target starts at the same position as the non-broken target with the same rotation and scale.
+
+Then the function sets the ``destroyed`` variable to ``true`` so the target knows it has been destroyed and calls the ``set_physics_process`` function and passes ``true``. This will start
+executing the code in ``_physics_process`` so that after ``DESTROY_WAIT_TIME`` seconds have passed, the sphere target will free/destroy itself.
+
+The function then gets the :ref:`AudioStreamPlayer3D <class_AudioStreamPlayer3D>` node and calls the ``play`` function so it plays its sound.
+
+Finally, the ``remove_sphere`` function is called in ``Game.gd``. To get ``Game.gd``, the code uses the scene tree and works its way from the root of the scene tree to the root of the
+``Game.tscn`` scene.
+
+
+Adding the ``remove_sphere`` function to ``Game.gd``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+You may have noticed we are calling a function in ``Game.gd``, called ``remove_sphere``, that we have not defined yet. Open up ``Game.gd`` and
+add the following additional class variables:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    var spheres_left = 10
+    var sphere_ui = null
+
+- ``spheres_left``: The amount of sphere targets left in the world. In the provided ``Game`` scene, there are ``10`` spheres, so that is the initial value.
+- ``sphere_ui``: A reference to the sphere UI. We will use this later in the tutorial to display the amount of spheres left in the world.
+
+With these variables defined, we can now add the ``remove_sphere`` function. Add the following code to ``Game.gd``:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    func remove_sphere():
+        spheres_left -= 1
+
+        if sphere_ui != null:
+            sphere_ui.update_ui(spheres_left)
+
+
+Let's go through what this function does real quick:
+
+First, it removes one from the ``spheres_left`` variable. It then checks to see if the ``sphere_ui`` variable is not equal to ``null``, and if it is not
+equal to ``null`` it calls the ``update_ui`` function on ``sphere_ui``, passing in the number of spheres as an argument to the function.
+
+.. note:: We will add the code for ``sphere_ui`` later in this tutorial!
+
+Now the ``Sphere_Target`` is ready to be used, but we don't have any way to destroy it. Let's fix that by adding some special :ref:`RigidBody <class_RigidBody>`-based nodes
+that can damage the targets.
+
+
+Adding a pistol
+---------------
+
+Let's add a pistol as the first interactable :ref:`RigidBody <class_RigidBody>` node. Open up ``Pistol.tscn``, which you can find in the ``Scenes`` folder.
+
+Let's quickly go over a few things of note in ``Pistol.tscn`` real quick before we add the code.
+
+All of the nodes in ``Pistol.tscn`` expect the root node are rotated. This is so the pistol is in the correct rotation relative to the VR controller when it is picked up. The root node
+is a :ref:`RigidBody <class_RigidBody>` node, which we need because we're going to use the ``VR_Interactable_Rigidbody`` class we created in the last part of this tutorial series.
+
+There is a :ref:`MeshInstance <class_MeshInstance>` node called ``Pistol_Flash``, which is a simple mesh that we will be using to simulate the muzzle flash on the end of the pistol's barrel.
+A :ref:`MeshInstance <class_MeshInstance>` node called ``LaserSight`` is used to as a guide for aiming the pistol, and it follows the direction of the :ref:`Raycast <class_Raycast>` node,
+called ``Raycast``, that the pistol uses to detect if its 'bullet' hit something. Finally, there is an :ref:`AudioStreamPlayer3D <class_AudioStreamPlayer3D>` node at the end of the
+pistol that we will use to play the sound of the pistol firing.
+
+Feel free to look at the other parts of the scene if you want. Most of the scene is fairly straightforward, with the major changes mentioned above. Select the :ref:`RigidBody <class_RigidBody>`
+node called ``Pistol`` and make a new script called ``Pistol.gd``. Add the following code:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+    
+    extends VR_Interactable_Rigidbody
+
+    var flash_mesh
+    const FLASH_TIME = 0.25
+    var flash_timer = 0
+
+    var laser_sight_mesh
+    var pistol_fire_sound
+
+    var raycast
+    const BULLET_DAMAGE = 20
+    const COLLISION_FORCE = 1.5
+
+
+    func _ready():
+        flash_mesh = get_node("Pistol_Flash")
+        flash_mesh.visible = false
+        
+        laser_sight_mesh = get_node("LaserSight")
+        laser_sight_mesh.visible = false
+        
+        raycast = get_node("RayCast")
+        pistol_fire_sound = get_node("AudioStreamPlayer3D")
+
+
+    func _physics_process(delta):
+        if flash_timer > 0:
+            flash_timer -= delta
+            if flash_timer <= 0:
+                flash_mesh.visible = false
+
+
+    func interact():
+        if flash_timer <= 0:
+            
+            flash_timer = FLASH_TIME
+            flash_mesh.visible = true
+            
+            raycast.force_raycast_update()
+            if raycast.is_colliding():
+                
+                var body = raycast.get_collider()
+                var direction_vector = raycast.global_transform.basis.z.normalized()
+                var raycast_distance = raycast.global_transform.origin.distance_to(raycast.get_collision_point())
+                
+                if body.has_method("damage"):
+                    body.damage(BULLET_DAMAGE)
+                elif body is RigidBody:
+                    var collision_force = (COLLISION_FORCE / raycast_distance) * body.mass
+                    body.apply_impulse((raycast.global_transform.origin - body.global_transform.origin).normalized(), direction_vector * collision_force)
+            
+            pistol_fire_sound.play()
+            
+            if controller != null:
+                controller.rumble = 0.25
+
+
+    func picked_up():
+        laser_sight_mesh.visible = true
+
+
+    func dropped():
+        laser_sight_mesh.visible = false
+
+Let's go over how this script works.
+
+
+Explaining the pistol code
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+First, notice how instead of ``extends RigidBody``, we instead have ``extends VR_Interactable_Rigidbody``. This makes it where the pistol script extends the
+``VR_Interactable_Rigidbody`` class so the VR controllers know this object can be interacted with and that the functions defined in ``VR_Interactable_Rigidbody``
+can be called when this object is held by a VR controller.
+
+Next, let's look at the class variables:
+
+* ``flash_mesh``: A variable to hold the :ref:`MeshInstance <class_MeshInstance>` node that is used to simulate muzzle flash on the pistol.
+* ``FLASH_TIME``: A constant to define how long the muzzle flash will be visible. This will also define how fast the pistol can fire.
+* ``flash_timer``: A variable to hold the amount of time the muzzle flash has been visible for.
+* ``laser_sight_mesh``: A variable to hold the :ref:`MeshInstance <class_MeshInstance>` node that acts as the pistol's 'laser sight'.
+* ``pistol_fire_sound``: A variable to hold the :ref:`AudioStreamPlayer3D <class_AudioStreamPlayer3D>` node used for the pistol's firing sound.
+* ``raycast``: A variable to hold the :ref:`Raycast <class_Raycast>` node that is used for calculating the bullet's position and normal when the pistol is fired.
+* ``BULLET_DAMAGE``: A constant to define the amount of damage a single bullet from the pistol does.
+* ``COLLISION_FORCE``: A constant that defines the amount of force that is applied to :ref:`RigidBody <class_RigidBody>` nodes when the pistol's bullet collides.
+
+
+``_ready`` function step-by-step explanation
+""""""""""""""""""""""""""""""""""""""""""""
+
+This function gets the nodes and assigns them to their proper variables. For the ``flash_mesh`` and ``laser_sight_mesh`` nodes, both have their ``visible`` property set to ``false``
+so they are not visible initially.
+
+``_physics_process`` function step-by-step explanation
+""""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+The ``_physics_process`` function first checks to see if the pistol's muzzle flash is visible by checking if ``flash_timer`` is more than zero. If ``flash_timer`` is more than
+zero, then we remove time, ``delta`` from it. Next we check if the ``flash_timer`` variable is zero or less now that we removed ``delta`` from it. If it is, then the pistol
+muzzle flash timer just finished and so we need to make ``flash_mesh`` invisible by setting it's ``visible`` property to ``false``.
+
+``interact`` function step-by-step explanation
+""""""""""""""""""""""""""""""""""""""""""""""
+
+The interact function first checks to see if the pistol's muzzle flash is invisible by checking to see if ``flash_timer`` is less than or equal to zero. We do this so we
+can limit the rate of fire of the pistol to the length of time the muzzle flash is visible, which is a simple solution for limiting how fast the player can fire.
+
+If ``flash_timer`` is zero or less, we then set ``flash_timer`` to ``FLASH_TIME`` so there is a delay before the pistol can fire again. After that we set ``flash_mesh.visible``
+to ``true`` so the muzzle flash at the end of the pistol is visible while ``flash_timer`` is more than zero.
+
+Next we call the ``force_raycast_update`` function on the :ref:`Raycast <class_Raycast>` node in ``raycast`` so that it gets the latest collision info from the physics world.
+We then check if the ``raycast`` hit something by checking if the ``is_colliding`` function is equal to ``true``.
+
+_________________
+
+If the ``raycast`` hit something, then we get the :ref:`PhysicsBody <class_PhysicsBody>` it collided with through the ``get_collider`` function. We assign the
+hit :ref:`PhysicsBody <class_PhysicsBody>` to a variable called ``body``.
+
+We then get the direction of the :ref:`Raycast <class_Raycast>` by getting it's positive ``Z`` directional axis from the :ref:`Basis <class_Basis>` on the ``raycast`` node's ``global_transform``.
+This will give us the direction the raycast is pointing on the Z axis, which is the same direction as the blue arrow on the :ref:`Spatial <class_Spatial>` gizmo when
+``Local space mode`` is enabled in the Godot editor. We store this direction in a variable called ``direction_vector``.
+
+Next we get the distance from the :ref:`Raycast <class_Raycast>` origin to the :ref:`Raycast <class_Raycast>` collision point by getting the distance from the global position, ``global_transform.origin``
+of the ``raycast`` node to the collision point of the :ref:`Raycast <class_Raycast>`, ``raycast.get_collision_point``, using the ``distance_to`` function. This will give us the distance the
+:ref:`Raycast <class_Raycast>` traveled before it collided, which we store in a variable called ``raycast_distance``.
+
+Then the code checks if the :ref:`PhysicsBody <class_PhysicsBody>`, ``body``, has a function/method called ``damage`` using the ``has_method`` function. If the :ref:`PhysicsBody <class_PhysicsBody>`
+has a function/method called ``damage``, then we call the ``damage`` function and pass ``BULLET_DAMAGE`` so it takes damage from the bullet colliding into it.
+
+Regardless of whether the :ref:`PhysicsBody <class_PhysicsBody>` has a ``damage`` function, we then check to see if ``body`` is a :ref:`RigidBody <class_RigidBody>`-based node. If ``body`` is a
+:ref:`RigidBody <class_RigidBody>`-based node, then we want to push it when the bullet collides.
+
+To calculate the amount of force applied, we simply take ``COLLISION_FORCE`` and divide it by ``raycast_distance``, then we multiply the whole thing by ``body.mass``. We store this calculation in
+a variable called ``collision_force``. This will make collisions over a shorter distance apply move force than those over longer distances, giving a *slightly* more realistic collision response.
+
+We then push the :ref:`RigidBody <class_RigidBody>` using the ``apply_impulse`` function, where the position is a zero Vector3 so the force is applied from the center, and the collision force is the ``collision_force`` variable we calculated.
+
+_________________
+
+Regardless of whether the ``raycast`` variable hit something or not, we then play the pistol shot sound by calling the ``play`` function on the ``pistol_fire_sound`` variable.
+
+Finally, we check to see if the pistol is being held by a VR controller by checking to see if the ``controller`` variable is not equal to ``null``. If it is not equal to ``null``,
+we then set the ``rumble`` property of the VR controller to ``0.25``, so there is a slight rumble when the pistol fires.
+
+
+``picked_up`` function step-by-step explanation
+"""""""""""""""""""""""""""""""""""""""""""""""
+
+This function simply makes the ``laser_sight_mesh`` :ref:`MeshInstance <class_MeshInstance>` visible by setting the ``visible`` property to ``true``.
+
+``dropped`` function step-by-step explanation
+"""""""""""""""""""""""""""""""""""""""""""""
+
+This function simply makes the ``laser_sight_mesh`` :ref:`MeshInstance <class_MeshInstance>` invisible by setting the ``visible`` property to ``false``.
+
+
+Pistol finished
+^^^^^^^^^^^^^^^
+
+.. image:: img/starter_vr_tutorial_pistol.png
+
+
+That is all we need to do to have working pistols in the project! Go ahead and run the project. If you climb up the stairs and grab the pistols, you can fire them at the sphere
+targets in the scene using the trigger button on the VR controller! If you fire at the targets long enough, they will break into pieces.
+
+
+
+Adding a shotgun
+----------------
+
+Next let's add a shotgun to the VR project.
+
+Adding a special shotgun :ref:`RigidBody <class_RigidBody>` should be fairly straightforward, as almost everything with the shotgun is the same as the pistol.
+
+Open up ``Shotgun.tscn``, which you can find in the ``Scenes`` folder and take a look at the scene. Almost everything is the same as in ``Pistol.tscn``.
+The only thing that is different, beyond name changes, is that instead of a single :ref:`Raycast <class_Raycast>`, there are five :ref:`Raycast <class_Raycast>` nodes.
+This is because a shotgun generally fires in a cone shape, so we are going to emulate that effect by having several :ref:`Raycast <class_Raycast>` nodes that will rotate
+randomly in a cone shape when the shotgun fires.
+
+Outside of that, everything is more or less the same as ``Pistol.tscn``.
+
+Let's write the code for the shotgun. Select the :ref:`RigidBody <class_RigidBody>` node called ``Shotgun`` and make a new script called ``Shotgun.gd``. Add the following code:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    extends VR_Interactable_Rigidbody
+
+    var flash_mesh
+    const FLASH_TIME = 0.25
+    var flash_timer = 0
+
+    var laser_sight_mesh
+    var shotgun_fire_sound
+
+    var raycasts
+    const BULLET_DAMAGE = 30
+    const COLLISION_FORCE = 4
+
+
+    func _ready():
+        flash_mesh = get_node("Shotgun_Flash")
+        flash_mesh.visible = false
+        
+        laser_sight_mesh = get_node("LaserSight")
+        laser_sight_mesh.visible = false
+        
+        raycasts = get_node("Raycasts")
+        shotgun_fire_sound = get_node("AudioStreamPlayer3D")
+
+
+    func _physics_process(delta):
+        if flash_timer > 0:
+            flash_timer -= delta
+            if flash_timer <= 0:
+                flash_mesh.visible = false
+
+
+    func interact():
+        if flash_timer <= 0:
+            
+            flash_timer = FLASH_TIME
+            flash_mesh.visible = true
+            
+            for raycast in raycasts.get_children():
+                
+                if not raycast is RayCast:
+                    continue
+                
+                raycast.rotation_degrees = Vector3(90 + rand_range(10, -10), 0, rand_range(10, -10))
+                
+                raycast.force_raycast_update()
+                if raycast.is_colliding():
+                    
+                    var body = raycast.get_collider()
+                    var direction_vector = raycasts.global_transform.basis.z.normalized()
+                    var raycast_distance = raycasts.global_transform.origin.distance_to(raycast.get_collision_point())
+                    
+                    if body.has_method("damage"):
+                        body.damage(BULLET_DAMAGE)
+                    
+                    if body is RigidBody:
+                        var collision_force = (COLLISION_FORCE / raycast_distance) * body.mass
+                        body.apply_impulse((raycast.global_transform.origin - body.global_transform.origin).normalized(), direction_vector * collision_force)
+            
+            shotgun_fire_sound.play()
+            
+            if controller != null:
+                controller.rumble = 0.25
+
+
+    func picked_up():
+        laser_sight_mesh.visible = true
+
+
+    func dropped():
+        laser_sight_mesh.visible = false
+
+
+The majority of this code is exactly the same as the code for the pistol with just a few *minor* changes that are primarily just different names.
+Due to how similar these scripts are, let's just focus on the changes.
+
+Explaining the shotgun code
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Like with the pistol, the shotgun extends ``VR_Interactable_Rigidbody`` so the VR controllers know that this object can be interacted with and what functions are
+available.
+
+There is only one new class variable:
+
+* ``raycasts``: A variable to hold the node that has all of the :ref:`Raycast <class_Raycast>` nodes as its children.
+
+The new class variable replaces the ``raycast`` variable from ``Pistol.gd``, because with the shotgun we need to process multiple :ref:`Raycast <class_Raycast>` nodes
+instead of just one. All of the other class variables are the same as ``Pistol.gd`` and function the same way, some just are renamed to be non-pistol specific.
+
+``interact`` function step-by-step explanation
+""""""""""""""""""""""""""""""""""""""""""""""
+
+The interact function first checks to see if the shotgun's muzzle flash is invisible by checking to see if ``flash_timer`` is less than or equal to zero. We do this so we
+can limit the rate of fire of the shotgun to the length of time the muzzle flash is visible, which is a simple solution for limiting how fast the player can fire.
+
+If ``flash_timer`` is zero or less, we then set ``flash_timer`` to ``FLASH_TIME`` so there is a delay before the shotgun can fire again. After that we set ``flash_mesh.visible``
+to ``true`` so the muzzle flash at the end of the shotgun is visible while ``flash_timer`` is more than zero.
+
+Next we call the ``force_raycast_update`` function on the :ref:`Raycast <class_Raycast>` node in ``raycast`` so that it gets the latest collision info from the physics world.
+We then check if the ``raycast`` hit something by checking if the ``is_colliding`` function is equal to ``true``.
+
+Next we go through each of the child nodes of the ``raycasts`` variable using a for loop. This way the code will go through each of the :ref:`Raycast <class_Raycast>` nodes
+that are children of the ``raycasts`` variable.
+
+_________________
+
+For each node, we check to see if ``raycast`` is *not* a :ref:`Raycast <class_Raycast>` node. If the node is not a :ref:`Raycast <class_Raycast>` node, we simply use ``continue`` to skip it.
+
+Next we rotate the ``raycast`` node randomly around a small ``10`` degrees cone by settings the ``rotation_degrees`` variable of the ``raycast`` to a Vector3 where the X and Z axis
+are a random number from ``-10`` to ``10``. This random number is selected using the ``rand_range`` function.
+
+Then we call the ``force_raycast_update`` function on the :ref:`Raycast <class_Raycast>` node in ``raycast`` so that it gets the latest collision info from the physics world.
+We then check if the ``raycast`` hit something by checking if the ``is_colliding`` function is equal to ``true``.
+
+The rest of the code is exactly the same, but this process is repeated for each :ref:`Raycast <class_Raycast>` node that is a child of the ``raycasts`` variable.
+
+_________________
+
+If the ``raycast`` hit something, then we get the :ref:`PhysicsBody <class_PhysicsBody>` it collided with through the ``get_collider`` function. We assign the
+hit :ref:`PhysicsBody <class_PhysicsBody>` to a variable called ``body``.
+
+We then get the direction of the raycast by getting it's positive ``Z`` directional axis from the :ref:`Basis <class_Basis>` on the ``raycast`` node's ``global_transform``.
+This will give us the direction the raycast is pointing on the Z axis, which is the same direction as the blue arrow on the :ref:`Spatial <class_Spatial>` gizmo when
+``Local space mode`` is enabled in the Godot editor. We store this direction in a variable called ``direction_vector``.
+
+Next we get the distance from the raycast origin to the raycast collision point by getting the distance from the global position, ``global_transform.origin`` of the ``raycast``
+node to the collision point of the raycast, ``raycast.get_collision_point``, using the ``distance_to`` function. This will give us the distance the :ref:`Raycast <class_Raycast>`
+traveled before it collided, which we store in a variable called ``raycast_distance``.
+
+Then the code checks if the :ref:`PhysicsBody <class_PhysicsBody>`, ``body``, has a function/method called ``damage`` using the ``has_method`` function. If the :ref:`PhysicsBody <class_PhysicsBody>`
+has a function/method called ``damage``, then we call the ``damage`` function and pass ``BULLET_DAMAGE`` so it takes damage from the bullet colliding into it.
+
+Regardless of whether the :ref:`PhysicsBody <class_PhysicsBody>` has a ``damage`` function, we then check to see if ``body`` is a :ref:`RigidBody <class_RigidBody>`-based node. If ``body`` is a
+:ref:`RigidBody <class_RigidBody>`-based node, then we want to push it when the bullet collides.
+
+To calculate the amount of force applied, we simply take ``COLLISION_FORCE`` and divide it by ``raycast_distance``, then we multiply the whole thing by ``body.mass``. We store this calculation in
+a variable called ``collision_force``. This will make collisions over a shorter distance apply move force than those over longer distances, giving a *slightly* more realistic collision response.
+
+We then push the :ref:`RigidBody <class_RigidBody>` using the ``apply_impulse`` function, where the position is a zero Vector3 so the force is applied from the center,
+and the collision force is the ``collision_force`` variable we calculated.
+
+_________________
+
+Once all of the :ref:`Raycast <class_Raycast>`s in the ``raycast`` variable have been iterated over, we then play the shotgun shot sound by calling the ``play`` function on the ``shotgun_fire_sound`` variable.
+
+Finally, we check to see if the shotgun is being held by a VR controller by checking to see if the ``controller`` variable is not equal to ``null``. If it is not equal to ``null``,
+we then set the ``rumble`` property of the VR controller to ``0.25``, so there is a slight rumble when the shotgun fires.
+
+Shotgun finished
+^^^^^^^^^^^^^^^^
+
+Everything else is exactly the same as the pistol, with at most just some simple name changes.
+
+Now the shotgun is finished! You can find the shotgun in the sample scene by looking around the back of one of the walls (not in the building though!).
+
+
+
+Adding a bomb
+-------------
+
+Okay, let's add a different special :ref:`RigidBody <class_RigidBody>`. Instead of adding something that shoots, let's add something we can throw - a bomb!
+
+Open up ``Bomb.tscn``, which is in the ``Scenes`` folder.
+
+The root node is a :ref:`RigidBody <class_RigidBody>` node that we'll be extending to use ``VR_Interactable_Rigidbody``, which has a :ref:`CollisionShape <class_CollisionShape>`
+like the other special :ref:`RigidBody <class_RigidBody>` nodes we've made so far. Likewise, there is a :ref:`MeshInstance <class_MeshInstance>` called ``Bomb`` that is used to
+display the mesh for the bomb.
+
+Then we have an :ref:`Area <class_Area>` node simply called ``Area`` that has a large :ref:`CollisionShape <class_CollisionShape>` as its child. We'll use this :ref:`Area <class_Area>`
+node to effect anything within it when the bomb explodes. Essentially, this :ref:`Area <class_Area>` node will be the blast radius for the bomb.
+
+There is also a couple :ref:`Particles <class_Particles>` nodes. One of the :ref:`Particles <class_Particles>` nodes are for the smoke coming out of the bomb's fuse, while another
+is for the explosion. You can take a look at the :ref:`ParticlesMaterial <class_ParticlesMaterial>` resources, which define how the particles work, if you want. We will not be covering
+how the particles work in this tutorial due to it being outside of the scope of this tutorial.
+
+There is one thing with the :ref:`Particles <class_Particles>` nodes that we need to make note of. If you select the ``Explosion_Particles`` node, you'll find that its ``lifetime`` property
+is set to ``0.75`` and that the ``one shot`` checkbox is enabled. This means that the particles will only play once, and the particles will last for ``0.75`` seconds.
+We'll need to know this so we can time the removal of the bomb with the end of the explosion :ref:`Particles <class_Particles>`.
+
+Let's write the code for the bomb. Select the ``Bomb`` :ref:`RigidBody <class_RigidBody>` node and make a new script called ``Bomb.gd``. Add the following code:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    extends VR_Interactable_Rigidbody
+
+    var bomb_mesh
+
+    const FUSE_TIME = 4
+    var fuse_timer = 0
+
+    var explosion_area
+    const EXPLOSION_DAMAGE = 100
+    const EXPLOSION_TIME = 0.75
+    var explosion_timer = 0
+    var exploded = false
+
+    const COLLISION_FORCE = 8
+
+    var fuse_particles
+    var explosion_particles
+    var explosion_sound
+
+
+    func _ready():
+        
+        bomb_mesh = get_node("Bomb")
+        explosion_area = get_node("Area")
+        fuse_particles = get_node("Fuse_Particles")
+        explosion_particles = get_node("Explosion_Particles")
+        explosion_sound = get_node("AudioStreamPlayer3D")
+        
+        set_physics_process(false)
+
+
+    func _physics_process(delta):
+        
+        if fuse_timer < FUSE_TIME:
+            
+            fuse_timer += delta
+            
+            if fuse_timer >= FUSE_TIME:
+                
+                fuse_particles.emitting = false
+                
+                explosion_particles.one_shot = true
+                explosion_particles.emitting = true
+                
+                bomb_mesh.visible = false
+                
+                collision_layer = 0
+                collision_mask = 0
+                mode = RigidBody.MODE_STATIC
+                
+                for body in explosion_area.get_overlapping_bodies():
+                    if body == self:
+                        pass
+                    else:
+                        if body.has_method("damage"):
+                            body.damage(EXPLOSION_DAMAGE)
+                        
+                        if body is RigidBody:
+                            var direction_vector = body.global_transform.origin - global_transform.origin
+                            var bomb_distance = direction_vector.length()
+                            var collision_force = (COLLISION_FORCE / bomb_distance) * body.mass
+                            body.apply_impulse(Vector3.ZERO, direction_vector.normalized() * collision_force)
+                
+                exploded = true
+                explosion_sound.play()
+        
+        
+        if exploded:
+            
+            explosion_timer += delta
+            
+            if explosion_timer >= EXPLOSION_TIME:
+                
+                explosion_area.monitoring = false
+
+                if controller != null:
+                    controller.held_object = null
+                    controller.hand_mesh.visible = true
+                    
+                    if controller.grab_mode == "RAYCAST":
+                        controller.grab_raycast.visible = true
+                
+                queue_free()
+
+
+    func interact():
+        set_physics_process(true)
+        
+        fuse_particles.emitting = true
+
+
+Let's go over how this script works.
+
+
+Explaining the bomb code
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+Like with the other special :ref:`RigidBody <class_RigidBody>` nodes, the bomb extends ``VR_Interactable_Rigidbody`` so the VR controllers know this object can be interacted with and
+that the functions defined defined in ``VR_Interactable_Rigidbody`` can be called when this object is held by a VR controller.
+
+Next, let's look at the class variables:
+
+* ``bomb_mesh``: A variable to hold the :ref:`MeshInstance <class_MeshInstance>` node that is used for the non-exploded bomb.
+* ``FUSE_TIME``: A constant to define how long the fuse will 'burn' before the bomb explodes
+* ``fuse_timer``: A variable to hold the length of time that has passed since the bomb's fuse has started to burn.
+* ``explosion_area``: A variable to hold the :ref:`Area <class_Area>` node used to detect objects within the bomb's explosion.
+* ``EXPLOSION_DAMAGE``: A constant to define how much damage is applied with the bomb explodes.
+* ``EXPLOSION_TIME``: A constant to define how long the bomb will last in the scene after it explodes. This value should be the same as the ``lifetime`` property of the explosion :ref:`Particles <class_Particles>` node.
+* ``explosion_timer`` A variable to hold the length of time that has passed since the bomb exploded.
+* ``exploded``: A variable to hold whether the bomb has exploded or not.
+* ``COLLISION_FORCE``: A constant that defines the amount of force that is applied to :ref:`RigidBody <class_RigidBody>` nodes when the bomb explodes.
+* ``fuse_particles``: A variable to hold a reference to the :ref:`Particles <class_Particles>` node used for the bomb's fuse.
+* ``explosion_particles``: A variable to hold a reference to the :ref:`Particles <class_Particles>` node used for the bomb's explosion.
+* ``explosion_sound``: A variable to hold a reference to the :ref:`AudioStreamPlayer3D <class_AudioStreamPlayer3D>` node used for the explosion sound.
+
+
+``_ready`` function step-by-step explanation
+""""""""""""""""""""""""""""""""""""""""""""
+
+The ``_ready`` function first gets all of the nodes from the bomb scene and assigns them to their respective class variables for later use.
+
+Then we call ``set_physics_process`` and pass ``false`` so ``_physics_process`` is not executed. We do this because the code in ``_physics_process`` will start burning
+the fuse and exploding the bomb, which we only want to do when the user interacts with the bomb. If we did not disable ``_physics_process``, the bomb's fuse would start
+before the user has a chance to get to the bomb.
+
+
+``_physics_process`` function step-by-step explanation
+""""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+The ``_physics_process`` function first checks to see if ``fuse_timer`` is less than ``FUSE_TIME``. If it is, then the bomb's fuse is still burning.
+
+If the bomb's fuse is still burning, we then add time, ``delta``, to the ``fuse_timer`` variable. We then check to see if ``fuse_timer`` is more than or equal to ``FUSE_TIME``
+now that we have added ``delta`` to it. If ``fuse_timer`` is more than or equal to ``FUSE_TIME``, then the fuse has just finished and we need to explode the bomb.
+
+To explode the bomb, we first stop emitting particles for the fuse by setting ``emitting`` to ``false`` on ``fuse_particles``. We then tell the explosion :ref:`Particles <class_Particles>`
+node, ``explosion_particles``, to emit all of its particle in a single shot by setting ``one_shot`` to ``true``. After that, we set ``emitting`` to ``true`` on ``explosion_particles`` so it looks
+like the bomb has exploded. To help make it look like the bomb exploded, we hide the bomb :ref:`MeshInstance <class_MeshInstance>` node by setting ``bomb_mesh.visible`` to ``false``.
+
+To keep the bomb from colliding with other objects in the physics world, we set the ``collision_layer`` and ``collision_mask`` properties of the bomb to ``0``. We also
+change the :ref:`RigidBody <class_RigidBody>` mode to ``MODE_STATIC`` so the bomb :ref:`RigidBody <class_RigidBody>` does not move.
+
+Then we need to get all of the :ref:`PhysicsBody <class_PhysicsBody>` nodes within the ``explosion_area`` node. To do this, we use the ``get_overlapping_bodies`` in a for loop. The ``get_overlapping_bodies``
+function will return an array of :ref:`PhysicsBody <class_PhysicsBody>` nodes within the :ref:`Area <class_Area>` node, which is exactly what we are looking for.
+
+_________________
+
+For each :ref:`PhysicsBody <class_PhysicsBody>` node, which we store in a variable called ``body``, we check to see if it is equal to ``self``. We do this so the bomb does not accidentally explode
+itself, as the ``explosion_area`` could potentially detect the ``Bomb`` :ref:`RigidBody <class_RigidBody>` as a PhysicsBody within the explosion area.
+
+If the :ref:`PhysicsBody <class_PhysicsBody>` node, ``body``, is not the bomb, then we first check to see if the :ref:`PhysicsBody <class_PhysicsBody>` node has a function
+called ``damage``. If the :ref:`PhysicsBody <class_PhysicsBody>` node has a function called ``damage``, we call it and pass ``EXPLOSION_DAMAGE`` to it so it takes damage from the explosion.
+
+Next we check to see if the :ref:`PhysicsBody <class_PhysicsBody>` node is a :ref:`RigidBody <class_RigidBody>`. If ``body`` is a :ref:`RigidBody <class_RigidBody>`, we want to move it
+when the bomb explodes.
+
+To move the :ref:`RigidBody <class_RigidBody>` node when the bomb explodes, we first need to calculate the direction from the bomb to the :ref:`RigidBody <class_RigidBody>` node. To do this
+we subtract the global position of the bomb, ``global_transform.origin`` from the global position of the :ref:`RigidBody <class_RigidBody>`. This will give us a :ref:`Vector3 <class_Vector3>`
+that points from the bomb to the :ref:`RigidBody <class_RigidBody>` node. We store this :ref:`Vector3 <class_Vector3>` in a variable called ``direction_vector``.
+
+We then calculate the distance the :ref:`RigidBody <class_RigidBody>` is from the bomb by using the ``length`` function on ``direction_vector``. We store the distance in a variable called
+``bomb_distance``.
+
+We then calculate the amount of force the bomb will be applied to the :ref:`RigidBody <class_RigidBody>` node when the bomb explodes by dividing ``COLLISION_FORCE`` by
+``bomb_distance``, and multiplying that by ``collision_force``. This will make it so if the :ref:`RigidBody <class_RigidBody>` node is closer to the bomb, it will be pushed farther.
+
+Finally, we push the :ref:`RigidBody <class_RigidBody>` node using the ``apply_impulse`` function, with a :ref:`Vector3 <class_Vector3>` position of zero and ``collision_force``
+multiplied by ``direction_vector.normalized`` as the force. This will send the :ref:`RigidBody <class_RigidBody>` node flying when the bomb explodes.
+
+_________________
+
+After we have looped through all of the :ref:`PhysicsBody <class_PhysicsBody>` nodes within the ``explosion_area``, we set the ``exploded`` variable to ``true`` so the code knows the bomb
+exploded and call ``play`` on ``explosion_sound`` so the sound of an explosion is played.
+
+_________________
+
+Alright, the next section of code starts by first checking if ``exploded`` is equal to ``true``.
+
+If ``exploded`` is equal to ``true``, then that means the bomb is waiting for the explosion particles to finish before it frees/destroys itself. We add time, ``delta``, to
+``explosion_timer`` so we can track how long it has been since the bomb has exploded.
+
+If ``explosion_timer`` is greater than or equal to ``EXPLOSION_TIME`` after we added ``delta``, then the explosion timer just finished.
+
+If the explosion timer just finished, we set ``explosion_area.monitoring`` to ``false``. The reason we do this is because there was a bug that would print an error when you
+freed/deleted an :ref:`Area <class_Area>` node when the ``monitoring`` property was true. To make sure this doesn't happen, we simply set ``monitoring`` to false on ``explosion_area``.
+
+Next we check to see if the bomb is being held by a VR controller by checking to see if the ``controller`` variable is not equal to ``null``. If the bomb is being held by a VR controller,
+we set the ``held_object`` property of the VR controller, ``controller``, to ``null``. Because the VR controller is no longer holding anything, we make the VR controller's hand mesh
+visible by setting ``controller.hand_mesh.visible`` to ``true``. Then we check to see if the VR controller grab mode is ``RAYCAST``, and if it is we set ``controller.grab_raycast.visible`` to
+``true`` so the 'laser sight' for the grab raycast is visible.
+
+Finally, regardless if the bomb is being held by a VR controller or not, we call ``queue_free`` so the bomb scene is freed/removed from the scene.
+
+``interact`` function step-by-step explanation
+""""""""""""""""""""""""""""""""""""""""""""""
+
+First the ``interact`` function calls ``set_physics_process`` and passes ``true`` so the code in ``_physics_process`` starts executing. This will start the bomb's fuse and
+eventually lead to the bomb exploding.
+
+Finally, we start the fuse particles by setting ``fuse_particles.visible`` to ``true``.
+
+
+Bomb finished
+^^^^^^^^^^^^^
+
+Now the bomb is ready to go! You can find the bombs in the orange building.
+
+Because of how we are calculating the VR controller's velocity, it is easiest to throw the bombs using a thrusting-like motion instead of a more natural throwing-like motion.
+The smooth curve of a throwing-like motion is harder to track with the code we are using for calculating the velocity of the VR controllers, so it does not always work correctly
+and can lead inaccurately calculated velocities.
+
+
+
+Adding a sword
+--------------
+
+Let's add one last special :ref:`RigidBody <class_RigidBody>`-based node that can destroy targets. Let's add a sword so we can slice through the targets!
+
+Open up ``Sword.tscn``, which you can find in the ``Scenes`` folder.
+
+There is not a whole lot going on here. All of the child nodes of the root ``Sword`` :ref:`RigidBody <class_RigidBody>` node are rotated to they are positioned correctly when the
+VR controller picks them up, there is a :ref:`MeshInstance <class_MeshInstance>` node for displaying the sword, and there is an :ref:`AudioStreamPlayer3D <class_AudioStreamPlayer3D>`
+node that holds a sound for the sword colliding with something.
+
+There is one thing that is slightly different though. There is a :ref:`KinematicBody <class_KinematicBody>` node called ``Damage_Body``. If you take a look at it, you'll find that it
+is not on any collision layers, and is instead only on a single collision mask. This is so the :ref:`KinematicBody <class_KinematicBody>` will not effect other
+:ref:`PhysicsBody <class_PhysicsBody>` nodes in the scene, but it will still be effected by :ref:`PhysicsBody <class_PhysicsBody>` nodes.
+
+We are going to use the ``Damage_Body`` :ref:`KinematicBody <class_KinematicBody>` node to detect the collision point and normal when the sword collides with something in the scene.
+
+.. tip:: While this is perhaps not the best way of getting the collision information from a performance point of view, it does give us a lot of information we can use for post-processing!
+         Using a :ref:`KinematicBody <class_KinematicBody>` this way means we can detect exactly where ths sword collided with other :ref:`PhysicsBody <class_PhysicsBody>` nodes.
+
+That is really the only thing note worthy about the sword scene. Select the ``Sword`` :ref:`RigidBody <class_RigidBody>` node and make a new script called ``Sword.gd``.
+Add the following code:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    extends VR_Interactable_Rigidbody
+
+    const SWORD_DAMAGE = 2
+
+    const COLLISION_FORCE = 0.15
+
+    var damage_body = null
+
+
+    func _ready():
+        damage_body = get_node("Damage_Body")
+        damage_body.add_collision_exception_with(self)
+        sword_noise = get_node("AudioStreamPlayer3D")
+
+
+    func _physics_process(_delta):
+        
+        var collision_results = damage_body.move_and_collide(Vector3.ZERO, true, true, true);
+        
+        if (collision_results != null):
+            if collision_results.collider.has_method("damage"):
+                collision_results.collider.damage(SWORD_DAMAGE)
+            
+            if collision_results.collider is RigidBody:
+                if controller == null:
+                    collision_results.collider.apply_impulse(
+                        collision_results.position, 
+                        collision_results.normal * linear_velocity * COLLISION_FORCE)
+                else:
+                    collision_results.collider.apply_impulse(
+                        collision_results.position,
+                        collision_results.normal * controller.controller_velocity * COLLISION_FORCE)
+            
+            sword_noise.play()
+
+Let's go over how this script works!
+
+
+Explaining the sword code
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Like with the other special :ref:`RigidBody <class_RigidBody>` nodes, the sword extends ``VR_Interactable_Rigidbody`` so the VR controllers know this object can be interacted with and
+that the functions defined defined in ``VR_Interactable_Rigidbody`` can be called when this object is held by a VR controller.
+
+Next, let's look at the class variables:
+
+* ``SWORD_DAMAGE``: A constant to define the amount of damage the sword does. This damage is applied  to every object in the sword on every ``_physics_process`` call
+* ``COLLISION_FORCE``: A constant that defines the amount of force applied to :ref:`RigidBody <class_RigidBody>` nodes when the sword collides with a :ref:`PhysicsBody <class_PhysicsBody>`.
+* ``damage_body``: A variable to hold the :ref:`KinematicBody <class_KinematicBody>` node used to detect whether the sword is stabbing a :ref:`PhysicsBody <class_PhysicsBody>` node or not.
+* ``sword_noise``: A variable to hold the :ref:`AudioStreamPlayer3D <class_AudioStreamPlayer3D>` node used to play a sound when the sword collides with something.
+
+
+``_ready`` function step-by-step explanation
+""""""""""""""""""""""""""""""""""""""""""""
+
+All we are doing in the ``_ready`` function is getting the ``Damage_Body`` :ref:`KinematicBody <class_KinematicBody>` node and assigning it to ``damage_body``.
+Because we do not want the sword to detect a collision with the root :ref:`RigidBody <class_RigidBody>` node of the sword, we call
+``add_collision_exception_with`` on ``damage_body`` and pass ``self`` so the sword will not be detected.
+
+Finally, we get the :ref:`AudioStreamPlayer3D <class_AudioStreamPlayer3D>` node for the sword collision sound and apply it to the ``sword_noise`` variable.
+
+
+``_physics_process`` function step-by-step explanation
+""""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+First we need to determine whether the sword is colliding with something or not. To do this, we use the ``move_and_collide`` function of the ``damage_body`` node.
+Unlike how ``move_and_collide`` is normally used, we are not passing a velocity and instead are passing an empty :ref:`Vector3 <class_Vector3>`. Because we do not
+want the ``damage_body`` node to move, we set the ``test_only`` argument (the fourth argument) as ``true`` so the :ref:`KinematicBody <class_KinematicBody>` generates
+collision info without actually causing any collisions within the collision world.
+
+The ``move_and_collide`` function will return a :ref:`KinematicCollision <class_KinematicCollision>` class that has all of the information we need for detecting collisions
+on the sword. We assign the return value of ``move_and_collide`` to a variable called ``collision_results``.
+
+Next we check to see if ``collision_results`` is not equal to ``null``. If ``collision_results`` is not equal to ``null``, then we know that the sword has collided with something.
+
+We then check to see if the :ref:`PhysicsBody <class_PhysicsBody>` the sword collided with has a function/method called ``damage`` using the ``has_method`` function. If the
+:ref:`PhysicsBody <class_PhysicsBody>` has a function called ``damage_body``, we call it and pass the amount of damage the sword does, ``SWORD_DAMAGE``, to it.
+
+Next we check to see if the :ref:`PhysicsBody <class_PhysicsBody>` the sword collided with is a :ref:`RigidBody <class_RigidBody>`. If what the sword collided with is a
+:ref:`RigidBody <class_RigidBody>` node, we then check to see if the sword is being held by a VR controller or not by checking to see if ``controller`` is equal to ``null``.
+
+If the sword is not being held by a VR controller, ``controller`` is equal to ``null``, then we move the :ref:`RigidBody <class_RigidBody>` node the sword collided with using
+the ``apply_impulse`` function. For the ``position`` of the ``apply_impulse`` function, we use ``collision_position`` variable stored within the :ref:`KinematicCollision <class_KinematicCollision>`
+class in ``collision_results``. For the ``velocity`` of the ``apply_impulse`` function, we use the ``collision_normal`` multiplied by the ``linear_velocity`` of the sword's
+:ref:`RigidBody <class_RigidBody>` node multiplied by ``COLLISION_FORCE``.
+
+If the sword is being held by a VR controller, ``controller`` is not equal to ``null``, then we move the :ref:`RigidBody <class_RigidBody>` node the sword collided with using
+the ``apply_impulse`` function. For the ``position`` of the ``apply_impulse`` function, we use ``collision_position`` variable stored within the :ref:`KinematicCollision <class_KinematicCollision>`
+class in ``collision_results``. For the ``velocity`` of the ``apply_impulse`` function, we use the ``collision_normal`` multiplied by the VR controller's velocity multiplied by ``COLLISION_FORCE``.
+
+Finally, regardless of whether the :ref:`PhysicsBody <class_PhysicsBody>` is a :ref:`RigidBody <class_RigidBody>` or not, we play the sound of the sword colliding with
+something by calling ``play`` on ``sword_noise``.
+
+
+Sword finished
+^^^^^^^^^^^^^^
+
+.. image:: img/starter_vr_tutorial_sword.png
+
+With that done, you can now slice through the targets! You can find the sword in the corner in between the shotgun and the pistol.
+
+
+
+Updating the target UI
+----------------------
+
+Let's update the UI as the sphere targets are destroyed.
+
+Open up ``Main_VR_GUI.tscn``, which you can find in the ``Scenes`` folder.
+Feel free to look at how the scene is setup if you want, but in an effort to keep this tutorial from becoming too long, we will not be covering the scene setup in this tutorial.
+
+Expand the ``GUI`` :ref:`Viewport <class_Viewport>` node and then select the ``Base_Control`` node. Add a new script called ``Base_Control.gd``, and add the following:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    extends Control
+
+    var sphere_count_label
+
+    func _ready():
+        sphere_count_label = get_node("Label_Sphere_Count")
+
+        get_tree().root.get_node("Game").sphere_ui = self
+
+
+    func update_ui(sphere_count):
+        if sphere_count > 0:
+            sphere_count_label.text = str(sphere_count) + " Spheres remaining"
+        else:
+            sphere_count_label.text = "No spheres remaining! Good job!"
+
+Let's go over how this script works real quick.
+
+First, in ``_ready``, we get the :ref:`Label <class_Label>` that shows how many spheres are left and assign it to the ``sphere_count_label`` class variable.
+Next, we get ``Game.gd`` by using ``get_tree().root`` and assign ``sphere_ui`` to this script.
+
+In ``update_ui``, we change the sphere :ref:`Label <class_Label>`'s text. If there is at least one sphere remaining, we change the text to show how many spheres are still
+left in the world. If there are no more spheres remaining, we change the text and congratulate the player.
+
+
+
+Adding the final special RigidBody
+----------------------------------
+
+Finally, before we finish this tutorial, let's add a way to reset the game while in VR.
+
+Open up ``Reset_Box.tscn``, which you will find in ``Scenes``. Select the ``Reset_Box`` :ref:`RigidBody <class_RigidBody>` node and make a new script called ``Reset_Box.gd``.
+Add the following code:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    extends VR_Interactable_Rigidbody
+
+    var start_transform
+
+    var reset_timer = 0
+    const RESET_TIME = 10
+    const RESET_MIN_DISTANCE = 1
+
+
+    func _ready():
+        start_transform = global_transform
+
+
+    func _physics_process(delta):
+        if start_transform.origin.distance_to(global_transform.origin) >= RESET_MIN_DISTANCE:
+            reset_timer += delta
+            if reset_timer >= RESET_TIME:
+                global_transform = start_transform
+                reset_timer = 0
+
+
+    func interact():
+        # (Ignore the unused variable warning)
+        # warning-ignore:return_value_discarded
+        get_tree().change_scene("res://Game.tscn")
+
+
+    func dropped():
+        global_transform = start_transform
+        reset_timer = 0
+
+
+Let's quickly go over how this script works.
+
+
+Explaining the reset box code
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Like with the other special :ref:`RigidBody <class_RigidBody>`-based objects we've created, the reset box extends ``VR_Interactable_Rigidbody``.
+
+The ``start_transform`` class variable will store the global transform of the reset box when the game starts, the ``reset_timer`` class variable will hold the length of
+time that has passed since the reset box's position has moved, the ``RESET_TIME`` constant defines the length of time the reset box has to wait before being reset, and
+the ``RESET_MIN_DISTANCE`` constant defines how far the reset box has to be away from it's initial position before the reset timer starts.
+
+In the ``_ready`` function all we are doing is storing the ``global_transform`` of the reset position when the scene starts. This is so we can reset the position, rotation, and scale
+of the reset box object to this initial transform when enough time has passed.
+
+In the ``_physics_process`` function, the code checks to see if the reset box's initial position to the reset box's current position is farther than ``RESET_MIN_DISTANCE``. If it is
+farther, then it starts adding time, ``delta``, to ``reset_timer``. Once ``reset_timer`` is more than or equal to ``RESET_TIME``, we reset the ``global_transform`` to the ``start_transform``
+so the reset box is back in its initial position. We then set ``reset_timer`` to ``0``.
+
+The ``interact`` function simply reloads the ``Game.tscn`` scene using ``get_tree().change_scene``. This will reload the game scene, resetting everything.
+
+Finally, the ``dropped`` function resets the ``global_transform`` to the initial transform in ``start_transform`` so the reset box has its initial position/rotation. Then ``reset_timer`` is
+set to ``0`` so the timer is reset.
+
+
+Reset box finished
+^^^^^^^^^^^^^^^^^^
+
+With that done, when you grab and interact with the reset box, the entire scene will reset/restart and you can destroy all the targets again!
+
+.. note:: Resetting the scene abruptly without any sort of transition can lead to discomfort in VR.
+
+
+
+Final notes
+-----------
+
+.. image:: img/starter_vr_tutorial_pistol.png
+
+Whew! That was a lot of work.
+
+Now you have a fully working VR project with multiple different types of special :ref:`RigidBody <class_RigidBody>`-based nodes that can be used and extended. Hopefully this will
+help serve as an introduction to making fully-featured VR games in Godot! The code and concepts detailed in this tutorial can be expanded on to make puzzle games, action games,
+story-based games, and more!
+
+.. warning:: You can download the finished project for this tutorial series on the `OpenVR GitHub repository <https://github.com/GodotVR/godot_openvr_fps>`_, under the releases tab!
+