Bladeren bron

Added a FPS (First person shooter) game tutorial in three parts.

* The first part covers making a first person character.
* The second part covers adding weapons/guns to the character built in part one.
* The third part adds ammo to the guns and sounds for when the player fires.
TwistedTwigleg 7 jaren geleden
bovenliggende
commit
c33d9e744c

BIN
tutorials/3d/fps_tutorial/files/Godot_FPS_BlenderFiles.zip


BIN
tutorials/3d/fps_tutorial/files/Godot_FPS_Finished.zip


BIN
tutorials/3d/fps_tutorial/files/Godot_FPS_Starter.zip


BIN
tutorials/3d/fps_tutorial/img/AnimationPlayerAddPoint.png


BIN
tutorials/3d/fps_tutorial/img/AnimationPlayerAddTrack.png


BIN
tutorials/3d/fps_tutorial/img/AnimationPlayerCallFuncTrack.png


BIN
tutorials/3d/fps_tutorial/img/AnimationPlayerEditPoints.png


BIN
tutorials/3d/fps_tutorial/img/FinishedTutorialPicture.png


BIN
tutorials/3d/fps_tutorial/img/LocalSpaceExample.png


BIN
tutorials/3d/fps_tutorial/img/LocalSpaceExampleGizmo.png


BIN
tutorials/3d/fps_tutorial/img/LocalSpaceExample_3D.png


BIN
tutorials/3d/fps_tutorial/img/PartOneFinished.png


BIN
tutorials/3d/fps_tutorial/img/PartThreeFinished.png


BIN
tutorials/3d/fps_tutorial/img/PartTwoFinished.png


BIN
tutorials/3d/fps_tutorial/img/PlayerSceneTree.png


BIN
tutorials/3d/fps_tutorial/img/WorldSpaceExample.png


BIN
tutorials/3d/fps_tutorial/img/WorldSpaceExample_3D.png


+ 10 - 0
tutorials/3d/fps_tutorial/index.rst

@@ -0,0 +1,10 @@
+FPS tutorial
+============
+
+.. toctree::
+   :maxdepth: 1
+   :name: toc-3D-fps-tutorial
+
+   part_one
+   part_two
+   part_three

+ 556 - 0
tutorials/3d/fps_tutorial/part_one.rst

