Teil 3¶
Teilübersicht¶
In diesem Teil werden wir die Waffen des Spielers einschränken, indem wir ihm Munition geben. Wir werden dem Spieler auch die Möglichkeit geben nachzuladen und wir werden beim abfeuern der Waffen Geräusche hinzufügen.

Bemerkung
You are assumed to have finished Teil 2 before moving on to this part of the tutorial. The finished project from Teil 2 will be the starting project for part 3
Lassen Sie uns anfangen!
Levels ändern¶
Nachdem wir nun einen voll funktionsfähigen FPS haben, gehen wir zu einem FPS-ähnlichen Level über.
Öffnen Sie Space_Level.tscn
(assets/Space_Level_Objects/Space_Level.tscn
) und/oder 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 Play Current Scene
button,
or F6 on keyboard, and give each a try.
Warnung
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.
Bemerkung
Due to Godot updates since this tutorial was published, if you are using Godot 3.2 or later, you may need to apply the following changes to the Space Level and Ruins Level scenes:
Öffnen Sie
res://assets/Space_Level_Objects/Space_Level.tscn
.In the Scene tree dock, select the Floor_and_Celing node. In the Inspector dock, if the Mesh Library field under GridMap is
[empty]
, set it toSpace_Level_Mesh_Lib.tres
by dragging the fileres://assets/Space_Level_Objects/Space_Level_Mesh_Lib.tres
from the FileSystem dock to that field.Machen Sie dasselbe für das Walls Node.
Öffnen Sie
res://assets/Ruin_Level_Objects/Ruins_Level.tscn
.In the Scene tree dock, select the Floor node. In the Inspector dock, if the Mesh Library field under GridMap is
[empty]
, set it toRuin_Level_Mesh_Lib.tres
by dragging the fileres://assets/Ruin_Level_Objects/Ruin_Level_Mesh_Lib.tres
from the FileSystem dock into that field.Machen Sie dasselbe für das Walls Node.
You might have noticed there are several 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!
Befolgen Sie die nachstehenden Anweisungen für eine (oder beide) der Szenen, die Sie verwenden möchten
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".
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".
Jetzt können Sie auf alle starren Körper in beiden Ebenen schießen und sie reagieren auf Geschosse, die sie treffen!
Munition hinzufügen¶
Nachdem der Spieler über funktionierende Waffen verfügt, geben wir ihm eine begrenzte Menge Munition.
Zunächst müssen wir in jedem unserer Waffenskripte einige Variablen definieren.
Öffnen Sie Weapon_Pistol.gd
und fügen Sie die folgenden Klassenvariablen hinzu:
var ammo_in_weapon = 10
var spare_ammo = 20
const AMMO_IN_MAG = 10
ammo_in_weapon
: Die Menge an Munition, die sich derzeit in der Pistole befindetspare_ammo
: Die Menge an Munition, die wir für die Pistole reserviert habenAMMO_IN_MAG
: Die Menge an Munition in einer vollständig nachgeladenen Waffe bzw. Magazin
Jetzt müssen wir nur noch eine einzige Codezeile zu fire_weapon
hinzufügen.
Fügen Sie direkt unter Clone.BULLET_DAMAGE = DAMAGE
dies hinzu: 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
.
Jetzt müssen wir Munition sowohl für das Gewehr als auch für das Messer hinzufügen.
Bemerkung
Sie fragen sich vielleicht warum wir Munition für das Messer hinzufügen, da es keine Munition verbraucht. Der Grund, warum wir dem Messer Munition hinzufügen möchten ist, dass wir eine einheitliche Oberfläche für alle unsere Waffen haben.
Wenn wir keine Munitionsvariablen für das Messer hinzufügen würden, müssten wir Abfragen für das Messer hinzufügen. Durch Hinzufügen der Munitionsvariablen zum Messer müssen wir uns keine Gedanken darüber machen, ob alle unsere Waffen dieselben Variablen haben oder nicht.
Fügen Sie die folgenden Klassenvariablen zu Weapon_Rifle.gd
hinzu:
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.
Jetzt bleibt nur noch das Messer. Fügen Sie Folgendes zu Weapon_Knife.gd
hinzu:
var ammo_in_weapon = 1
var spare_ammo = 1
const AMMO_IN_MAG = 1
Da das Messer keine Munition verbraucht, ist das alles was wir hinzufügen müssen.
Jetzt müssen wir noch eine Sache in Player.gd
ändern
wie wir die Waffen in process_input
abfeuern. Ändern Sie den Code zum Abfeuern von Waffen wie folgt:
# ----------------------------------
# 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)
# ----------------------------------
Jetzt haben die Waffen eine begrenzte Menge an Munition und hören auf zu schießen, wenn sie dem Spieler ausgeht.
Im Idealfall möchten wir dem Spieler zeigen, wie viel Munition noch übrig ist. Lassen Sie uns eine neue Funktion namens process_UI
erstellen.
Zuerst fügen Sie process_UI(delta)
zu _physics_process
hinzu.
Fügen Sie nun Folgendes zu Player.gd
hinzu:
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)
Lassen Sie uns das einmal durchgehen:
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.
Wenn der Spieler eine Waffe verwendet die Munition verbraucht, erhalten wir zuerst den Waffen-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.
Jetzt können wir durch das HUD sehen, wie viel Munition der Spieler hat.
Nachladen den Waffen hinzufügen¶
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.
Öffnen Sie Weapon_Pistol.gd
und fügen Sie die folgenden Klassenvariablen hinzu:
const CAN_RELOAD = true
const CAN_REFILL = true
const RELOADING_ANIM_NAME = "Pistol_reload"
CAN_RELOAD
: Ein Boolescher Wert um zu verfolgen, ob diese Waffe nachgeladen werden kannCAN_REFILL
: A boolean to track whether we can refill this weapon's spare ammo. We will not be usingCAN_REFILL
in this part, but we will in the next part!RELOADING_ANIM_NAME
: Der Name der Nachladeanimation für diese Waffe.
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
Lassen Sie uns das einmal durchgehen:
Zuerst definieren wir eine Variable um zu sehen, ob diese Waffe nachgeladen werden kann oder nicht.
Dann prüfen wir ob sich der Spieler im Ruhezustand dieser Waffe befindet, da wir nur dann nachladen möchten, wenn der Spieler nicht feuert, Dinge aufnimmt oder ablegt.
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.
Wenn wir noch nachladen können, berechnen wir die Munitionsmenge die zum Nachladen der Waffe benötigt wird.
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
.
Als nächstes spielen wir die Nachladeanimation für diese Waffe ab und geben dann wahr
zurück.
Wenn der Spieler nicht nachladen konnte, geben wir false
zurück.
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.
Jetzt müssen wir reload_weapon
zu Weapon_Rifle.gd
hinzufügen:
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
Dieser Code ist genau der gleiche wie der für die Pistole.
Das letzte was wir für die Waffen tun müssen, ist das Nachladen des Messers. Fügen Sie die folgenden Klassenvariablen zu Weapon_Knife.gd
hinzu:
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.
Jetzt müssen wir reloading_weapon
hinzufügen:
func reload_weapon():
return false
Da wir ein Messer nicht nachladen können, geben wir immer false
zurück.
Nachladen zum Spieler hinzufügen¶
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
: Eine Variable um zu verfolgen, ob der Player gerade versucht neu zu laden.
Als nächstes müssen wir _physics_process
einen weiteren Funktionsaufruf hinzufügen.
Fügen Sie process_reloading(delta)
zu _physics_process
hinzu. Nun sollte _physics_process
in etwa so aussehen:
func _physics_process(delta):
process_input(delta)
process_movement(delta)
process_changing_weapons(delta)
process_reloading(delta)
process_UI(delta)
Jetzt müssen wir process_reloading
hinzufügen. Fügen Sie die folgende Funktion zu Player.gd
hinzu:
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
Lassen Sie uns noch einmal durchgehen was hier passiert.
Zuerst überprüfen wir ob der Spieler versucht neu zu laden.
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.
Bemerkung
Wenn die aktuelle Waffe gleich null
ist, dann ist die aktuelle Waffe 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
Dadurch kann der Spieler die Waffen nicht wechseln wenn er nachlädt.
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
# ----------------------------------
Lassen Sie uns noch einmal durchgehen was hier passiert.
Zuerst stellen wir sicher, dass der Spieler nicht bereits nachlädt und auch nicht versucht die Waffen zu wechseln.
Dann prüfen wir, ob die Aktion reload
gedrückt wurde.
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.
Wenn die Waffe neu geladen werden kann, erhalten wir den aktuellen Animationsstatus und erstellen eine Variable um zu verfolgen, ob der Spieler bereits neu lädt oder nicht.
Wir gehen dann jede Waffe durch um sicherzustellen, dass der Spieler die Nachladeanimation dieser Waffe noch nicht abspielt.
Wenn der Spieler keine Waffe nachlädt, setzen wir reloading_weapon
auf 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.
Dadurch wird der Spieler beim Versuch eine leere Waffe abzufeuern automatisch aufladen.
Damit kann der Spieler jetzt neu laden! Versuchen Sie es! Jetzt können Sie die gesamte Ersatzmunition für jede Waffe abfeuern.
Ton hinzufügen¶
Lassen Sie uns zum Schluss einige Geräusche hinzufügen, die den Spieler beim Schießen, Nachladen und Waffenwechsel begleiten.
Tipp
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 Spatial with an AudioStreamPlayer as its child.
Bemerkung
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 AudioStreamPlayer and select "Change type".
This will open the node browser. Navigate to AudioStreamPlayer3D and select "change". In the source for this tutorial, we will be using AudioStreamPlayer, but you can optionally use 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 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()
Tipp
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
.
Lassen Sie uns durchgehen was hier passiert:
In _ready
, we get the AudioStreamPlayer and connect its finished
signal to the destroy_self
function.
It doesn't matter if it's an AudioStreamPlayer or AudioStreamPlayer3D node,
as they both have the finished signal. To make sure it is not playing any sounds, we call stop
on the AudioStreamPlayer.
Warnung
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 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 AudioStreamPlayer3D, remove the #
to set the position of
the audio player node so it plays at the correct position.
Schließlich weisen wir den AudioStreamPlayer an, abzuspielen.
When the AudioStreamPlayer is finished playing the sound, it will call destroy_self
because
we connected the finished
signal in _ready
. We stop the AudioStreamPlayer and free the audio player
to save on resources.
Bemerkung
Dieses System ist extrem einfach und weist einige große Mängel auf:
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)
Lassen Sie uns durchgehen, was diese Funktion bewirkt:
Die erste Zeile instanziiert die Szene Simple_Audio_Player.tscn
und weist sie einer Variablen mit dem Namen audio_clone
zu.
Die zweite Zeile erhält die Szenenwurzel, und dies hat eine große (wenn auch sichere) Annahme.
We first get this node's SceneTree,
and then access the root node, which in this case is the Viewport this entire game is running under.
Then we get the first child of the 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!
öffnen Sie 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)
Wenn der Spieler nun die Pistole abfeuert, spielen wir den Pistol_shot
Sound ab.
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)
Wenn der Spieler nun nachlädt, spielen wir den Gun_cock
Sound ab.
Fügen wir nun dem Gewehr Geräusche hinzu. Öffnen Sie 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)
Wenn der Spieler nun das Gewehr abfeuert, spielen wir den Rifle_shot
Sound ab.
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)
Wenn der Spieler nun nachlädt, spielen wir den Gun_cock
Sound ab.
Schlussbemerkungen¶

Jetzt haben Sie Waffen mit begrenzter Munition, die beim abfeuern Geräusche abspielen!
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 RigidBody nodes.
In Teil 4 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!
Warnung
Wenn Sie jemals die Orientierung verlieren, lesen Sie den Code unbedingt noch einmal durch!
Sie können das fertige Projekt für diesen Teil hier herunterladen: Godot_FPS_Teil_3.zip