123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684 |
- .. _doc_fps_tutorial_part_three:
- Part 3
- ======
- Part overview
- -------------
- In this part, we will be limiting the player's weapons by giving them ammo. We will also
- be giving the player the ability to reload, and we will be adding sounds when the
- weapons fire.
- .. image:: img/PartThreeFinished.png
- .. note:: You are assumed to have finished :ref:`doc_fps_tutorial_part_two` before moving on to this part of the tutorial.
- The finished project from :ref:`doc_fps_tutorial_part_two` will be the starting project for part 3
- 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 ``Space_Level.tscn`` (``assets/Space_Level_Objects/Space_Level.tscn``) and/or ``Ruins_Level.tscn`` (``assets/Ruin_Level_Objects/Ruins_Level.tscn``).
- ``Space_Level.tscn`` and ``Ruins_Level.tscn`` are complete custom FPS levels created for the purpose of this tutorial. Press ``F6`` to
- play the open scene, or press the ``play current scene button``, and give each a try.
- .. warning:: ``Space_Level.tscn`` is more graphically demanding of the GPU than ``Ruins_Level.tscn``. If your computer is struggling to render
- ``Space_Level.tscn``, try using ``Ruins_Level.tscn`` instead.
- You might have noticed there are several :ref:`RigidBody <class_RigidBody>` nodes placed throughout the level.
- We can place ``RigidBody_hit_test.gd`` on them and then they will react to being hit with bullets, so let's do that!
- Follow the instructions below for either (or both) of the scenes you want to use
- .. tabs::
- .. code-tab:: gdscript Space_Level.tscn
- Expand "Other_Objects" and then expand "Physics_Objects".
- Expand one of the "Barrel_Group" nodes and then select "Barrel_Rigid_Body" and open it using
- the "Open in Editor" button.
- This will bring you to the "Barrel_Rigid_Body" scene. From there, select the root node and
- scroll the inspector down to the bottom.
- Select the drop down arrow under the "Node" tab, and then select "Load". Navigate to
- "RigidBody_hit_test.gd" and select "Open".
- Return back to "Space_Level.tscn".
- Expand one of the "Box_Group" nodes and then select "Crate_Rigid_Body" and open it using the
- "Open in Editor" button.
- This will bring you to the "Crate_Rigid_Body" scene. From there, select the root node and
- scroll the inspector down to the bottom.
- Select the drop down arrow under the "Node" tab, and then select "Load". Navigate to
- "RigidBody_hit_test.gd" and select "Open".
- Return to "Space_Level.tscn".
- .. code-tab:: gdscript Ruins_Level.tscn
- Expand "Misc_Objects" and then expand "Physics_Objects".
- Select all the "Stone_Cube" RigidBodies and then in the inspector scroll down to the bottom.
- Select the drop down arrow under the "Node" tab, and then select "Load". Navigate to
- "RigidBody_hit_test.gd" and select "Open".
- Return to "Ruins_Level.tscn".
- Now you can fire at all the rigid bodies in either level and they will react to bullets hitting them!
- Adding ammo
- -----------
- Now that the player has working guns, let's give them a limited amount of ammo.
- Firstly, we need to define a few variables in each of our weapon scripts.
- Open up ``Weapon_Pistol.gd`` and add the following class variables:
- ::
- var ammo_in_weapon = 10
- var spare_ammo = 20
- const AMMO_IN_MAG = 10
- * ``ammo_in_weapon``: The amount of ammo currently in the pistol
- * ``spare_ammo``: The amount of ammo we have left in reserve for the pistol
- * ``AMMO_IN_MAG``: The amount of ammo in a fully reloaded weapon/magazine
- Now all we need to do is add a single line of code to ``fire_weapon``.
- Add the following right under ``Clone.BULLET_DAMAGE = DAMAGE``: ``ammo_in_weapon -= 1``
- This will remove one from ``ammo_in_weapon`` every time the player fires. Notice we're not checking to see
- if the player has enough ammo or not in ``fire_weapon``. Instead, we're going to check to see if the player has enough ammo in ``Player.gd``.
- _______
- Now we need to add ammo for both the rifle and the knife.
- .. note:: You may be wondering why we are adding ammo for the knife given it does not consume any ammunition.
- The reason we want to add ammo to the knife is so we have a consistent interface for all our weapons.
- If we did not add ammo variables for the knife, we would have to add checks for the knife. By adding the ammo
- variables to the knife, we don't need to worry about whether or not all our weapons have the same variables.
- Add the following class variables to ``Weapon_Rifle.gd``:
- ::
- var ammo_in_weapon = 50
- var spare_ammo = 100
- const AMMO_IN_MAG = 50
- And then add the following to ``fire_weapon``: ``ammo_in_weapon -= 1``. Make sure that ``ammo_in_weapon -= 1`` is outside of the ``if ray.is_colliding()`` check so
- the player loses ammo regardless of whether the player hit something or not.
- Now all that's left is the knife. Add the following to ``Weapon_Knife.gd``:
- ::
- var ammo_in_weapon = 1
- var spare_ammo = 1
- const AMMO_IN_MAG = 1
- Because the knife does not consume ammo, that is all we need to add.
- _______
- Now we need to change one thing in ``Player.gd``, that is to say,
- how we're firing the weapons in ``process_input``. Change the code for firing weapons to the following:
- ::
- # ----------------------------------
- # Firing the weapons
- if Input.is_action_pressed("fire"):
- if changing_weapon == false:
- var current_weapon = weapons[current_weapon_name]
- if current_weapon != null:
- if current_weapon.ammo_in_weapon > 0:
- if animation_manager.current_state == current_weapon.IDLE_ANIM_NAME:
- animation_manager.set_animation(current_weapon.FIRE_ANIM_NAME)
- # ----------------------------------
- Now the weapons have a limited amount of ammo, and will stop firing when the player runs out.
- _______
- Ideally, we'd like to let the player be able to see how much ammo is left. Let's make a new function called ``process_UI``.
- First, add ``process_UI(delta)`` to ``_physics_process``.
- Now add the following to ``Player.gd``:
- ::
- func process_UI(delta):
- if current_weapon_name == "UNARMED" or current_weapon_name == "KNIFE":
- UI_status_label.text = "HEALTH: " + str(health)
- else:
- var current_weapon = weapons[current_weapon_name]
- UI_status_label.text = "HEALTH: " + str(health) + \
- "\nAMMO: " + str(current_weapon.ammo_in_weapon) + "/" + str(current_weapon.spare_ammo)
- Let's go over what's happening:
- Firstly, we check to see if the current weapon is either ``UNARMED`` or ``KNIFE``. If it is, we
- change the ``UI_status_label``'s text to only show the player's health since ``UNARMED`` and ``KNIFE`` do not consume ammo.
- If the player is using a weapon that consumes ammo, we first get the weapon node.
- Then we change ``UI_status_label``'s text to show the player's health, along with how much ammo the player has in the weapon
- and how much spare ammo the player has for that weapon.
- Now we can see how much ammo the player has through the HUD.
- Adding reloading to the weapons
- -------------------------------
- Now that the player can run out of ammo, we need a way to let the player fill them back up. Let's add reloading next!
- For reloading, we need to add a few more variables and a function to every weapon.
- Open up ``Weapon_Pistol.gd`` and add the following class variables:
- ::
- const CAN_RELOAD = true
- const CAN_REFILL = true
- const RELOADING_ANIM_NAME = "Pistol_reload"
- * ``CAN_RELOAD``: A boolean to track whether this weapon has the ability to reload
- * ``CAN_REFILL``: A boolean to track whether we can refill this weapon's spare ammo. We will not be using ``CAN_REFILL`` in this part, but we will in the next part!
- * ``RELOADING_ANIM_NAME``: The name of the reloading animation for this weapon.
- Now we need to add a function for handling reloading. Add the following function to ``Weapon_Pistol.gd``:
- ::
- func reload_weapon():
- var can_reload = false
- if player_node.animation_manager.current_state == IDLE_ANIM_NAME:
- can_reload = true
- if spare_ammo <= 0 or ammo_in_weapon == AMMO_IN_MAG:
- can_reload = false
- if can_reload == true:
- var ammo_needed = AMMO_IN_MAG - ammo_in_weapon
- if spare_ammo >= ammo_needed:
- spare_ammo -= ammo_needed
- ammo_in_weapon = AMMO_IN_MAG
- else:
- ammo_in_weapon += spare_ammo
- spare_ammo = 0
- player_node.animation_manager.set_animation(RELOADING_ANIM_NAME)
- return true
- return false
- Let's go over what's happening:
- First we define a variable to see whether or not this weapon can reload.
- Then we check to see if the player is in this weapon's idle animation state because we only want to be able to reload when the player is not
- firing, equipping, or unequipping.
- Next we check to see if the player has spare ammo, and if the ammo already in the weapon is equal to a fully reloaded weapon.
- This way we can ensure the player cannot reload when the player has no ammo or when the weapon is already full of ammo.
- If we can still reload, then we calculate the amount of ammo needed to reload the weapon.
- If the player has enough ammo to fill the weapon, we remove the ammo needed from ``spare_ammo`` and then set ``ammo_in_weapon`` to a full weapon/magazine.
- If the player does not have enough ammo, we add all the ammo left in ``spare_ammo``, and then set ``spare_ammo`` to ``0``.
- Next we play the reloading animation for this weapon, and then return ``true``.
- If the player could not reload, we return ``false``.
- _______
- Now we need to add reloading to the rifle. Open up ``Weapon_Rifle.gd`` and add the following class variables:
- ::
- const CAN_RELOAD = true
- const CAN_REFILL = true
- const RELOADING_ANIM_NAME = "Rifle_reload"
- These variables are exactly the same as the pistol, just with ``RELOADING_ANIM_NAME`` changed to the rifle's reloading animation.
- Now we need to add ``reload_weapon`` to ``Weapon_Rifle.gd``:
- ::
- func reload_weapon():
- var can_reload = false
- if player_node.animation_manager.current_state == IDLE_ANIM_NAME:
- can_reload = true
- if spare_ammo <= 0 or ammo_in_weapon == AMMO_IN_MAG:
- can_reload = false
- if can_reload == true:
- var ammo_needed = AMMO_IN_MAG - ammo_in_weapon
- if spare_ammo >= ammo_needed:
- spare_ammo -= ammo_needed
- ammo_in_weapon = AMMO_IN_MAG
- else:
- ammo_in_weapon += spare_ammo
- spare_ammo = 0
- player_node.animation_manager.set_animation(RELOADING_ANIM_NAME)
- return true
- return false
- This code is exactly the same as the one for the pistol.
- _______
- The last bit we need to do for the weapons is add 'reloading' to the knife. Add the following class variables to ``Weapon_Knife.gd``:
- ::
- const CAN_RELOAD = false
- const CAN_REFILL = false
- const RELOADING_ANIM_NAME = ""
- Since we both cannot reload or refill a knife, we set both constants to ``false``. We also define ``RELOADING_ANIM_NAME`` as an empty string, since the knife
- has no reloading animation.
- Now we need to add ``reloading_weapon``:
- ::
- func reload_weapon():
- return false
- Since we cannot reload a knife, we always return ``false``.
- Adding reloading to the player
- ------------------------------
- Now we need to add a few things to ``Player.gd``. First we need to define a new class variable:
- ::
- var reloading_weapon = false
- * ``reloading_weapon``: A variable to track whether or not the player is currently trying to reload.
- Next we need to add another function call to ``_physics_process``.
- Add ``process_reloading(delta)`` to ``_physics_process``. Now ``_physics_process`` should look something like this:
- ::
- func _physics_process(delta):
- process_input(delta)
- process_movement(delta)
- process_changing_weapons(delta)
- process_reloading(delta)
- process_UI(delta)
- Now we need to add ``process_reloading``. Add the following function to ``Player.gd``:
- ::
- func process_reloading(delta):
- if reloading_weapon == true:
- var current_weapon = weapons[current_weapon_name]
- if current_weapon != null:
- current_weapon.reload_weapon()
- reloading_weapon = false
- Let's go over what's happening here.
- Firstly, we check to make sure the player is trying to reload.
- If the player is trying to reload, we then get the current weapon. If the current weapon does not equal ``null``, we call its ``reload_weapon`` function.
- .. note:: If the current weapon is equal to ``null``, then the current weapon is ``UNARMED``.
- Finally, we set ``reloading_weapon`` to ``false`` because, regardless of whether the player successfully reloaded, we've tried reloading
- and no longer need to keep trying.
- _______
- Before we can let the player reload, we need to change a few things in ``process_input``.
- The first thing we need to change is in the code for changing weapons. We need to add an additional check (``if reloading_weapon == false:``) to see if the player is reloading:
- ::
- if changing_weapon == false:
- # New line of code here!
- if reloading_weapon == false:
- if WEAPON_NUMBER_TO_NAME[weapon_change_number] != current_weapon_name:
- changing_weapon_name = WEAPON_NUMBER_TO_NAME[weapon_change_number]
- changing_weapon = true
- This makes it so the player cannot change weapons if the player is reloading.
- Now we need to add the code to trigger a reload when the player pushes the ``reload`` action. Add the following code to ``process_input``:
- ::
- # ----------------------------------
- # Reloading
- if reloading_weapon == false:
- if changing_weapon == false:
- if Input.is_action_just_pressed("reload"):
- var current_weapon = weapons[current_weapon_name]
- if current_weapon != null:
- if current_weapon.CAN_RELOAD == true:
- var current_anim_state = animation_manager.current_state
- var is_reloading = false
- for weapon in weapons:
- var weapon_node = weapons[weapon]
- if weapon_node != null:
- if current_anim_state == weapon_node.RELOADING_ANIM_NAME:
- is_reloading = true
- if is_reloading == false:
- reloading_weapon = true
- # ----------------------------------
- Let's go over what's happening here.
- First we make sure the player is not reloading already, nor is the player trying to change weapons.
- Then we check to see if the ``reload`` action has been pressed.
- If the player has pressed ``reload``, we then get the current weapon and check to make sure it is not ``null``. Then we check to see whether the
- weapon can reload or not using its ``CAN_RELOAD`` constant.
- If the weapon can reload, we then get the current animation state, and make a variable for tracking whether the player is already reloading or not.
- We then go through every weapon to make sure the player is not already playing that weapon's reloading animation.
- If the player is not reloading any weapon, we set ``reloading_weapon`` to ``true``.
- _______
- One thing I like to add is where the weapon will reload itself if you try to fire it and it's out of ammo.
- We also need to add an additional if check (``is_reloading_weapon == false:``) so the player cannot fire the current weapon while
- reloading.
- Let's change our firing code in ``process_input`` so it reloads when trying to fire an empty weapon:
- ::
- # ----------------------------------
- # Firing the weapons
- if Input.is_action_pressed("fire"):
- if reloading_weapon == false:
- if changing_weapon == false:
- var current_weapon = weapons[current_weapon_name]
- if current_weapon != null:
- if current_weapon.ammo_in_weapon > 0:
- if animation_manager.current_state == current_weapon.IDLE_ANIM_NAME:
- animation_manager.set_animation(current_weapon.FIRE_ANIM_NAME)
- else:
- reloading_weapon = true
- # ----------------------------------
- Now we check to make sure the player is not reloading before we fire the weapon, and when we have ``0`` or less ammo in the current weapon,
- we set ``reloading_weapon`` to ``true`` if the player tries to fire.
- This will make it so the player will try to reload when attempting to fire an empty weapon.
- _______
- With that done, the player can now reload! Give it a try! Now you can fire all the spare ammo for each weapon.
- Adding sounds
- -------------
- Finally, let's add some sounds that accompany the player firing, reloading and changing weapons.
- .. 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.
- Open up ``Simple_Audio_Player.tscn``. It is simply a :ref:`Spatial <class_Spatial>` with an :ref:`AudioStreamPlayer <class_AudioStreamPlayer>` as its 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.
- 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 ``Simple_Audio_Player.gd``. Attach it to the :ref:`Spatial <class_Spatial>` in ``Simple_Audio_Player.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 = $Audio_Stream_Player
- audio_node.connect("finished", self, "destroy_self")
- audio_node.stop()
- func play_sound(sound_name, position=null):
- if audio_pistol_shot == null or audio_rifle_shot == null or audio_gun_cock == null:
- print ("Audio not set!")
- queue_free()
- return
- 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 an AudioStreamPlayer3D, then uncomment these lines to set the position.
- #if audio_node is AudioStreamPlayer3D:
- # 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 ``play_sound``.
- Let's go over what's happening here:
- _________
- In ``_ready``, we get the :ref:`AudioStreamPlayer <class_AudioStreamPlayer>` and connect its ``finished`` signal to the ``destroy_self`` function.
- It doesn't matter if it's an :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 one of the three sounds we set the audio stream in :ref:`AudioStreamPlayer <class_AudioStreamPlayer>`
- to the correct sound.
- If it is an unknown sound, we print an error message to the console and free the audio player.
- If you are using an :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 the audio player
- 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.
- One of the biggest flaws with this system is we can only play sounds from ``Player.gd``.
- Ideally we'd like to be able to play sounds from any script at any time.
- _________
- With that done, let's open up ``Player.gd`` again.
- First we need to load the ``Simple_Audio_Player.tscn``. Place the following code in the class variables section of the script:
- ::
- var simple_audio_player = preload("res://Simple_Audio_Player.tscn")
- Now we need to instance the simple audio player when we need it, and then call its
- ``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 in ``Player.gd``:
- ::
- 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)
- Let's 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, and this has a large (though safe) 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 any of the other provided levels. **We are making a huge assumption that the first child of the root node
- is the root scene that the player is under, which may 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 does not work
- reliably if you have multiple scenes loaded as children of the root node at a time, which will rarely happen for most projects and will not be happening in this tutorial series.
- This is only potentially a issue depending on how you handle scene loading.
- The third line adds our newly created ``Simple_Audio_Player`` 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 passed in to ``create_sound``. This will call
- ``Simple_Audio_Player.gd``'s ``play_sound`` function with the passed in arguments.
- _________
- Now all that is left is playing the sounds when we want to. Let's add sound to the pistol first!
- Open up ``Weapon_Pistol.gd``.
- Now, we want to make a noise when the player fires the pistol, so add the following to the end of the ``fire_weapon`` function:
- ::
- player_node.create_sound("Pistol_shot", self.global_transform.origin)
- Now when the player fires the pistol, we'll play the ``Pistol_shot`` sound.
- To make a sound when the player reloads, we need to add the following right under ``player_node.animation_manager.set_animation(RELOADING_ANIM_NAME)`` in the
- ``reload_weapon`` function:
- ::
- player_node.create_sound("Gun_cock", player_node.camera.global_transform.origin)
- Now when the player reloads, we'll play the ``Gun_cock`` sound.
- _________
- Now let's add sounds to the rifle.
- Open up ``Weapon_Rifle.gd``.
- To play sounds when the rifle is fired, add the following to the end of the ``fire_weapon`` function:
- ::
- player_node.create_sound("Rifle_shot", ray.global_transform.origin)
- Now when the player fires the rifle, we'll play the ``Rifle_shot`` sound.
- To make a sound when the player reloads, we need to add the following right under ``player_node.animation_manager.set_animation(RELOADING_ANIM_NAME)`` in the
- ``reload_weapon`` function:
- ::
- player_node.create_sound("Gun_cock", player_node.camera.global_transform.origin)
- Now when the player reloads, we'll play the ``Gun_cock`` sound.
- Final notes
- -----------
- .. image:: img/PartThreeFinished.png
- Now you have weapons with limited ammo that play sounds when you fire them!
- At this point, we have all the basics of an FPS game working.
- There are still a few things that would be nice to add, and we're going to add them in the next three parts!
- For example, right now we have no way to add ammo to our spares, so we'll eventually run out. Also, we don't
- have anything to shoot at outside of the :ref:`RigidBody <class_RigidBody>` nodes.
- In :ref:`doc_fps_tutorial_part_four` we'll add some targets to shoot at, along with some health and ammo pick ups!
- We're also going to add joypad support, so we can play with wired Xbox 360 controllers!
- .. warning:: If you ever get lost, be sure to read over the code again!
- You can download the finished project for this part here: :download:`Godot_FPS_Part_3.zip <files/Godot_FPS_Part_3.zip>`
|