@@ -0,0 +1,556 @@
+.. _doc_fps_tutorial_part_one:
+
+Part 1
+======
+
+Tutorial introduction
+---------------------
+
+.. image:: img/FinishedTutorialPicture.png
+
+This tutorial series will show you how to make a single player FPS game.
+
+Throughout the course of these tutorials, we will cover how:
+
+- To make a first person character, with sprinting and a flash light.
+- To make a simple animation state machine for handling animation transitions.
+- To add a pistol, rifle, and knife to the first person character.
+- To add ammo and reloading to weapons that consume ammo.
+- To add sounds that play when the guns fire.
+
+.. 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 **before** going through
+          this tutorial series.
+
+          Remember: Making 3D games is much harder than making 2D games. If you do not know
+          how to make 2D games you will likely struggle making 3D games.
+
+          This tutorial assumes you know have experience working with the Godot editor,
+          have basic programming experience in GDScript, and have basic experience in game development.
+
+You can find the start assets for this parts 1 through 3 here: :download:`Godot_FPS_Starter.zip <files/Godot_FPS_Starter.zip>`
+
+.. warning:: A video version of this tutorial series is coming soon!
+
+The provided starter assets contain a animated 3D model, a bunch of 3D models for making levels,
+and a few scenes already configured for this tutorial.
+
+All assets provided are created by me (TwistedTwigleg) unless otherwise noted, and are
+released under the ``MIT`` license.
+
+.. note:: The skybox is created by **StumpyStrust** on OpenGameArt. The skybox used is
+          licensed under ``CC0``.
+
+          The font used is **Titillium-Regular**, and is licensed under the ``SIL Open Font License, Version 1.1``.
+
+.. note:: You can find the finished project for parts 1 through 3 at the bottom of
+          :ref:`doc_fps_tutorial_part_three`.
+
+Part Overview
+-------------
+
+In this part we will be making a first person player that can move around
+the environment.
+
+.. image:: img/PartOneFinished.png
+
+By the end of this part you will have a working first person character with a
+mouse based camera that can walk, jump, and sprint around the game environment in
+any direction
+
+Getting everything setup
+------------------------
+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 pre-setup scenes we
+          will be using throughout the tutorial series.
+
+First, go open the project settings and go to the "Input Map" tab. You'll find several
+actions have already been defined. We will be using these actions for our player.
+Feel free to change the keys bound to these actions if you want.
+
+While we still have the project settings open, quickly go check if MSAA (MultiSample Anti-Aliasing)
+is turned off. We want to make sure MSAA is off because otherwise we will get strange red lines
+between the tiles in our level later.
+
+.. tip:: The reason we get those red lines is because we are using lowpoly models
+        with low resolution textures. MSAA tries to reduce jagged edges between models and
+        because we are using lowpoly and low resolution textures in this project,
+        we need to turn it off to avoid texture bleeding.
+
+        A bonus with turning off MSAA is we get a more 'retro' looking result.
+
+_________
+
+Lets take a second to see what we have in the starter assets.
+
+Included in the starter assets are five scenes: ``BulletScene.tscn``, ``Player.tscn``,
+``SimpleAudioPlayer.tscn``, ``TestingArea.tscn``, and ``TestLevel.tscn``.
+
+We will visit all of these scenes later, but for now open up ``Player.tscn``.
+
+.. note:: There are a bunch of scenes and a few textures in the ``Assets`` folder. You can look at these if you want,
+          but we will not be directly using them in this tutorial.
+
+Making the FPS movement logic
+-----------------------------
+
+Once you have ``Player.tscn`` open, let's take a quick look at how it is setup
+
+.. image:: img/PlayerSceneTree.png
+
+First, notice how the player's collision shapes are setup. Using a vertical pointing
+capsule as the collision shape for the player is fairly common in most first person games.
+
+We are adding a small square to the 'feet' of the player so the player does not
+feel like they are balancing on a single point.
+
+.. note:: Many times player will notice how the collision shape being circular when
+          they walk to an edge and slide off. We are adding the small square at the
+          bottom of the capsule to reduce sliding on, and around, edges.
+
+Another thing to notice is how many nodes are children of ``Rotation_helper``. This is because
+``Rotation_helper`` contains all of the nodes we want to rotate on the ``X`` axis (up and down).
+The reason behind this is so we rotate ``Player`` on the ``Y`` axis, and ``Rotation_helper`` on
+the ``X`` axis.
+
+.. note:: If we did not use ``Rotation_helper`` then we'd likely have cases where we are rotating
+          both the ``X`` and ``Y`` axes at the same time. This can lead to undesirable results, as we then
+          could rotate on all three axes in some cases.
+
+_________
+
+Attach a new script to the ``Player`` node and call it ``Player.gd``.
+
+Lets programming our player by adding the ability to move around, look around with the mouse, and jump.
+Add the following code to ``Player.gd``:
+
+::
+
+    extends KinematicBody
+
+    const norm_grav = -24.8
+    var vel = Vector3()
+    const MAX_SPEED = 20
+    const JUMP_SPEED = 18
+    const ACCEL = 3.5
+
+    const DEACCEL= 16
+    const MAX_SLOPE_ANGLE = 40
+
+    var camera
+    var camera_holder
+
+    # You may need to adjust depending on the sensitivity of your mouse
+    const MOUSE_SENSITIVITY = 0.05
+
+    var flashlight
+
+    func _ready():
+        camera = get_node("Rotation_helper/Camera")
+        camera_holder = get_node("Rotation_helper")
+
+        set_physics_process(true)
+
+        Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
+        set_process_input(true)
+
+        flashlight = get_node("Rotation_helper/Flashlight")
+
+    func _physics_process(delta):
+        var dir = Vector3()
+        var cam_xform = camera.get_global_transform()
+
+        if Input.is_action_pressed("movement_forward"):
+            dir += -cam_xform.basis.z.normalized()
+        if Input.is_action_pressed("movement_backward"):
+            dir += cam_xform.basis.z.normalized()
+        if Input.is_action_pressed("movement_left"):
+            dir += -cam_xform.basis.x.normalized()
+        if Input.is_action_pressed("movement_right"):
+            dir += cam_xform.basis.x.normalized()
+
+        if is_on_floor():
+            if Input.is_action_just_pressed("movement_jump"):
+                vel.y = JUMP_SPEED
+
+        if Input.is_action_just_pressed("flashlight"):
+    		if flashlight.is_visible_in_tree():
+    			flashlight.hide()
+    		else:
+    			flashlight.show()
+
+        dir.y = 0
+        dir = dir.normalized()
+
+        var grav = norm_grav
+        vel.y += delta*grav
+
+        var hvel = vel
+        hvel.y = 0
+
+        var target = dir
+        target *= MAX_SPEED
+
+        var accel
+        if dir.dot(hvel) > 0:
+            accel = ACCEL
+        else:
+            accel = DEACCEL
+
+        hvel = hvel.linear_interpolate(target, accel*delta)
+        vel.x = hvel.x
+        vel.z = hvel.z
+        vel = move_and_slide(vel,Vector3(0,1,0), 0.05, 4, deg2rad(MAX_SLOPE_ANGLE))
+
+        # (optional, but highly useful) Capturing/Freeing the cursor
+        if Input.is_action_just_pressed("ui_cancel"):
+            if Input.get_mouse_mode() == Input.MOUSE_MODE_VISIBLE:
+                Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
+            else:
+                Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
+
+    func _input(event):
+        if event is InputEventMouseMotion && Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:
+            camera_holder.rotate_x(deg2rad(event.relative.y * MOUSE_SENSITIVITY))
+            self.rotate_y(deg2rad(event.relative.x * MOUSE_SENSITIVITY * -1))
+
+            var camera_rot = camera_holder.rotation_degrees
+            camera_rot.x = clamp(camera_rot.x, -70, 70)
+            camera_holder.rotation_degrees = camera_rot
+
+This is a lot of code, so let's break it down from top to bottom:
+
+_________
+
+First, we define some global variables to dictate how our player will move about the world.
+
+.. note:: Throughout this tutorial, *variables defined outside functions will be
+          referred to as "global variables"*. This is because we can access any of these
+          variables from any place in the script. We can "globally" access them, hence the
+          name.
+
+Lets go through each of the global variables:
+
+- ``norm_grav``: How strong gravity pulls us down while we are walking.
+- ``vel``: Our :ref:`KinematicBody <class_KinematicBody>`'s velocity.
+- ``MAX_SPEED``: The fastest speed we can reach. Once we hit this speed, we will not go any faster.
+- ``JUMP_SPEED``: How high we can jump.
+- ``ACCEL``: How fast we accelerate. The higher the value, the faster we get to max speed.
+- ``DEACCEL``: How fast we are going to decelerate. The higher the value, the faster we will come to a complete stop.
+- ``MAX_SLOPE_ANGLE``: The steepest angle we can climb.
+- ``camera``: The :ref:`Camera <class_Camera>` node.
+- ``rotation_helper``: A :ref:`Spatial <class_Spatial>` node holding everything we want to rotate on the X axis (up and down).
+- ``MOUSE_SENSITIVITY``: How sensitive the mouse is. I find a value of ``0.05`` works well for my mouse, but you may need to change it based on how sensitive your mouse is.
+- ``flashlight``: A :ref:`Spotlight <class_Spotlight>` node that will act as our player's flashlight.
+
+You can tweak many of these variables to get different results. For example, by lowering ``normal_gravity`` and/or
+increasing ``JUMP_SPEED`` you can get a more 'floaty' feeling character.
+Feel free to experiment!
+
+_________
+
+Now lets look at the ``_ready`` function:
+
+First we get the ``camera`` and ``rotation_helper`` nodes and store them into their variables.
+After that we set ``_physics_process`` to ``true``. Then we need to set the mouse mode to captured.
+
+This will hide the mouse and keep it at the center of the screen. We do this for two reasons:
+The first reason being we do not want to the player to see their mouse cursor as they play.
+The second reason is because we do not want the cursor to leave the game window. If the cursor leaves
+the game window there could be instances where the player clicks outside the window, and then the game
+would lose focus. To assure neither of these issues happen, we capture the mouse cursor.
+
+.. note:: see :ref:`Input documentation <class_Input>` for the various mouse modes. We will only be using
+          ``MOUSE_MODE_CAPTURED`` and ``MOUSE_MODE_VISIBLE`` in this tutorial series.
+
+Finally, we call ``set_process_input(true)``. We need to use ``_input`` so we can rotate the player and
+camera when there is mouse motion.
+
+_________
+
+Next is ``_physics_process``:
+
+We define a directional vector (``dir``) for storing the direction the player intends to move.
+
+Next we get the camera's global transform and store it as well, into the ``cam_xform`` variable.
+
+Now we check for directional input. If we find that the player is moving, we get the ``camera``'s directional
+vector in the direction we are wanting to move towards and add (or subtract) it to ``dir``.
+
+Many have found directional vectors confusing, so lets take a second to explain how they work:
+
+_________
+
+World space can be defined as: The space in which all objects are placed in, relative to a constant origin point.
+Every object, no matter if it is 2D or 3D, has a position in world space.
+
+To put it another way: world space is the space in a universe where every object's position, rotation, and scale
+can be measured by a known, fixed point called the origin.
+
+In Godot, the origin is at position ``(0, 0, 0)`` with a rotation of ``(0, 0, 0)`` and a scale of ``(1, 1, 1)``.
+
+.. note:: When you open up the Godot editor and select a :ref:`Spatial <class_Spatial>` based node, a gizmo pops up.
+          Each of the arrows points using world space directions by default.
+
+If you want to move using the world space directional vectors, you'd do something like this:
+
+::
+
+    if Input.is_action_pressed("movement_forward"):
+        node.translate(Vector3(0, 0, 1))
+    if Input.is_action_pressed("movement_backward"):
+        node.translate(Vector3(0, 0, -1))
+    if Input.is_action_pressed("movement_left"):
+        node.translate(Vector3(1, 0, 0))
+    if Input.is_action_pressed("movement_right"):
+        node.translate(Vector3(-1, 0, 0))
+
+.. note:: Notice how we do not need to do any calculations to get world space directional vectors.
+          We can just define a few :ref:`Vector3 <class_Vector3>` variables and input the values pointing in each direction.
+
+Here is what world space looks like in 2D:
+
+.. note:: The following images are just examples. Each arrow/rectangle represents a directional vector
+
+.. image:: img/WorldSpaceExample.png
+
+And here is what it looks like for 3D:
+
+.. image:: img/WorldSpaceExample_3D.png
+
+Notice how in both examples, the rotation of the node does not change the directional arrows.
+This is because world space is a constant. No matter how you translate, rotate, or scale an object, world
+space will *always point in the same direction*.
+
+Local space is different, because it takes the rotation of the object into account.
+
+Local space can be defined as follows:
+The space in which a object's position is the origin of the universe. Because the position
+of the origin can be at ``N`` many locations, the values derived from local space change
+with the position of the origin.
+
+.. note:: This stack overflow question has a much better explanation of world space and local space.
+
+          https://gamedev.stackexchange.com/questions/65783/what-are-world-space-and-eye-space-in-game-development
+          (Local space and eye space are essentially the same thing in this context)
+
+To get a :ref:`Spatial <class_Spatial>` node's local space, we need to get its :ref:`Transform <class_Transform>`, so then we
+can get the :ref:`Basis <class_Basis>` from the :ref:`Transform <class_Transform>`.
+
+Each :ref:`Basis <class_Basis>` has three vectors: ``X``, ``Y``, and ``Z``.
+Each of those vectors point towards each of the local space vectors coming from that object.
+
+To use the a :ref:`Spatial <class_Spatial>` node's local directional vectors, we use this code:
+
+::
+
+    if Input.is_action_pressed("movement_forward"):
+        node.translate(node.global_transform.basis.z.normalized())
+    if Input.is_action_pressed("movement_backward"):
+        node.translate(-node.global_transform.basis.z.normalized())
+    if Input.is_action_pressed("movement_left"):
+        node.translate(node.global_transform.basis.x.normalized())
+    if Input.is_action_pressed("movement_right"):
+        node.translate(-node.global_transform.basis.x.normalized())
+
+Here is what local space looks like in 2D:
+
+.. image:: img/LocalSpaceExample.png
+
+And here is what it looks like for 3D:
+
+.. image:: img/LocalSpaceExample_3D.png
+
+Here is what the :ref:`Spatial <class_Spatial>` gizmo shows when you are using local space mode.
+Notice how the arrows follow the rotation of the object on the left, which looks exactly
+the same as the 3D example for local space.
+
+.. note:: You can change between local and world space modes by pressing the little cube button
+          when you have a :ref:`Spatial <class_Spatial>` based node selected.
+
+.. image:: img/LocalSpaceExampleGizmo.png
+
+Local vectors are confusing even for more experienced game developers, so do not worry if this all doesn't make a
+lot of sense. The key thing to remember about local vectors is that we are using local coordinates to get direction
+from the object's point of view, as opposed to using world vectors which give direction from the world's point of view.
+
+_________
+
+Back to ``_physics_process``:
+
+When the player pressed any of the directional movement actions, we get the local vector pointing in that direction
+and add it to ``dir``.
+
+.. note:: Because the camera is rotated by ``-180`` degrees, we have to flip the directional vectors.
+          Normally forward would be the positive Z axis, so using ``basis.z.normalized()`` would work,
+          but we are using ``-basis.z.normalized()`` because our camera's Z axis faces backwards in relation
+          to the rest of the player.
+
+Next we check if the player is on the floor using :ref:`KinematicBody <class_KinematicBody>`'s ``is_on_floor`` function. If it is, then we
+check to see if the "movement_jump" action has just been pressed. If it has, then we set our ``Y`` velocity to
+``JUMP_SPEED``.
+
+Next we check if the flash light action was just pressed. If it was, we then check if the flash light
+is visible, or hidden. If it is visible, we hide it. If it is hidden, we make it visible.
+
+Next we assure that our movement vector does not have any movement on the ``Y`` axis, and then we normalize it.
+We set a variable to our normal gravity and apply that gravity to our velocity.
+
+.. note:: If you are wondering why we are seemingly needlessly assigning a new variable
+          for gravity, it is because later we will be changing gravity.
+
+After that we assign our velocity to a new variable (called ``hvel``) and remove any movement on the ``Y`` axis.
+Next we set a new variable (``target``) to our direction vector. Then we multiply that by our max speed
+so we know how far we will can move in the direction provided by ``dir``.
+
+After that we make a new variable for our acceleration, named ``accel``. We then take the dot product
+of ``hvel`` to see if we are moving according to ``hvel``. Remember, ``hvel`` does not have any
+``Y`` velocity, meaning we are only checking if we are moving forwards, backwards, left, or right.
+If we are moving, then we set ``accel`` to our ``ACCEL`` constant so we accelerate, otherwise we set ``accel` to
+our ``DEACCEL`` constant so we decelerate.
+
+Finally, we interpolate our horizontal velocity, set our ``X`` and ``Z`` velocity to the interpolated horizontal velocity,
+and then call ``move_and_slide`` to let the :ref:`KinematicBody <class_KinematicBody>` handle moving through the physics world.
+
+.. tip:: All of the code in ``_physics_process`` is almost exactly the same as the movement code from the Kinematic Character demo!
+         The only thing that is different is how we use the directional vectors, and the flash light!
+
+You can optionally add some code to capture and free the mouse cursor when "ui_cancel" is
+pressed. While entirely optional, it is highly recommended for debugging purposes.
+
+_________
+
+The final function we have is the ``_input`` function, and thankfully it's fairly short:
+
+First we make sure that the event we are dealing with is a :ref:`InputEventMouseMotion <class_InputEventMouseMotion>` event.
+We also want to check if the cursor is captured, as we do not want to rotate if it is not.
+
+.. tip:: See :ref:`Mouse and input coordinates <doc_mouse_and_input_coordinates>` for a list of
+         possible input events.
+
+If the event is indeed a mouse motion event and the cursor is captured, we rotate
+based on the mouse motion provided by :ref:`InputEventMouseMotion <class_InputEventMouseMotion>`.
+
+First we rotate the ``rotation_helper`` node on the ``X`` axis, using the relative mouse motion's
+``Y`` value, provided by :ref:`InputEventMouseMotion <class_InputEventMouseMotion>`.
+
+Then we rotate the entire :ref:`KinematicBody <class_KinematicBody>` on the ``Y`` axis by the relative mouse motion's ``X`` value.
+
+.. tip:: Godot converts relative mouse motion into a :ref:`Vector2 <class_Vector2>` where mouse movement going
+         up and down is ``1`` and ``-1`` respectively. Right and Left movement is
+         ``1`` and ``-1`` respectively.
+
+         Because of how we are rotating the player, we multiply the relative mouse motion's
+         ``X`` value by ``-1`` so mouse motion going left and right rotates the player left and right
+         in the same direction.
+
+Finally, we clamp the ``rotation_helper``'s ``X`` rotation to be between ``-70`` and ``70``
+degrees so we cannot rotate ourselves upside down.
+
+_________
+
+To test the code open up the scene named ``Testing_Area.tscn`` if it's not already opened up. We will be using
+this scene as we go through the tutorial, so be sure to keep it open in one of your scene tabs.
+
+Go ahead and test your code either by pressing ``F4`` with ``Testing_Area.tscn`` as the open tab, by pressing the
+play button in the top right corner, or by pressing ``F6``.
+You should now be able to walk around, jump in the air, and look around using the mouse.
+
+Giving the player the option to sprint
+--------------------------------------
+
+Before we get to making the weapons work, there is one more thing we should add.
+Many FPS games have an option to sprint, and we can easily add sprinting to our player,
+so let's do that!
+
+First we need a few more global variables in our player script:
+
+::
+
+    const sprint_grav = -30.8
+    const MAX_SPRINT_SPEED = 30
+    const SPRINT_ACCEL = 18
+    const SPRINT_JUMP_SPEED = 24
+    var is_sprinting = false
+
+All of these variables work exactly the same as the non sprinting variables with
+similar names. The only that's different is ``is_sprinting``, which is a boolean to track
+whether the player is currently sprinting.
+
+Now we just need to change some of the code in our ``_physics_process`` function
+so we can add the ability to sprint.
+
+First, we want to change gravity when we are sprinting. The reason behind this is
+we want the player to feel a little more weighty when sprinting. If we were not also
+increasing the max speed and jump height, we wouldn't need to change gravity, but since
+we are, we need to make a few changes.
+
+The first change is we need to replace ``var grav = norm_grav`` with the code below:
+
+::
+
+    # Was "var grav = norm_grav"
+    var grav = 0
+    if Input.is_action_pressed("movement_sprint"):
+        is_sprinting = true
+        grav = sprint_grav
+    else:
+        is_sprinting = false;
+        grav = norm_grav
+
+
+Next we need to set our max speed to the higher speed if we are sprinting, and we also need
+to change our acceleration to the new acceleration:
+
+::
+
+    var target = dir
+    # NEW CPDE. Replaces "target *= MAX_SPEED"
+    if is_sprinting:
+        target *= MAX_SPRINT_SPEED
+    else:
+        target *= MAX_SPEED
+
+    # Same code as before:
+    var accel
+    if dir.dot(hvel) > 0:
+        # New code. Replaces "accel = ACCEL"
+        if is_sprinting:
+            accel = SPRINT_ACCEL
+        else:
+            accel = ACCEL
+    else:
+        accel = DEACCEL
+
+
+Finally, we need to increase the jump height when we are sprinting:
+
+::
+
+    # Same code as before
+    if is_on_floor():
+        if Input.is_action_just_pressed("movement_jump"):
+            # NEW CODE. replaces "vel.y = JUMP_SPEED"
+            if is_sprinting:
+                vel.y = SPRINT_JUMP_SPEED
+            else:
+                vel.y = JUMP_SPEED
+
+Now you should be able to sprint if you press the shift button! Go give it a
+whirl! You can change the sprint related global variables to make the player faster, to reduce
+gravity, and to accelerate faster.
+
+Phew! That was a lot of work. Now you have a fully working first person character!
+
+In :ref:`doc_fps_tutorial_part_two` we will add some guns to our player character.
+
+.. note:: At this point we've recreated the Kinematic character demo with sprinting!
+
+.. tip:: Currently the player script would be at an ideal state for making all sorts of
+         first person games. For example: Horror games, platformer games, adventure games, and more!
+
+.. warning:: If you ever get lost, be sure to read over the code again! You can also
+             download the finished project at the bottom of :ref:`doc_fps_tutorial_part_three`.

+ 705 - 0
tutorials/3d/fps_tutorial/part_three.rst

@@ -0,0 +1,705 @@
+.. _doc_fps_tutorial_part_three:
+
+Part 3
+======
+
+Part Overview
+-------------
+
+In this part we will be limiting our guns by giving them ammo. We will also
+be giving the player the ability to reload, and we will be adding sounds when the
+guns fire.
+
+.. image:: img/PartThreeFinished.png
+
+By the end of this part, the player will have limited ammo, the ability to reload,
+and sounds will play when the player fires and changes weapons.
+
+.. note:: You are assumed to have finished :ref:`doc_fps_tutorial_part_two` before moving on to this part of the tutorial.
+
+Let's get started!
+
+
+Changing levels
+---------------
+
+Now that we have a fully working FPS, let's move to a more FPS like level. Open up ``Test_Level.tscn``.
+``Test_Level.tscn`` is a complete custom FPS level created for the purpose of this tutorial. Press ``F6`` to
+play the open scene, or press the "play current scene button", and give it a whirl.
+
+You might have noticed there are several boxes and cylinders placed throughout the level. They are :ref:`RigidBody <class_RigidBody>`
+nodes we can place ``RigidBody_hit_test.gd`` on and then they will react to being hit with bullets, so lets do that!
+
+Select ``Center_room`` and open it up. From there select ``Physics_objects`` and open that up. You'll find there are
+``6`` crates in a seemingly random order. Go select one of them and press the "Open in Editor" button. It's the one that
+looks like a little movie slide.
+
+.. note:: The reason the objects seem to be placed in a random order is because all of the objects were copied and pasted around
+          in the Godot editor to save on time. If you want to move any of the nodes around, it is highly suggested to just
+          left click inside the editor viewport to get the node you want, and then move it around with the :ref:`Spatial <class_Spatial>` gizmo.
+
+This will bring you to the crate's scene. From there, select the ``Crate`` :ref:`RigidBody <class_RigidBody>` (the one that is the root of the scene)
+and scroll down in the inspector until you get to the script section. From there, click the drop down and select "Load". Chose
+``RigidBody_hit_test.gd`` and then return to ``Test_Level.tscn``.
+
+Now open ``Upper_room``, select ``Physics_objects``, and chose one of the cylinder :ref:`RigidBody <class_RigidBody>` nodes.
+Press the "Open in Editor" button beside one of the cylinders. This will bring you to the cylinder's scene.
+
+From there, select the ``Cylinder`` :ref:`RigidBody <class_RigidBody>` (the one that is the root of the scene)
+and scroll down in the inspector until you get to the script section. From there, click the drop down and select "Load". Chose
+``RigidBody_hit_test.gd`` and then return to ``Test_Level.tscn``.
+
+Now you can fire at the boxes and cylinders and they will react to your bullets just like the cubes in ``Testing_Area.tscn``!
+
+
+Adding ammo
+-----------
+
+Now that we've got working guns, lets give them a limited amount of ammo.
+
+Lets define some more global variables in ``Player.gd``, ideally nearby the other gun related variables:
+
+::
+
+    var ammo_for_guns = {"PISTOL":60, "RIFLE":160, "KNIFE":1}
+    var ammo_in_guns = {"PISTOL":20, "RIFLE":80, "KNIFE":1}
+    const AMMO_IN_MAGS = {"PISTOL":20, "RIFLE":80, "KNIFE":1}
+
+
+Here is what these variables will be doing for us:
+
+- ``ammo_for_guns``: The amount of ammo we have in reserve for each weapon/gun.
+- ``ammo_in_guns``: The amount of ammo currently inside the weapon/gun.
+- ``AMMO_IN_MAGS``: How much ammo is in a fully filled weapon/gun.
+
+.. note:: There is no reason we've included ammo for the knife, so feel free to remove the knife's ammo
+          if you desire.
+
+          Depending on how you program melee weapons, you may need to define an ammo count even if the
+          weapon does not use ammo. Some games use extremely short range 'guns' as their melee weapons,
+          and in those cases you may need to define ammo for your melee weapons.
+
+_________
+
+Now we need to add a few ``if`` checks to ``_physics_process``.
+
+We need to make sure we have ammo in our gun before we try to fire a bullet.
+Go find the line that checks for the fire action being pressed and add the following new
+bits of code:
+
+::
+
+    # NOTE: You should have this if condition in your _physics_process function
+    # Firing the weapons
+    if Input.is_action_pressed("fire"):
+        if current_gun == "PISTOL":
+            if ammo_in_guns["PISTOL"] > 0: # NEW CODE
+                if animation_manager.current_state == "Pistol_idle":
+                    animation_manager.set_animation("Pistol_fire")
+
+        elif current_gun == "RIFLE":
+            if ammo_in_guns["RIFLE"] > 0: # NEW CODE
+                if animation_manager.current_state == "Rifle_idle":
+                    animation_manager.set_animation("Rifle_fire")
+
+        elif current_gun == "KNIFE":
+            if animation_manager.current_state == "Knife_idle":
+                animation_manager.set_animation("Knife_fire")
+
+These two additional ``if`` checks make sure we have a bullet to fire before setting our firing animation.
+
+While we're still in ``_physics_process``, let's also add a way to track how much ammo we have. Find the line that
+has ``UI_status_label.text = "HEALTH: " + str(health)`` in ``_physics_process`` and replace it with the following:
+
+::
+
+    # HUD (UI)
+    if current_gun == "UNARMED" or current_gun == "KNIFE":
+        UI_status_label.text = "HEALTH: " + str(health)
+    else:
+        UI_status_label.text = "HEALTH: " + str(health) + "\nAMMO:" + \
+            str(ammo_in_guns[current_gun]) + "/" + str(ammo_for_guns[current_gun])
+
+.. tip:: Did you now that you can combine two lines using ``\``? We're using it here
+         so we do not have a extremely long line of code all on one line by splitting it
+         into two lines!
+
+This will show the player how much ammo they currently have and how much ammo they currently have in reserve, only for
+the appropriate weapons (not unarmed or the knife). Regardless of the currently selected weapon/gun, we will always show
+how much health the player has
+
+.. note:: we cannot just add ``ammo_for_guns[current_gun]`` or ``ammo_in_guns[current_gun]`` to the ``string`` we
+          are passing in to the :ref:`Label <class_Label>`. Instead we have to cast them from ``floats`` to ``strings``, which is what we are doing
+          by using ``str()``.
+
+          For more information on casting, see this page from wiki books:
+          https://en.wikibooks.org/wiki/Computer_Programming/Type_conversion
+
+.. warning:: We are currently not using the player's health just yet in the tutorial. We will start
+             using health for the player and objects when we include turrets and targets in later parts.
+
+
+Now we need to remove a bullet from the gun when we fire. To do that, we just need to add a few lines in
+``fire_bullet``:
+
+::
+
+    func fire_bullet():
+        if changing_gun == true:
+            return
+
+        # Pistol bullet handling: Spawn a bullet object!
+        if current_gun == "PISTOL":
+            var clone = bullet_scene.instance()
+            var scene_root = get_tree().root.get_children()[0]
+            scene_root.add_child(clone)
+
+            clone.global_transform = get_node("Rotation_helper/Gun_fire_points/Pistol_point").global_transform
+            # The bullet is a little too small (by default), so let's make it bigger!
+            clone.scale = Vector3(4, 4, 4)
+
+            ammo_in_guns["PISTOL"] -= 1 # NEW CODE
+
+        # Rifle bullet handeling: Send a raycast!
+        elif current_gun == "RIFLE":
+                var ray = get_node("Rotation_helper/Gun_fire_points/Rifle_point/RayCast")
+                ray.force_raycast_update()
+
+                if ray.is_colliding():
+                    var body = ray.get_collider()
+                    if body.has_method("bullet_hit"):
+                        body.bullet_hit(RIFLE_DAMAGE, ray.get_collision_point())
+
+                ammo_in_guns["RIFLE"] -= 1 # NEW CODE
+
+        # Knife bullet(?) handeling: Use an area!
+        elif current_gun == "KNIFE":
+            var area = get_node("Rotation_helper/Gun_fire_points/Knife_point/Area")
+            var bodies = area.get_overlapping_bodies()
+
+            for body in bodies:
+                if body.has_method("bullet_hit"):
+                    body.bullet_hit(KNIFE_DAMAGE, area.global_transform.origin)
+
+
+Go play the project again! Now you'll lose ammo as you fire, until you reach zero and
+cannot fire anymore.
+
+Adding reloading
+----------------
+
+Now that we can empty our gun, we need a way to refill it!
+
+First, let's start by
+adding another global variable. Add ``var reloading_gun = false`` somewhere along with your
+other global variables, preferably near the other gun related variables.
+
+_________
+
+Now we need to add several things to ``_physics_process``.
+
+First, let's make sure we cannot change guns while reloading.
+We need to change the weapon changing code to include the following:
+
+::
+
+    # Was "if changing_gun == false"
+    if changing_gun == false and reloading_gun == false:
+        if Input.is_key_pressed(KEY_1):
+            current_gun = "UNARMED"
+            changing_gun = true
+        elif Input.is_key_pressed(KEY_2):
+            current_gun = "KNIFE"
+            changing_gun = true
+        elif Input.is_key_pressed(KEY_3):
+            current_gun = "PISTOL"
+            changing_gun = true
+        elif Input.is_key_pressed(KEY_4):
+            current_gun = "RIFLE"
+            changing_gun = true
+
+Now the player cannot change guns while reloading.
+
+_________
+
+Ideally we want the player to be able to reload when they chose, so lets given them
+the ability to reload when they press the ``reload`` action. Add the following
+somewhere in ``_physics_process``, ideally nearby your other input related code:
+
+::
+
+    # Reloading
+    if reloading_gun == false:
+        if Input.is_action_just_pressed("reload"):
+            if animation_manager.current_state != "Pistol_reload" and animation_manager.current_state != "Rifle_reload":
+                reloading_gun = true
+
+First we see if the player is already reloading. If they are not, then we check if they've pressed
+the reloading action. If they have pressed the ``reload`` action, we then make sure they are not already
+in a reloading animation. If they are not, we set ``reloading_gun`` to ``true``.
+
+We do not want to do our reloading processing here with the input in an effort to keep game logic
+separate from input logic. Keeping them separate makes the code easier to debug, and as a bonus it
+keeps the input logic from being overly bloated.
+
+_________
+
+Finally, we need to add the code that actually handles reloading. Add the following code to ``_physics_process``,
+ideally somewhere underneath the reloading input code you just inputted:
+
+::
+
+    # Reloading logic
+    if reloading_gun == true:
+        var can_reload = false
+
+        if current_gun == "PISTOL":
+            if animation_manager.current_state == "Pistol_idle":
+                can_reload = true
+        elif current_gun == "RIFLE":
+            if animation_manager.current_state == "Rifle_idle":
+                can_reload = true
+        elif current_gun == "KNIFE":
+            can_reload = false
+            reloading_gun = false
+        else:
+            can_reload = false
+            reloading_gun = false
+
+        if ammo_for_guns[current_gun] <= 0 or ammo_in_guns[current_gun] == AMMO_IN_MAGS[current_gun]:
+            can_reload = false
+            reloading_gun = false
+
+
+        if can_reload == true:
+            var ammo_needed = AMMO_IN_MAGS[current_gun] - ammo_in_guns[current_gun]
+
+            if ammo_for_guns[current_gun] >= ammo_needed:
+                ammo_for_guns[current_gun] -= ammo_needed
+                ammo_in_guns[current_gun] = AMMO_IN_MAGS[current_gun]
+            else:
+                ammo_in_guns[current_gun] += ammo_for_guns[current_gun]
+                ammo_for_guns[current_gun] = 0
+
+            if current_gun == "PISTOL":
+                animation_manager.set_animation("Pistol_reload")
+            elif current_gun == "RIFLE":
+                animation_manager.set_animation("Rifle_reload")
+
+            reloading_gun = false
+
+
+Lets go over what this code does.
+
+_________
+
+First we check if ``reloading_gun`` is ``true``. If it is we then go through a series of checks
+to see if we can reload or not. We use ``can_reload`` as a variable to track whether or not
+it is possible to reload.
+
+We go through series of checks for each weapon. For the pistol and the rifle we check if
+we're in an idle state or not. If we are, then we set ``can_reload`` to ``true``.
+
+For the knife we do not want to reload, because you cannot reload a knife, so we set ``can_reload`` and ``reloading_gun``
+to ``false``. If we are using a weapon that we do not have a ``if`` or ``elif`` check for, we set
+``can_reload`` and ``reloading_gun`` to ``false``, as we do not want to be able to reload a weapon we are unaware of.
+
+Next we check if we have ammo in reserve for the gun in question. We also check to make sure the gun we are trying to reload
+is not already full of ammo. If the gun does not have ammo in reserve or the gun is already full, we set
+``can_reload`` and ``reloading_gun`` to ``false``.
+
+If we've made it through all those checks and we can reload, then we have a few more steps to take.
+
+First we assign the ammo we are needing to fill the gun fully to the ``ammo_needed`` variable.
+We just subtract the amount of ammo we currently have in our gun by the amount of ammo in a full magazine.
+
+Then we check if have enough ammo in reserves to fill the gun fully. If we do, we subtract the amount of ammo
+we need to refill our gun from the reserves, and we set the amount of ammo in the gun to full.
+
+If we do not have enough ammo in reserves to fill the gun, we add all of the ammo left in reserves to our
+gun and then set the ammo in reserves to zero, making it empty.
+
+Regardless of how much ammo we've added to the gun, we set our animation to the reloading animation for the current gun.
+
+Finally, we set ``reloading_gun`` to false because we have finished reloading the gun.
+
+_________
+
+Go test the project again, and you'll find you can reload your gun when it is not
+full and when there is ammo left in the ammo reserves.
+
+_________
+
+Personally, I like the guns to automatically start reloading if we try to fire them
+when they have no ammo in them, so lets add that! Add the following code to the input code for
+firing the guns:
+
+::
+
+    # Firing the weapons
+    if Input.is_action_pressed("fire"):
+        if current_gun == "PISTOL":
+            if ammo_in_guns["PISTOL"] > 0:
+                if animation_manager.current_state == "Pistol_idle":
+                    animation_manager.set_animation("Pistol_fire")
+            # NEW CODE!
+            else:
+                reloading_gun = true
+
+        elif current_gun == "RIFLE":
+            if ammo_in_guns["RIFLE"] > 0:
+                if animation_manager.current_state == "Rifle_idle":
+                    animation_manager.set_animation("Rifle_fire")
+            # NEW CODE!
+            else:
+                reloading_gun = true
+
+        elif current_gun == "KNIFE":
+            if animation_manager.current_state == "Knife_idle":
+                animation_manager.set_animation("Knife_fire")
+
+Now whenever the player tries to fire the gun when it's empty, we automatically
+set ``reloading_gun`` to true, which will reload the gun if possible.
+
+Adding sounds
+-------------
+
+Finally, let's add some sounds that play when we are reloading, changing guns, and when we
+are firing them.
+
+.. tip:: There are no game sounds provided in this tutorial (for legal reasons).
+         https://gamesounds.xyz/ is a collection of **"royalty free or public domain music and sounds suitable for games"**.
+         I used Gamemaster's Gun Sound Pack, which can be found in the Sonniss.com GDC 2017 Game Audio Bundle.
+
+         The video tutorial will briefly show how to edit the audio files for use in the tutorial.
+
+Open up ``SimpleAudioPlayer.tscn``. It is simply a :ref:`Spatial <class_Spatial>` with a :ref:'AudioStreamPlayer <class_AudioStreamPlayer>' as it's child.
+
+.. note:: The reason this is called a 'simple' audio player is because we are not taking performance into account
+          and because the code is designed to provide sound in the simplest way possible. This will likely change
+          in a future part.
+
+If you want to use 3D audio, so it sounds like it's coming from a location in 3D space, right click
+the :ref:'AudioStreamPlayer <class_AudioStreamPlayer>' and select "Change type".
+
+This will open the node browser. Navigate to :ref:'AudioStreamPlayer3D <class_AudioStreamPlayer3D>' and select "change".
+In the source for this tutorial, we will be using :ref:'AudioStreamPlayer <class_AudioStreamPlayer>', but you can optionally
+use :ref:'AudioStreamPlayer3D <class_AudioStreamPlayer3D>' if you desire, and the code provided below will work regardless of which
+one you chose.
+
+Create a new script and call it "SimpleAudioPlayer.gd". Attach it to the :ref:`Spatial <class_Spatial>` in ``SimpleAudioPlayer.tscn``
+and insert the following code:
+
+::
+
+    extends Spatial
+
+    # All of the audio files.
+    # You will need to provide your own sound files.
+    var audio_pistol_shot = preload("res://path_to_your_audio_here")
+    var audio_gun_cock = preload("res://path_to_your_audio_here")
+    var audio_rifle_shot = preload("res://path_to_your_audio_here")
+
+    var audio_node = null
+
+    func _ready():
+        audio_node = get_node("AudioStreamPlayer")
+        audio_node.connect("finished", self, "destroy_self")
+        audio_node.stop()
+
+
+    func play_sound(sound_name, position=null):
+        if sound_name == "Pistol_shot":
+            audio_node.stream = audio_pistol_shot
+        elif sound_name == "Rifle_shot":
+            audio_node.stream = audio_rifle_shot
+        elif sound_name == "Gun_cock":
+            audio_node.stream = audio_gun_cock
+        else:
+            print ("UNKNOWN STREAM")
+            queue_free()
+            return
+
+        # If you are using a AudioPlayer3D, then uncomment these lines to set the position.
+        # if position != null:
+        #	audio_node.global_transform.origin = position
+
+        audio_node.play()
+
+
+    func destroy_self():
+        audio_node.stop()
+        queue_free()
+
+
+.. tip:: By setting ``position`` to ``null`` by default in ``play_sound``, we are making it an optional argument,
+         meaning position doesn't necessarily have to be passed in to call the ``play_sound``.
+
+Let's go over what's happening here:
+
+_________
+
+In ``_ready`` we get the :ref:'AudioStreamPlayer <class_AudioStreamPlayer>' and connect it's ``finished`` signal to ourselves.
+It doesn't matter if it's :ref:'AudioStreamPlayer <class_AudioStreamPlayer>' or :ref:'AudioStreamPlayer3D <class_AudioStreamPlayer3D>' node,
+as they both have the finished signal. To make sure it is not playing any sounds, we call ``stop`` on the :ref:'AudioStreamPlayer <class_AudioStreamPlayer>'.
+
+.. warning:: Make sure your sound files are **not** set to loop! If it is set to loop
+             the sounds will continue to play infinitely and the script will not work!
+
+The ``play_sound`` function is what we will be calling from ``Player.gd``. We check if the sound
+is one of the three possible sounds, and if it is we set the audio stream for our :ref:'AudioStreamPlayer <class_AudioStreamPlayer>'
+to the correct sound.
+
+If it is an unknown sound, we print an error message to the console and free ourselves.
+
+If you are using a :ref:'AudioStreamPlayer3D <class_AudioStreamPlayer3D>', remove the ``#`` to set the position of
+the audio player node so it plays at the correct position.
+
+Finally, we tell the :ref:'AudioStreamPlayer <class_AudioStreamPlayer>' to play.
+
+When the :ref:'AudioStreamPlayer <class_AudioStreamPlayer>' is finished playing the sound, it will call ``destroy_self`` because
+we connected the ``finished`` signal in ``_ready``. We stop the :ref:'AudioStreamPlayer <class_AudioStreamPlayer>' and free ourself
+to save on resources.
+
+.. note:: This system is extremely simple and has some major flaws:
+          One flaw is we have to pass in a string value to play a sound. While it is relatively simple
+          to remember the names of the three sounds, it can be increasingly complex when you have more sounds.
+          Ideally we'd place these sounds in some sort of container with exposed variables so we do not have
+          to remember the name(s) of each sound effect we want to play.
+
+          Another flaw is we cannot play looping sounds effects, nor background music easily with this system.
+          Because we cannot play looping sounds, certain effects like footstep sounds are harder to accomplish
+          because we then have to keep track of whether or not there is a sound effect *and* whether or not we
+          need to continue playing it.
+
+_________
+
+With that done, lets open up ``Player.gd`` again.
+First we need to load the ``SimpleAudioPlayer.tscn``. Place the following code in your global variables:
+
+::
+
+    var simple_audio_player = preload("res://SimpleAudioPlayer.tscn")
+
+Now we just need to instance the simple audio player when we need it, and then call it's
+``play_sound`` function and pass the name of the sound we want to play. To make the process simpler,
+let's create a ``create_sound`` function:
+
+::
+
+    func create_sound(sound_name, position=null):
+        var audio_clone = simple_audio_player.instance()
+        var scene_root = get_tree().root.get_children()[0]
+        scene_root.add_child(audio_clone)
+        audio_clone.play_sound(sound_name, position)
+
+Lets walk through what this function does:
+
+_________
+
+The first line instances the ``simple_audio_player.tscn`` scene and assigns it to a variable,
+named ``audio_clone``.
+
+The second line gets the scene root, using one large assumption. We first get this node's :ref:`SceneTree <class_SceneTree>`,
+and then access the root node, which in this case is the :ref:`Viewport <class_Viewport>` this entire game is running under.
+Then we get the first child of the :ref:`Viewport <class_Viewport>`, which in our case happens to be the root node in
+``Test_Area.tscn`` or ``Test_Level.tscn``. We are making a huge assumption that the first child of the root
+is the root node that our player is under, which could not always be the case.
+
+If this doesn't make sense to you, don't worry too much about it. The second line of code only doesn't work
+reliably if you have multiple scenes loaded as childs to the root node at a time, which will rarely happen for most projects. This is really
+only potentially a issue depending on how you handle scene loading.
+
+The third line adds our newly created ``SimpleAudioPlayer`` scene to be a child of the scene root. This
+works exactly the same as when we are spawning bullets.
+
+Finally, we call the ``play_sound`` function and pass in the arguments we're given. This will call
+``SimpleAudioPlayer.gd``'s ``play_sound`` function with the passed in arguments.
+
+_________
+
+Now all that is left is playing the sounds when we want to. First, let's play the shooting sounds
+when a bullet is fired. Go to ``fire_bullet`` and add the following:
+
+::
+
+    func fire_bullet():
+        if changing_gun == true:
+            return
+
+        # Pistol bullet handling: Spawn a bullet object!
+        if current_gun == "PISTOL":
+            var clone = bullet_scene.instance()
+            var scene_root = get_tree().root.get_children()[0]
+            scene_root.add_child(clone)
+
+            clone.global_transform = get_node("Rotation_helper/Gun_fire_points/Pistol_point").global_transform
+            # The bullet is a little too small (by default), so let's make it bigger!
+            clone.scale = Vector3(4, 4, 4)
+
+            ammo_in_guns["PISTOL"] -= 1
+            create_sound("Pistol_shot", clone.global_transform.origin); # NEW CODE
+
+        # Rifle bullet handeling: Send a raycast!
+        elif current_gun == "RIFLE":
+            var ray = get_node("Rotation_helper/Gun_fire_points/Rifle_point/RayCast")
+            ray.force_raycast_update()
+
+            if ray.is_colliding():
+                var body = ray.get_collider()
+                if body.has_method("bullet_hit"):
+                    body.bullet_hit(RIFLE_DAMAGE, ray.get_collision_point())
+
+            ammo_in_guns["RIFLE"] -= 1
+            create_sound("Rifle_shot", ray.global_transform.origin); # NEW CODE
+
+        # Knife bullet(?) handeling: Use an area!
+        elif current_gun == "KNIFE":
+            var area = get_node("Rotation_helper/Gun_fire_points/Knife_point/Area")
+            var bodies = area.get_overlapping_bodies()
+
+            for body in bodies:
+                if body.has_method("bullet_hit"):
+                    body.bullet_hit(KNIFE_DAMAGE, area.global_transform.origin)
+
+Now we will play the shooting noise for both the pistol and the rifle when a bullet is created.
+
+.. note:: We are passing in the positions of the ends of the guns using the bullet object's
+          global :ref:`Transform <class_transform>` and the :ref:`Raycast <class_raycast>`'s global :ref:`Transform <class_transform>`.
+          If you are not using a :ref:`AudioStreamPlayer3D <class_AudioStreamPlayer3D>` node, you can optionally leave the positions out and only
+          pass in the name of the sound you want to play.
+
+Finally, lets play the sound of a gun being cocked when we reload and when we change weapons.
+Add the following to our reloading logic section of ``_physics_process``:
+
+::
+
+    # Reloading logic
+    if reloading_gun == true:
+        var can_reload = false
+
+        if current_gun == "PISTOL":
+            if animation_manager.current_state == "Pistol_idle":
+                can_reload = true
+        elif current_gun == "RIFLE":
+            if animation_manager.current_state == "Rifle_idle":
+                can_reload = true
+        elif current_gun == "KNIFE":
+            can_reload = false
+            reloading_gun = false
+        else:
+            can_reload = false
+            reloading_gun = false
+
+        if ammo_for_guns[current_gun] <= 0 or ammo_in_guns[current_gun] == AMMO_IN_MAGS[current_gun]:
+            can_reload = false
+            reloading_gun = false
+
+
+        if can_reload == true:
+            var ammo_needed = AMMO_IN_MAGS[current_gun] - ammo_in_guns[current_gun]
+
+            if ammo_for_guns[current_gun] >= ammo_needed:
+                ammo_for_guns[current_gun] -= ammo_needed
+                ammo_in_guns[current_gun] = AMMO_IN_MAGS[current_gun]
+            else:
+                ammo_in_guns[current_gun] += ammo_for_guns[current_gun]
+                ammo_for_guns[current_gun] = 0
+
+            if current_gun == "PISTOL":
+                animation_manager.set_animation("Pistol_reload")
+            elif current_gun == "RIFLE":
+                animation_manager.set_animation("Rifle_reload")
+
+            reloading_gun = false
+            create_sound("Gun_cock", camera.global_transform.origin) # NEW CODE
+
+And add this code to the changing weapons section of ``_physics_process``:
+
+::
+
+    if changing_gun == true:
+        if current_gun != "PISTOL":
+            if animation_manager.current_state == "Pistol_idle":
+                animation_manager.set_animation("Pistol_unequip")
+        if current_gun != "RIFLE":
+            if animation_manager.current_state == "Rifle_idle":
+                animation_manager.set_animation("Rifle_unequip")
+        if current_gun != "KNIFE":
+            if animation_manager.current_state == "Knife_idle":
+                animation_manager.set_animation("Knife_unequip")
+
+        if current_gun == "UNARMED":
+            if animation_manager.current_state == "Idle_unarmed":
+                changing_gun = false
+
+        elif current_gun == "KNIFE":
+            if animation_manager.current_state == "Knife_idle":
+                changing_gun = false
+            if animation_manager.current_state == "Idle_unarmed":
+                animation_manager.set_animation("Knife_equip")
+
+        elif current_gun == "PISTOL":
+            if animation_manager.current_state == "Pistol_idle":
+                changing_gun = false
+            if animation_manager.current_state == "Idle_unarmed":
+                animation_manager.set_animation("Pistol_equip")
+
+                create_sound("Gun_cock", camera.global_transform.origin) # NEW CODE
+
+        elif current_gun == "RIFLE":
+            if animation_manager.current_state == "Rifle_idle":
+                changing_gun = false
+            if animation_manager.current_state == "Idle_unarmed":
+                animation_manager.set_animation("Rifle_equip")
+
+                create_sound("Gun_cock", camera.global_transform.origin) # NEW CODE
+
+Now whatever sound you have assigned to "Gun_cock" will play when you reload and when you
+change to either the pistol or the rifle.
+
+
+Final notes
+-----------
+
+.. image:: img/FinishedTutorialPicture.png
+
+Now you have a fully working single player FPS!
+
+You can find the completed project here: :download:`Godot_FPS_Finished.zip <files/Godot_FPS_Finished.zip>`
+
+.. tip:: The finished project source is hosted on Github as well: https://github.com/TwistedTwigleg/Godot_FPS_Tutorial
+
+You can also download all of the ``.blend`` files used here: :download:`Godot_FPS_BlenderFiles.zip <files/Godot_FPS_BlenderFiles.zip>`
+
+.. note:: The finished project source files contain the same exact code, just written in a different order.
+          This is because the finished project source files are what the tutorial is based on.
+
+          The finished project code was written in the order that features were created, not necessarily
+          in a order that is ideal for learning.
+
+          Other than that, the source is exactly the same, just with helpful comments explaining what
+          each part does.
+
+The skybox is created by **StumpyStrust** and can be found at OpenGameArt.org. https://opengameart.org/content/space-skyboxes-0
+
+The font used is **Titillium-Regular**, and is licensed under the SIL Open Font License, Version 1.1.
+
+The skybox was convert to a 360 equirectangular image using this tool: https://www.360toolkit.co/convert-cubemap-to-spherical-equirectangular.html
+
+While no sounds are provided, you can find many game ready sounds at https://gamesounds.xyz/
+
+.. warning:: OpenGameArt.org, 360toolkit.co, the creator(s) of Titillium-Regular, and GameSounds.xyz are in no way involved in this tutorial.
+
+__________
+
+In future parts we will be adding the following:
+
+- Adding a spawning system
+- Adding grenades
+- Adding turrets and targets
+- Adding a sound manager
+- Adding ammo and health pickups
+- Refining and cleaning up the code
+
+.. warning:: All plans are subject to change without warning!

+ 1004 - 0
tutorials/3d/fps_tutorial/part_two.rst

@@ -0,0 +1,1004 @@
+.. _doc_fps_tutorial_part_two:
+
+Part 2
+======
+
+Part Overview
+-------------
+
+In this part we will be giving our player weapons to play with.
+
+.. image:: img/PartTwoFinished.png
+
+By the end of this part, you will have a player that can fire a pistol,
+rifle, and attack using a knife. The player will also now have animations with transitions,
+and the weapons can interact with objects in the environment.
+
+.. note:: You are assumed to have finished :ref:`doc_fps_tutorial_part_one` before moving on to this part of the tutorial.
+
+Let's get started!
+
+Making a system to handle animations
+------------------------------------
+
+First we need a way to handle changing animations. Open up ``Player.tscn`` and select the :ref:`AnimationPlayer <class_AnimationPlayer>`
+Node (``Player``->``Rotation_helper``->``Model``->``AnimationPlayer``).
+
+Create a new script called ``AnimationPlayer_Manager.gd`` and attach that to the :ref:`AnimationPlayer <class_AnimationPlayer>`.
+
+Add the following code to ``AnimationPlayer_Manager.gd``:
+
+::
+
+    # Structure -> Animation name :[Connecting Animation states]
+    var states = {
+    "Idle_unarmed":["Knife_equip", "Pistol_equip", "Rifle_equip", "Idle_unarmed"],
+
+    "Pistol_equip":["Pistol_idle"],
+    "Pistol_fire":["Pistol_idle"],
+    "Pistol_idle":["Pistol_fire", "Pistol_reload", "Pistol_unequip", "Pistol_idle"],
+    "Pistol_reload":["Pistol_idle"],
+    "Pistol_unequip":["Idle_unarmed"],
+
+    "Rifle_equip":["Rifle_idle"],
+    "Rifle_fire":["Rifle_idle"],
+    "Rifle_idle":["Rifle_fire", "Rifle_reload", "Rifle_unequip", "Rifle_idle"],
+    "Rifle_reload":["Rifle_idle"],
+    "Rifle_unequip":["Idle_unarmed"],
+
+    "Knife_equip":["Knife_idle"],
+    "Knife_fire":["Knife_idle"],
+    "Knife_idle":["Knife_fire", "Knife_unequip", "Knife_idle"],
+    "Knife_unequip":["Idle_unarmed"],
+    }
+
+    var animation_speeds = {
+    "Idle_unarmed":1,
+
+    "Pistol_equip":1.4,
+    "Pistol_fire":1.8,
+    "Pistol_idle":1,
+    "Pistol_reload":1,
+    "Pistol_unequip":1.4,
+
+    "Rifle_equip":2,
+    "Rifle_fire":6,
+    "Rifle_idle":1,
+    "Rifle_reload":1.45,
+    "Rifle_unequip":2,
+
+    "Knife_equip":1,
+    "Knife_fire":1.35,
+    "Knife_idle":1,
+    "Knife_unequip":1,
+    }
+
+    var current_state = null
+    var callback_function = null
+
+    func _ready():
+        set_animation("Idle_unarmed")
+        connect("animation_finished", self, "animation_ended")
+
+
+
+    func set_animation(animation_name):
+        if animation_name == current_state:
+            print ("AnimationPlayer_Manager.gd -- WARNING: animation is already ", animation_name)
+            return true
+
+        if has_animation(animation_name) == true:
+            if current_state != null:
+                var possible_animations = states[current_state]
+                if animation_name in possible_animations:
+                    current_state = animation_name
+                    play(animation_name, -1, animation_speeds[animation_name])
+                    return true
+                else:
+                    print ("AnimationPlayer_Manager.gd -- WARNING: Cannot change to ", animation_name, " from ", current_state)
+                    return false
+            else:
+                current_state = animation_name
+                play(animation_name, -1, animation_speeds[animation_name])
+                return true
+        return false
+
+
+    func animation_ended(anim_name):
+        # UNARMED transitions
+        if current_state == "Idle_unarmed":
+            pass
+        # KNIFE transitions
+        elif current_state == "Knife_equip":
+            set_animation("Knife_idle")
+        elif current_state == "Knife_idle":
+            pass
+        elif current_state == "Knife_fire":
+            set_animation("Knife_idle")
+        elif current_state == "Knife_unequip":
+            set_animation("Idle_unarmed")
+        # PISTOL transitions
+        elif current_state == "Pistol_equip":
+            set_animation("Pistol_idle")
+        elif current_state == "Pistol_idle":
+            pass
+        elif current_state == "Pistol_fire":
+            set_animation("Pistol_idle")
+        elif current_state == "Pistol_unequip":
+            set_animation("Idle_unarmed")
+        elif current_state == "Pistol_reload":
+            set_animation("Pistol_idle")
+        # RIFLE transitions
+        elif current_state == "Rifle_equip":
+            set_animation("Rifle_idle")
+        elif current_state == "Rifle_idle":
+            pass;
+        elif current_state == "Rifle_fire":
+            set_animation("Rifle_idle")
+        elif current_state == "Rifle_unequip":
+            set_animation("Idle_unarmed")
+        elif current_state == "Rifle_reload":
+            set_animation("Rifle_idle")
+
+    func animation_callback():
+        if callback_function == null:
+            print ("AnimationPlayer_Manager.gd -- WARNING: No callback function for the animation to call!")
+        else:
+            callback_function.call_func()
+
+Lets go over what this script is doing:
+
+_________
+
+Lets start with this script's global variables:
+
+- ``states``: A dictionary for holding our animation states. (Further explanation below)
+- ``animation_speeds``: A dictionary for holding all of the speeds we want to play our animations at.
+- ``current_state``: A variable for holding the name of the animation state we are currently in.
+- ``callback_function``: A variable for holding the callback function. (Further explanation below)
+
+If you are familiar with state machines, then you may have noticed that ``states`` is structured
+like a basic state machine. Here is roughly how ``states`` is set up:
+
+``states`` is a dictionary with the key being the name of the current state, and the value being
+an array holding all of the states we can transition to. For example, if we are in currently in
+state ``Idle_unarmed``, we can only transition to ``Knife_equip``, ``Pistol_equip``, ``Rifle_equip``, and
+``Idle_unarmed``.
+
+If we try to transition to a state that is not included in our possible transitions states,
+then we get a warning message and the animation does not change. We will also automatically
+transition from some states into others, as will be explained further below in ``animation_ended``
+
+.. note:: For the sake of keeping this tutorial simple we are not using a 'proper'
+          state machine. If you are interested to know more about state machines,
+          see the following articles:
+
+          - (Python example) https://dev.to/karn/building-a-simple-state-machine-in-python
+          - (C# example) https://www.codeproject.com/Articles/489136/UnderstandingplusandplusImplementingplusStateplusP
+          - (Wiki article) https://en.wikipedia.org/wiki/Finite-state_machine
+
+          In a future part of this tutorial series we may revise this script to include a proper state machine.
+
+``animation_speeds`` is how fast each animation will play. Some of the animations are a little slow
+and in an effort to make everything smooth, we need to play them at faster speeds than some
+of the others.
+
+-- note:: Notice that all of the firing animations are faster than their normal speed. Remember this for later!
+
+``current_state`` will hold the name of the animation state we are currently in.
+
+Finally, ``callback_function`` will be a :ref:`FuncRef <class_FuncRef>` passed in by our player for spawning bullets
+at the proper frame of animation. A :ref:`FuncRef <class_FuncRef>` allows us to pass in a function as an argument,
+effectively allowing us to call a function from another script, which is how we will use it later.
+
+_________
+
+Now lets look at ``_ready``. First we are setting our animation to ``Idle_unarmed``, using the ``set_animation`` function,
+so we for sure start in that animation. Next we connect the ``animation_finished`` signal to this script and assign
+it to call ``animation_ended``.
+
+_________
+
+Lets look at ``set_animation`` next.
+
+``set_animation`` sets the animation to the that of the passed in
+animation state *if* we can transition to it. In other words, if the animation state we are currently in
+has the passed in animation state name in ``states``, then we will change to that animation.
+
+First we check if the passed in animation is the same as the animation state we are currently in.
+If it is, then we write a warning to the console and return ``true``.
+
+Next we see if :ref:`AnimationPlayer <class_AnimationPlayer>` has the passed in animation using ``has_animation``. If it does not, we return ``false``.
+
+Then we check if ``current_state`` is set or not. If ``current_state`` is *not* currently set, we
+set ``current_state`` to the passed in animation and tell :ref:`AnimationPlayer <class_AnimationPlayer>` to start playing the animation with
+a blend time of ``-1`` and at the speed set in ``animation_speeds`` and then we return ``true``.
+
+If we have a state in ``current_state``, then we get all of the possible states we can transition to.
+If the animation name is in the array of possible transitions, then we set ``current_state`` to the passed
+in animation, tell :ref:`AnimationPlayer <class_AnimationPlayer>` to play the animation with a blend time of ``-1`` at the speed set in ``animation_speeds``
+and then we return ``true``.
+
+_________
+
+Now lets look at ``animation_ended``.
+
+``animation_ended`` is the function that will be called by :ref:`AnimationPlayer <class_AnimationPlayer>` when it's done playing a animation.
+
+
+For certain animation states, we may need to transition into another state when its finished. To handle this, we
+check for every possible animation state. If we need to, we transition into another state.
+
+.. warning:: If you are using your own animated models, make sure that none of the animations are set
+             to loop. Looping animations do not send the ``animation_finished`` signal when they reach
+             the end of the animation and are about to loop.
+
+.. note:: the transitions in ``animation_ended`` ideally would be part of the data in ``states``, but in
+          an effort to make the tutorial easier to understand, we'll just hard code each state transition
+          in ``animation_ended``.
+
+_________
+
+Finally we have ``animation_callback``. This function will be called by a function track in our animations.
+If we have a :ref:`FuncRef <class_FuncRef>` assigned to ``callback_function``, then we call that passed in function. If we do not
+have a :ref:`FuncRef <class_FuncRef>` assigned to ``callback_function``, we print out a warning to the console.
+
+.. tip:: Try running ``Testing_Area.tscn`` just to make sure there is no runtime issues. If the game runs but nothing
+         seems to have changed, then everything is working correctly.
+
+Getting the animations ready
+----------------------------
+
+Now that we have a working animation manager, we need to call it from our player script.
+Before that though, we need to set some animation callback tracks in our firing animations.
+
+Open up ``Player.tscn`` if you don't have it open and navigate to the :ref:`AnimationPlayer <class_AnimationPlayer>` node
+(``Player``->``Rotation_helper``->``Model``->``AnimationPlayer``).
+
+We need to attach a function track to three of our animations: The firing animation for the pistol, rifle, and knife.
+Let's start with the pistol. Click the animation drop down list and select "Pistol_fire".
+
+Now scroll down to the very bottom of the list of animation tracks. The final item in the list should read
+``Armature/Skeleton:Left_UpperPointer``. Now at the bottom of the list, click the plus icon on the bottom
+bar of animation window, right plus right next to the loop button and the up arrow.
+
+.. image:: img/AnimationPlayerAddTrack.png
+
+This will bring up a window with three choices. We're wanting to add a function callback track, so click the
+option that reads "Add Call Func Track". This will open a window showing the entire node tree. Navigate to the
+:ref:`AnimationPlayer <class_AnimationPlayer>` node, select it, and press OK.
+
+.. image:: img/AnimationPlayerCallFuncTrack.png
+
+Now at the bottom of list of animation tracks you will have a green track that reads "AnimationPlayer".
+Now we need to add the point where we want to call our callback function. Scrub the timeline until you
+reach the point where the muzzle just starts to flash.
+
+.. note:: The timeline is the window where all of the points in our animation are stored. Each of the little
+          points represents a point of animation data.
+
+          Scrubbing the timeline means moving ourselves through the animation. So when we say "scrub the timeline
+          until you reach a point", what we mean is move through the animation window until you reach the a point
+          on the timeline.
+
+          Also, the muzzle of a gun is the end point where the bullet comes out. The muzzle flash is the flash of
+          light that escapes the muzzle when a bullet is fired. The muzzle is also sometimes referred to as the
+          barrel of the gun.
+
+.. tip:: For finer control when scrubbing the timeline, press ``control`` and scroll forwards with the mouse wheel to zoom in.
+         Scrolling backwards will zoom out.
+
+         You can also change how the timeline scrubbing snaps by changing the value in ``Step (s)`` to a lower/higher value.
+
+Once you get to a point you like, press the little green plus symbol on the far right side of the
+``AnimationPlayer`` track. This will place a little green point at the position you are currently
+at in the animation on your ``AnimationPlayer`` track.
+
+.. image:: img/AnimationPlayerAddPoint.png
+
+Now we have one more step before we are done with the pistol. Select the "enable editing of individual keys"
+button on the far right corner of the animation window. It looks like a pencil with a little point beside it.
+
+.. image:: img/AnimationPlayerEditPoints.png
+
+Once you've click that, a new window will open on the right side. Now click the green point on the ``AnimationPlayer``
+track. This will bring up the information associated with that point in the timeline. In the empty name field, enter
+"animation_callback" and press ``enter``.
+
+Now when we are playing this animation the callback function will be triggered at that specific point of the animation.
+
+.. warning:: Be sure to press the "enable editing of individual keys" button again to turn off the ability to edit individual keys
+              so you cannot change one of the transform tracks by accident!
+
+_________
+
+Let's repeat the process for the rifle and knife firing animations!
+
+.. note:: Because the process is exactly the same as the pistol, the process is going to explained in a little less depth.
+          Follow the steps in the above if you get lost! It is exactly the same, just on a different animation.
+
+Go to the "Rifle_fire" animation from the animation drop down. Add the function callback track once you reach the bottom of the
+animation track list by clicking the little plus icon at the bottom of the screen. Find the point where the muzzle just starts
+to flash and click the little green plus symbol to add a function callback point at that position on the track.
+
+Next, click the "enable editing of individual keys" button, the button with a plus at the bottom right side of the animation window.
+Select the newly created function callback point, put "animation_callback" into the name field and press ``enter``.
+Click the "enable editing of individual keys" button again to turn off individual key editing.
+so we cannot change one of the transform tracks by accident.
+
+Now we just need to apply the callback function track to the knife animation. Select the "Knife_fire" animation and scroll to the bottom of the
+animation tracks. Click the plus symbol at the bottom of the animation window and add a function callback track.
+Next find a point around the first third of the animation to place the animation callback function point at.
+
+.. note:: We will not actually be firing the knife, and the animation really is a stabbing animation rather than a firing one.
+         For this tutorial we are just reusing the gun firing logic for our knife, so the animation has been named in a style that
+         is consistent with the other animations.
+
+From there click the little green plus to add a function callback point at the current position. Then click the "enable editing of individual keys"
+button, the button with a plus at the bottom right side of the animation window.
+Select the newly created function callback point, put "animation_callback" into the name field and press ``enter``.
+Click the "enable editing of individual keys" button again to turn off individual key editing.
+so we cannot change one of the transform tracks by accident.
+
+.. tip:: Be sure to save your work!
+
+With that done, we are almost ready to start adding the ability to fire to our player script! We just need to setup one last scene:
+The scene for our bullet object.
+
+Creating the bullet scene
+-------------------------
+
+There are several ways to handle a gun's bullets in video games. In this tutorial series,
+we will be exploring two of the more common ways: Objects, and raycasts.
+
+_________
+
+One of the two ways is using a bullet object. This will be a object that travels through the world and handles
+its own collision code. This method we create/spawn a bullet object in the direction our gun is facing, and then
+it sends itself forward.
+
+There are several advantages to this method. The first being we do not have to store the bullets in our player. We can simply create the bullet
+and then move on, and the bullet itself with handle checking for collisions, sending the proper signal(s) to the object it collides with, and destroying itself.
+
+Another advantage is we can have more complex bullet movement. If we want to make the bullet fall ever so slightly as time goes on, we can make the bullet
+controlling script slowly push the bullet towards the ground. Using a object also makes the bullet take time to reach its target, it doesn't just instantly
+hit whatever its pointed at. This feels more realistic because nothing in real life really moves instantly from one point to another.
+
+One of the huge disadvantages performance. While having each bullet calculate their own paths and handle their own collision allows for a lot of flexibility,
+it comes at the cost of performance. With this method we are calculating every bullet's movement every step, and while this may not be a problem for a few dozen
+bullets, it can become a huge problem when you potentially have several hundred bullets.
+
+Despite the performance hit, many first person shooters include some form of object bullets. Rocket launchers are a prime example because in many
+first person shooters, Rockets do not just instantly explode at their target position. You can also find bullets as object many times with grenades
+because they generally bounce around the world before exploding.
+
+.. note:: While I cannot say for sure this is the case, these games *probably* use bullet objects in some form or another:
+          (These are entirely from my observations. **They may be entirely wrong**. I have never worked on **any** of the following games)
+
+          - Halo (Rocket launchers, fragment grenades, sniper rifles, brute shot, and more)
+          - Destiny (Rocket launchers, grenades, fusion rifles, sniper rifles, super moves, and more)
+          - Call of Duty (Rocket launchers, grenades, ballistic knifes, crossbows, and more)
+          - Battlefield (Rocket launchers, grenades, claymores, mortars, and more)
+
+Another disadvantage with bullet objects is networking. Bullet objects have to sync the positions (at least) with however many clients are connected
+to the server.
+
+While we are not implementing any form of networking (as that would be it's own entire tutorial series), it is a consideration
+to keep in mind when creating your first person shooter, especially if you plan on adding some form of networking in the future.
+
+_________
+
+The other way of handling bullet collisions we will be looking at, is raycasting.
+
+This method is extremely common in guns that have fast moving bullets that rarely change trajectory change over time.
+
+Instead of creating a bullet object and sending it through space, we instead send a ray starting from the barrel/muzzle of the gun forwards.
+We set the raycast's origin to the starting position of the bullet, and based on the length we can adjust how far the bullet 'travels' through space.
+
+.. note:: While I cannot say for sure this is the case, these games *probably* use raycasts in some form or another:
+          (These are entirely from my observations. **They may be entirely wrong**. I have never worked on **any** of the following games)
+
+          - Halo (Assault rifles, DMRs, battle rifles, covenant carbine, spartan laser, and more)
+          - Destiny (Auto rifles, pulse rifles, scout rifles, hand cannons, machine guns, and more)
+          - Call of Duty (Assault rifles, light machine guns, sub machine guns, pistols, and more)
+          - Battlefield (Assault rifles, SMGs, carbines, pistols, and more)
+
+One huge advantage for this method is it's really light on performance.
+Sending a couple hundred rays through space is *way* easier for the computer to calculate than sending a couple hundred
+bullet objects.
+
+Another advantage is we can instantly know if we've hit something or not exactly when we call for it. For networking this is important because we do not need
+to sync the bullet movements over the Internet, we just need to send whether or not the raycast hit.
+
+Raycasting does have some disadvantages though. One major disadvantage is we cannot easily cast a ray in anything but a linear line.
+This means we can only fire in a straight line for however long our ray length is. You can create the illusion of bullet movement by casting
+multiple rays at different positions, but not only is this hard to implement in code, it is also is heavier on performance.
+
+Another disadvantage is we cannot see the bullet. With bullet objects we can actually see the bullet travel through space if we attach a mesh
+to it, but because raycasts happen instantly, we do not really have a decent way of showing the bullets. You could draw a line from the origin of the
+raycast to the point where the raycast collided, and that is one popular way of showing raycasts. Another way is simply not drawing the raycast
+at all, because theoretically the bullets move so fast our eyes could not see it anyway.
+
+_________
+
+Lets get the bullet object setup. This is what our pistol will create when the "Pistol_fire" animation callback function is called.
+
+Open up ``Bullet_Scene.tscn``. The scene contains :ref:`Spatial <class_Spatial>` node called bullet, with a :ref:`MeshInstance <class_MeshInstance>`
+and an :ref:`Area <class_Area>` with a :ref:`CollisionShape <class_CollisionShape>` childed to it.
+
+Create a new script called ``Bullet_script.gd`` and attach it to the ``Bullet`` :ref:`Spatial <class_Spatial>`.
+
+We are going to move the entire bullet object at the root (``Bullet``). We will be using the :ref:`Area <class_Area>` to check whether or not we've collided with something
+
+.. note:: Why are we using a :ref:`Area <class_Area>` and not a :ref:`RigidBody <class_RigidBody>`? The mean reason we're not using a :ref:`RigidBody <class_RigidBody>`
+          is because we do not want the bullet to interact with other :ref:`RigidBody <class_RigidBody>` nodes.
+          By using an :ref:`Area <class_Area>` we are assuring that none of the other :ref:`RigidBody <class_RigidBody>` nodes, including other bullets, will be effected.
+
+          Another reason is simply because it is easier to detect collisions with a :ref:`Area <class_Area>`!
+
+Here's the script that will control our bullet:
+
+::
+
+    extends Spatial
+
+    const BULLET_SPEED = 80
+    const BULLET_DAMAGE = 15
+
+    const KILL_TIMER = 4
+    var timer = 0
+
+    var hit_something = false
+
+    func _ready():
+        get_node("Area").connect("body_entered", self, "collided")
+        set_physics_process(true)
+
+
+    func _physics_process(delta):
+        var forward_dir = global_transform.basis.z.normalized()
+        global_translate(forward_dir * BULLET_SPEED * delta)
+
+        timer += delta;
+        if timer >= KILL_TIMER:
+            queue_free()
+
+
+    func collided(body):
+        if hit_something == false:
+            if body.has_method("bullet_hit"):
+                body.bullet_hit(BULLET_DAMAGE, self.global_transform.origin)
+
+        hit_something = true
+        queue_free()
+
+
+Lets go through the script:
+
+_________
+
+First we define a few global variables:
+
+- ``BULLET_SPEED``: The speed the bullet travels at.
+- ``BULLET_DAMAGE``: The damage the bullet will cause to whatever it collides with.
+- ``KILL_TIMER``: How long the bullet can last without hitting anything.
+- ``timer``: A float for tracking how long we've been alive.
+- ``hit_something``: A boolean for tracking whether or not we've hit something.
+
+With the exception of ``timer`` and ``hit_something``, all of these variables
+change how the bullet interacts with the world.
+
+.. note:: The reason we are using a kill timer is so we do not have a case where we
+          get a bullet traveling forever. By using a kill timer, we can assure that
+          no bullets will just travel forever and consume resources.
+
+_________
+
+In ``_ready`` we set the area's ``body_entered`` signal to ourself so that it calls
+the ``collided`` function. Then we set ``_physics_process`` to ``true``.
+
+_________
+
+``_physics_process`` gets the bullet's local ``Z`` axis. If you look in at the scene
+in local mode, you will find that the bullet faces the positive local ``Z`` axis.
+
+Next we translate the entire bullet by that forward direction, multiplying in our speed and delta time.
+
+After that we add delta time to our timer and check if the timer has as long or longer
+than our ``KILL_TIME`` constant. If it has, we use ``queue_free`` to free ourselves.
+
+_________
+
+In ``collided`` we check if we've hit something yet or not.
+
+Remember that ``collided`` is
+only called when a body has entered the :ref:`Area <class_Area>` node. If we have not already collided with
+something, we the proceed to check if the body we've collided with has a function/method
+called ``bullet_hit``. If it does, we call it and pass in our damage and our position.
+
+.. note:: in ``collided``, the passed in body can be a :ref:`StaticBody <class_StaticBody>`,
+          :ref:`RigidBody <class_RigidBody>`, or :ref:`KinematicBody <class_KinematicBody>`
+
+Then we set ``hit_something`` to ``true`` because regardless of whether or not the body
+the bullet collided with has the ``bullet_hit`` function/method, it have hit something.
+
+Then we free the bullet using ``queue_free``.
+
+.. tip:: You may be wondering why we even have a ``hit_something`` variable if we
+         free the bullet using ``queue_free`` as soon as it hits something.
+
+         The reason we need to track whether we've hit something or not is because ``queue_free``
+         does not immediately free the node, so the bullet could collide with another body
+         before Godot has a chance to free it. By tracking if the bullet has hit something
+         we can make sure that the bullet will only hit one object.
+
+
+_________
+
+Before we start programming the player again, let's take a quick look at ``Player.tscn``.
+Open up ``Player.tscn`` again.
+
+Expand ``Rotation_helper`` and notice how it has two nodes: ``Gun_fire_points`` and
+``Gun_aim_point``.
+
+``Gun_aim_point`` is the point that the bullets will be aiming at. Notice how it
+is lined up with the center of the screen and pulled a distance forward on the Z
+axis. ``Gun_aim_point`` will serve as the point where the bullets will for sure collied
+with as it goes along.
+
+.. note:: There is a invisible mesh instance for debugging purposes. The mesh is
+          a small sphere that visually shows where the bullets will be aiming at.
+
+Open up ``Gun_fire_points`` and you'll find three more :ref:`Spatial <class_Spatial>` nodes, one for each
+weapon.
+
+Open up ``Rifle_point`` and you'll find a :ref:`Raycast <class_Raycast>` node. This is where
+we will be sending the raycasts for our rilfe's bullets.
+The length of the raycast will dictate how far our the bullets will travel.
+
+We are using a :ref:`Raycast <class_Raycast>` node to handle the rifle's bullet because
+we want to fire lots of bullets quickly. If we use bullet objects, it is quite possible
+we could run into performance issues on older machines.
+
+.. note:: If you are wondering where the positions of the points came from, they
+          are the rough positions of the ends of each weapon. You can see this by
+          going to ``AnimationPlayer``, selecting one of the firing animations
+          and scrubbing through the timeline. The point for each weapon should mostly line
+          up with the end of each weapon.
+
+Open up ``Knife_point`` and you'll find a :ref:`Area <class_Area>` node. We are using a :ref:`Area <class_Area>` for the knife
+because we only care for all of the bodies close to us, and because our knife does
+not fire into space. If we were making a throwing knife, we would likely spawn a bullet
+object that looks like a knife.
+
+Finally, we have ``Pistol point``. This is the point where we will be creating/instancing
+our bullet objects. We do not need any additional nodes here, as the bullet handles all
+of its own collision detection.
+
+Now that we've seen how we will handle our other weapons, and where we will spawn the bullets,
+let's start working on making them work.
+
+.. note:: You can also look at the HUD nodes if you want. There is nothing fancy there and other
+         than using a single :ref:`Label <class_Label>`, we will not be touching any of those nodes.
+         Check :ref:`doc_design_interfaces_with_the_control_nodes` for a tutorial on using GUI nodes.
+
+         The GUI provided in this tutorial is *very* basic. Maybe in a later part we will
+         revise the GUI, but for now we are going to just use this GUI as it will serve our needs for now.
+
+Making the weapons work
+-----------------------
+
+Lets start making the weapons work in ``Player.gd``.
+
+First lets start by adding some global variables we'll need for the weapons:
+
+::
+
+    # Place before _ready
+    var animation_manager;
+
+    var current_gun = "UNARMED"
+    var changing_gun = false
+
+    var bullet_scene = preload("Bullet_Scene.tscn")
+
+    var health = 100
+
+    const RIFLE_DAMAGE = 4
+    const KNIFE_DAMAGE = 40
+
+    var UI_status_label
+
+Let's go over what these new variables will do:
+
+- ``animation_manager``: This will hold the :ref:`AnimationPlayer <class_AnimationPlayer>` node and its script, which we wrote previously.
+- ``current_gun``: This is the name of the gun we are currently using. It has four possible values: ``UNARMED``, ``KNIFE``, ``PISTOL``, and ``RIFLE``.
+- ``changing_gun``: A boolean to track whether or not we are changing guns/weapons.
+- ``bullet_scene``: The bullet scene we worked on earlier, ``Bullet_Scene.tscn``. We need to load it here so we can create/spawn it when the pistol fires
+- ``health``: How much health our player has. In this part of the tutorial we will not really be using it.
+- ``RIFLE_DAMAGE``: How much damage a single rifle bullet causes.
+- ``KNIFE_DAMAGE``: How much damage a single knife stab/swipe causes.
+- ``UI_status_label``: A label to show how much health we have, and how much ammo we have both in our gun and in reserves.
+
+_________
+
+Next we need to add a few things in ``_ready``. Here's the new ``_ready`` function:
+
+::
+
+    func _ready():
+        camera = get_node("Rotation_helper/Camera")
+        rotation_helper = get_node("Rotation_helper")
+
+        animation_manager = get_node("Rotation_helper/Model/AnimationPlayer")
+        animation_manager.callback_function = funcref(self, "fire_bullet")
+
+        set_physics_process(true)
+
+        Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
+        set_process_input(true)
+
+        # Make sure the bullet spawn point, the raycast, and the knife area are all aiming at the center of the screen
+        var gun_aim_point_pos = get_node("Rotation_helper/Gun_aim_point").global_transform.origin
+        get_node("Rotation_helper/Gun_fire_points/Pistol_point").look_at(gun_aim_point_pos, Vector3(0, 1, 0))
+        get_node("Rotation_helper/Gun_fire_points/Rifle_point").look_at(gun_aim_point_pos, Vector3(0, 1, 0))
+        get_node("Rotation_helper/Gun_fire_points/Knife_point").look_at(gun_aim_point_pos, Vector3(0, 1, 0))
+
+        # Because we have the camera rotated by 180 degrees, we need to rotate the points around by 180
+        # degrees on their local Y axis because otherwise the bullets will fire backwards
+        get_node("Rotation_helper/Gun_fire_points/Pistol_point").rotate_object_local(Vector3(0, 1, 0), deg2rad(180))
+        get_node("Rotation_helper/Gun_fire_points/Rifle_point").rotate_object_local(Vector3(0, 1, 0), deg2rad(180))
+        get_node("Rotation_helper/Gun_fire_points/Knife_point").rotate_object_local(Vector3(0, 1, 0), deg2rad(180))
+
+        UI_status_label = get_node("HUD/Panel/Gun_label")
+        flashlight = get_node("Rotation_helper/Flashlight")
+
+Let's go over what's changed.
+
+First we get the :ref:`AnimationPlayer <class_AnimationPlayer>` node and assign it to our animation_manager variable. Then we set the callback function
+to a :ref:`FuncRef <class_FuncRef>` that will call the player's ``fire_bullet`` function. Right now we haven't written our fire_bullet function,
+but we'll get there soon.
+
+Then we get all of the weapon points and call each of their ``look_at``.
+This will make sure they all are facing the gun aim point, which is in the center of our camera at a certain distance back.
+
+Next we rotate all of those weapon points by ``180`` degrees on their ``Y`` axis. This is because our camera is pointing backwards.
+If we did not rotate all of these weapon points by ``180`` degrees, all of the weapons would fire backwards at ourselves.
+
+Finally, we get the UI :ref:`Label <class_Label>` from our HUD.
+
+_________
+
+Lets add a few things to ``_physics_process`` so we can fire our weapons. Here's the new code:
+
+::
+
+    func _physics_process(delta):
+        var dir = Vector3()
+        var cam_xform = camera.get_global_transform()
+
+
+        if Input.is_action_pressed("movement_forward"):
+            dir += -cam_xform.basis.z.normalized()
+        if Input.is_action_pressed("movement_backward"):
+            dir += cam_xform.basis.z.normalized()
+        if Input.is_action_pressed("movement_left"):
+            dir += -cam_xform.basis.x.normalized()
+        if Input.is_action_pressed("movement_right"):
+            dir += cam_xform.basis.x.normalized()
+
+
+        if is_on_floor():
+            if Input.is_action_just_pressed("movement_jump"):
+                if is_sprinting:
+                    vel.y = SPRINT_JUMP_SPEED
+                else:
+                    vel.y = JUMP_SPEED
+
+        if Input.is_action_just_pressed("flashlight"):
+            if flashlight.is_visible_in_tree():
+                flashlight.hide()
+            else:
+                flashlight.show()
+
+        dir.y = 0
+        dir = dir.normalized()
+
+        var grav = 0
+        if Input.is_action_pressed("movement_sprint"):
+            is_sprinting = true
+            grav = sprint_grav
+        else:
+            is_sprinting = false;
+            grav = norm_grav
+
+        vel.y += delta*grav
+
+        var hvel = vel
+        hvel.y = 0
+
+        var target = dir
+        if is_sprinting:
+            target *= MAX_SPRINT_SPEED
+        else:
+            target *= MAX_SPEED
+
+        var accel
+        if dir.dot(hvel) > 0:
+            if is_sprinting:
+                accel = SPRINT_ACCEL
+            else:
+                accel = ACCEL
+        else:
+            accel = DEACCEL
+
+        hvel = hvel.linear_interpolate(target, accel*delta)
+        vel.x = hvel.x
+        vel.z = hvel.z
+        vel = move_and_slide(vel,Vector3(0,1,0), 0.05, 4, deg2rad(MAX_SLOPE_ANGLE))
+
+
+        if Input.is_action_just_pressed("ui_cancel"):
+            if Input.get_mouse_mode() == Input.MOUSE_MODE_VISIBLE:
+                Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
+            else:
+                Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
+
+        # NEW CODE
+        if changing_gun == false:
+    		if Input.is_key_pressed(KEY_1):
+    			current_gun = "UNARMED"
+    			changing_gun = true
+    		elif Input.is_key_pressed(KEY_2):
+    			current_gun = "KNIFE"
+    			changing_gun = true
+    		elif Input.is_key_pressed(KEY_3):
+    			current_gun = "PISTOL"
+    			changing_gun = true
+    		elif Input.is_key_pressed(KEY_4):
+    			current_gun = "RIFLE"
+    			changing_gun = true
+
+        if changing_gun == true:
+    		if current_gun != "PISTOL":
+    			if animation_manager.current_state == "Pistol_idle":
+    				animation_manager.set_animation("Pistol_unequip")
+    		if current_gun != "RIFLE":
+    			if animation_manager.current_state == "Rifle_idle":
+    				animation_manager.set_animation("Rifle_unequip")
+    		if current_gun != "KNIFE":
+    			if animation_manager.current_state == "Knife_idle":
+    				animation_manager.set_animation("Knife_unequip")
+
+    		if current_gun == "UNARMED":
+    			if animation_manager.current_state == "Idle_unarmed":
+    				changing_gun = false
+
+    		elif current_gun == "KNIFE":
+    			if animation_manager.current_state == "Knife_idle":
+    				changing_gun = false
+    			if animation_manager.current_state == "Idle_unarmed":
+    				animation_manager.set_animation("Knife_equip")
+
+    		elif current_gun == "PISTOL":
+    			if animation_manager.current_state == "Pistol_idle":
+    				changing_gun = false
+    			if animation_manager.current_state == "Idle_unarmed":
+    				animation_manager.set_animation("Pistol_equip")
+
+    		elif current_gun == "RIFLE":
+    			if animation_manager.current_state == "Rifle_idle":
+    				changing_gun = false
+    			if animation_manager.current_state == "Idle_unarmed":
+    				animation_manager.set_animation("Rifle_equip")
+
+
+	# Firing the weapons
+	if Input.is_action_pressed("fire"):
+		if current_gun == "PISTOL":
+    		if animation_manager.current_state == "Pistol_idle":
+    			animation_manager.set_animation("Pistol_fire")
+
+		elif current_gun == "RIFLE":
+			if animation_manager.current_state == "Rifle_idle":
+				animation_manager.set_animation("Rifle_fire")
+
+		elif current_gun == "KNIFE":
+			if animation_manager.current_state == "Knife_idle":
+				animation_manager.set_animation("Knife_fire")
+
+    # HUD (UI)
+	UI_status_label.text = "HEALTH: " + str(health)
+
+
+Lets go over it one chunk at a time:
+
+_________
+
+First we have a if check to see if ``changing_gun`` is equal to ``false``. If it is, we
+then check to see if the number keys ``1`` through ``4`` are pressed. If one of the keys
+are pressed, we set current gun to the name of each weapon assigned to each key and set
+``changing_gun`` to ``true``.
+
+Then we check if ``changing_gun`` is ``true``. If it is ``true``, we then go through a series of checks.
+The first set of checks is checking if we are in a idle animation that is not the weapon/gun we are trying
+to change to, as then we'd be stuck in a loop.
+
+Then we check if we are in an unarmed state. If we are and the newly selected 'weapon'
+is ``UNARMED``, then we set ``changing_gun`` to ``false``.
+
+If we are trying to change to any of the other weapons, we first check if we are in the
+desired weapon's idle state. If we are, then we've successfully changed weapons and set
+``changing_gun`` to false.
+
+If we are not in the desired weapon's idle state, we then check if we are in the idle unarmed state.
+This is because all unequip animations transition to idle unarmed, and because we can transition to
+any equip animation from idle unarmed.
+
+If we are in the idle unarmed state, we set the animation to the equip animation for the
+desired weapon. Once the equip animation is finished, it will change to the idle state for that
+weapon, which will pass the ``if`` check above.
+
+_________
+
+For firing the weapons we first check if the ``fire`` action is pressed or not.
+If the fire action is pressed, we then check which weapon we are using.
+
+If we are in a weapon's idle state, we then call set the animation to the weapon's fire animation.
+
+_________
+
+Now, we just need to add one more function to the player, and then the player is ready to start shooting!
+
+We just need to add ``fire_bullet``, which will be called when by the :ref:`AnimationPlayer <class_AnimationPlayer>` at those
+points we set earlier in the :ref:`AnimationPlayer <class_AnimationPlayer>` function track:
+
+::
+
+    func fire_bullet():
+        if changing_gun == true:
+            return
+
+        # Pistol bullet handling: Spawn a bullet object!
+        if current_gun == "PISTOL":
+            var clone = bullet_scene.instance()
+            var scene_root = get_tree().root.get_children()[0]
+            scene_root.add_child(clone)
+
+            clone.global_transform = get_node("Rotation_helper/Gun_fire_points/Pistol_point").global_transform
+            # The bullet is a little too small (by default), so let's make it bigger!
+            clone.scale = Vector3(4, 4, 4)
+
+        # Rifle bullet handeling: Send a raycast!
+        elif current_gun == "RIFLE":
+            var ray = get_node("Rotation_helper/Gun_fire_points/Rifle_point/RayCast")
+            ray.force_raycast_update()
+
+            if ray.is_colliding():
+                var body = ray.get_collider()
+                if body.has_method("bullet_hit"):
+                    body.bullet_hit(RIFLE_DAMAGE, ray.get_collision_point())
+
+        # Knife bullet(?) handeling: Use an area!
+        elif current_gun == "KNIFE":
+            var area = get_node("Rotation_helper/Gun_fire_points/Knife_point/Area")
+            var bodies = area.get_overlapping_bodies()
+
+            for body in bodies:
+                if body.has_method("bullet_hit"):
+                    body.bullet_hit(KNIFE_DAMAGE, area.global_transform.origin)
+
+
+Lets go over what this function is doing:
+
+_________
+
+First we check if we are changing weapons or not. If we are changing weapons, we do not want shoot so we just ``return``.
+
+.. tip:: Calling ``return`` stops the rest of the function from being called. In this case, we are not returning a variable
+         because we are only interested in not running the rest of the code, and because we are not looking for a returned
+         variable either when we call this function.
+
+Next we check which weapon we are using.
+
+If we are using a pistol, we first create a ``Bullet_Scene.tscn`` instance and assign it to
+a variable named ``clone``. Then we get the root node in the :ref:`SceneTree <class_SceneTree>`, which happens to be a :ref:`Viewport <class_Viewport>`.
+We then get the first child of the :ref:`Viewport <class_Viewport>` and assign it to the ``scene_root`` variable.
+
+We then add our newly instanced/created bullet as a child of ``scene_root``.
+
+.. warning:: As mentioned later below in the section on adding sounds, this method makes a assumption. This will be explained later
+             in the section on adding sounds in :ref:`doc_fps_tutorial_part_three`
+
+Next we set the global :ref:`Transform <class_Transform>` of the bullet to that of the pistol bullet spawn point we
+talked about earlier.
+
+Finally, we set the scale a little bigger because the bullet normally is too small to see.
+
+_______
+
+For the rifle, we first get the :ref:`Raycast <class_Raycast>` node and assign it to a variable called ``ray``.
+Then we call :ref:`Raycast <class_Raycast>`'s ``force_raycast_update`` function.
+
+``force_raycast_update`` sends the :ref:`Raycast <class_Raycast>` out and collects the collision data as soon as we call it,
+meaning we get frame perfect collision data and we do not need to worry about performance issues by having the
+:ref:`Raycast <class_Raycast>` enabled all the time.
+
+Next we check if the :ref:`Raycast <class_Raycast>` collided with anything. If it has, we then get the collision body
+it collided with. If the body has the ``bullet_hit`` method/function, we then call it and pass
+in ``RIFLE_DAMAGE`` and the position where the :ref:`Raycast <class_Raycast>` collided.
+
+.. tip:: Remember how we mentioned the speed of the animations for firing was faster than
+         the other animations? By changing the firing animation speeds, you can change how
+         fast the weapon fires bullets!
+
+_______
+
+For the knife we first get the :ref:`Area <class_Area>` node and assign it to a variable named ``area``.
+Then we get all of the collision bodies inside the :ref:`Area <class_Area>`. We loop through each one
+and check if they have the ``bullet_hit`` method/function. If they do, we call it and pass
+in ``KNIFE_DAMAGE`` and the global position of :ref:`Area <class_Area>`.
+
+.. note:: While we could attempt to calculate a rough location for where the knife hit, we
+          do not bother because using the area's position works well enough and the extra time
+          needed to calculate a rough position for each body is not really worth the effort.
+
+_______
+
+
+Before we are ready to test our new weapons, we still have just a little bit of work to do.
+
+Creating some test subjects
+---------------------------
+
+Create a new script by going to the scripting window, clicking "file", and selecting new.
+Name this script "RigidBody_hit_test" and make sure it extends :ref:`RigidBody <class_RigidBody>`.
+
+Now we just need to add this code:
+
+::
+
+    extends RigidBody
+
+    func _ready():
+        pass
+
+    func bullet_hit(damage, bullet_hit_pos):
+        var direction_vect = self.global_transform.origin - bullet_hit_pos
+        direction_vect = direction_vect.normalized()
+
+        self.apply_impulse(bullet_hit_pos, direction_vect * damage)
+
+
+Lets go over how ``bullet_hit`` works:
+
+First we get the direction from the bullet pointing towards our global :ref:`Transform <class_Transform>`.
+We do this by subtracting the bullet's hit position from the :ref:`RigidBody <class_RigidBody>`'s position.
+This results in a :ref:`Vector3 <class_Vector3>` that we can use to tell the direction the bullet collided into the
+:ref:`RigidBody <class_RigidBody>` at.
+
+We then normalize it so we do not get crazy results from collisions on the extremes
+of the collision shape attached to the :ref:`RigidBody <class_RigidBody>`. Without normalizing shots farther
+away from the center of the :ref:`RigidBody <class_RigidBody>` would cause a more noticeable reaction than
+those closer to the center.
+
+Finally, we apply an impulse at the passed in bullet collision position. With the force
+being the directional vector times the damage the bullet is supposed to cause. This makes
+the :ref:`RigidBody <class_RigidBody>` seem to move in response to the bullet colliding into it.
+
+_______
+
+Now we just need to attach this script to all of the :ref:`RigidBody <class_RigidBody>` nodes we want to effect.
+
+Open up ``Testing_Area.tscn`` and select all of the cubes parented to the ``Cubes`` node.
+
+.. tip:: If you select the top cube, and then hold down ``shift`` and select the last cube, Godot will
+         select all of the cubes in between!
+
+Once you have all of the cubes selected, scroll down in the inspector until you get to the
+the "scripts" section. Click the drop down and select "Load". Open your newly created ``RigidBody_hit_test.gd`` script.
+
+With that done, go give your guns a whirl! You should now be able to fire as many bullets as you want on the cubes and
+they will move in response to the bullets colliding into them.
+
+In :ref:`doc_fps_tutorial_part_three`, we will add ammo to the guns, as well as some sounds!

+ 1 - 0
tutorials/3d/index.rst

@@ -18,3 +18,4 @@
    working_with_3d_skeletons
    inverse_kinematics
    mesh_generation_with_heightmap_and_shaders
+   fps_tutorial/index