Parte 3

Resumen

En esta parte, limitaremos las armas del jugador dándoles munición. También le daremos al jugador la capacidad de recargar, y añadiremos sonidos cuando las armas disparen.

../../../_images/PartThreeFinished.png

Nota

Se supone que has terminado Parte 2 antes de pasar a esta parte del tutorial. El proyecto terminado de Parte 2 será el proyecto inicial de la parte 3

¡Vamos a empezar!

Cambiando los niveles

Ahora que tenemos un FPS completamente funcional, pasemos a un nivel más parecido al FPS.

Abre Space_Level.tscn (assets/Space_Level_Objects/Space_Level.tscn) y/o Ruins_Level.tscn (assets/Ruin_Level_Objects/Ruins_Level.tscn).

Space_Level.tscn y Ruins_Level.tscn son niveles FPS completos y personalizados creados para el propósito de este tutorial. Presiona el botón Play Current Scene, o F6 en el teclado, y prueba cada uno.

Advertencia

"Space_Level.tscn" es más exigente gráficamente con la GPU que "Ruins_Level.tscn". Si tu ordenador tiene problemas para renderizar Space_Level.tscn, intenta usar Ruins_Level.tscn en su lugar.

Nota

Debido a las actualizaciones de Godot desde que este tutorial fue publicado, si está utilizando Godot 3.2 o una versión posterior, usted podría tener que aplicar los siguientes cambios a las escenas de Space Level y Ruins Level:

  • Abre res://assets/Space_Level_Objects/Space_Level.tscn.

  • En el dock del Scene Tree, seleccione el nodo ** Floor_and_Celing **. En el dock del Inspector, si el campo del Mesh Library debajo de GridMap está [empty], póngalo en Space_Level_Mesh_Lib.tres arrastrando el archivo res://assets/Space_Level_Objects/Space_Level_Mesh_Lib.tres desde el dock FileSystem a ese campo.

  • Haga lo mismo para el nodo Walls.

  • Abra res://assets/Ruin_Level_Objects/Ruins_Level.tscn.

  • En el dock del árbol de escenas, seleccione el nodo Floor. En el dock del Inspector, si el campo del Mesh Library debajo del GridMap es [empty], póngalo en Ruin_Level_Mesh_Lib.tres arrastrando el archivo res://assets/Ruin_Level_Objects/Ruin_Level_Mesh_Lib.tres desde el dock FileSystem en ese campo.

  • Haga lo mismo para el nodo Walls.

Habrás notado que hay varios nodos RigidBody colocados a lo largo del nivel. Podemos colocar RigidBody_hit_test.gd en ellos y entonces reaccionarán al ser golpeados con balas, así que hagámoslo!

Siga las siguientes instrucciones para cualquiera (o ambas) de las escenas que quiera usar

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".

¡Ahora puedes disparar a todos los cuerpos rígidos en cualquier nivel y reaccionarán a las balas que los alcancen!

Añadiendo munición

Ahora que el jugador tiene armas de trabajo, démosle una cantidad limitada de munición.

En primer lugar, necesitamos definir unas pocas variables en cada uno de nuestros scripts de armas.

Abre Weapon_Pistol.gd y añade las siguientes variables de clase:

var ammo_in_weapon = 10
var spare_ammo = 20
const AMMO_IN_MAG = 10
  • ammo_in_weapon: La cantidad de munición que hay actualmente en la pistola

  • "Spare_ammo": La cantidad de munición que nos queda en reserva para la pistola

  • AMMO_IN_MAG: La cantidad de munición en un recarga de arma/cargador

Ahora todo lo que tenemos que hacer es añadir una sola línea de código a fire_weapon.

Añade el siguiente texto justo debajo Clone.BULLET_DAMAGE = DAMAGE: ammo_in_weapon -= 1

Esto eliminará a uno de ammo_in_weapon cada vez que el jugador dispare. Nota que no estamos revisando si el jugador tiene suficiente munición o no en fire_weapon. En su lugar, vamos a comprobar si el jugador tiene suficiente munición en Player.gd.


Ahora tenemos que añadir munición para el rifle y el cuchillo.

Nota

Se preguntarán por qué añadimos munición para el cuchillo, ya que no consume ninguna munición. La razón por la que queremos añadir munición al cuchillo es para tener una interfaz consistente para todas nuestras armas.

Si no añadimos variables de munición para el cuchillo, tendríamos que añadir controles para el cuchillo. Al agregar las variables de munición al cuchillo, no tenemos que preocuparnos de si todas nuestras armas tienen las mismas variables.

Añade las siguientes variables de clase a Weapon_Rifle.gd:

var ammo_in_weapon = 50
var spare_ammo = 100
const AMMO_IN_MAG = 50

Y luego agrega lo siguiente a fire_weapon: ammo_in_weapon. Asegúrate de que ammo_in_weapon -= 1 está fuera de la condición if ray.is_colliding() para que el jugador pierda munición sin importar si el jugador golpeó algo o no.

Ahora sólo queda el cuchillo. Añade lo siguiente a Weapon_Knife.gd:

var ammo_in_weapon = 1
var spare_ammo = 1
const AMMO_IN_MAG = 1

Debido a que el cuchillo no consume municiones, eso es todo lo que tenemos que añadir.


Ahora tenemos que cambiar una cosa en Player.gd, que es,

cómo estamos disparando las armas en process_input. Cambia el código para disparar las armas a lo siguiente:

# ----------------------------------
# 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)
# ----------------------------------

Ahora las armas tienen una cantidad limitada de munición, y dejarán de disparar cuando al jugador se le termine.


Lo ideal sería que el jugador pudiera ver cuánta munición le queda. Hagamos una nueva función llamada process_UI.

Primero, agrega process_UI(delta) al _physics_process.

Ahora agrega lo siguiente a 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)

Repasemos lo que está pasando:

En primer lugar, comprobamos si el arma actual está UNARMED o KNIFE. Si lo es, cambiamos el texto de la etiqueta "UI_status_label" para que sólo muestre la salud del jugador ya que UNARMED y KNIFE no consumen munición.

Si el jugador usa un arma que consume municiones, primero obtenemos el nodo de armas.

Luego cambiamos el texto de "UI_status_label" para mostrar la salud del jugador, junto con la cantidad de munición que tiene en el arma y la munición de sobra que tiene para esa arma.

Ahora podemos ver cuanta munición tiene el jugador a través del HUD.

Añadiendo la recarga a las armas

Ahora que el jugador se puede quedar sin municiones, necesitamos una forma de dejar que el jugador las llene de nuevo. ¡Añadamos la recarga a continuación!

Para recargar, necesitamos añadir unas cuantas variables más y una función a cada arma.

Abre Weapon_Pistol.gd y añade las siguientes variables de clase:

const CAN_RELOAD = true
const CAN_REFILL = true

const RELOADING_ANIM_NAME = "Pistol_reload"
  • CAN_RELOAD: Un booleano para rastrear si esta arma tiene la capacidad de recargar

  • CAN_REFILL: Una booleana para saber si podemos rellenar la munición de repuesto de esta arma. ¡No usaremos "CAN_REFILL" en esta parte, pero lo haremos en la siguiente!

  • RELOADING_ANIM_NAME: El nombre de la animación de recarga para esta arma.

Ahora necesitamos añadir una función para manejar la recarga. Añade la siguiente función a 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

Repasemos lo que está pasando:

Primero definimos una variable para ver si esta arma puede recargarse o no.

Luego comprobamos si el jugador está en el estado de animación en inactividad de esta arma porque sólo queremos poder recargar cuando el jugador no este disparando, equipando o desarmando.

A continuación comprobamos si el jugador tiene munición de repuesto, y si la munición que ya está en el arma es igual a un arma completamente recargada. De esta manera podemos asegurarnos de que el jugador no puede recargar cuando no tiene municiones o cuando el arma ya está llena de municiones.

Si todavía podemos recargar, entonces calculamos la cantidad de munición necesaria para recargar el arma.

Si el jugador tiene suficiente munición para llenar el arma, quitamos la munición necesaria de spare_ammo y luego ponemos ammo_in_weapon al maximo del arma/cargador.

Si el jugador no tiene suficiente munición, añadimos toda la munición que queda en spare_ammo, y luego ponemos spare_ammo en 0.

A continuación reproducimos la animación de recarga de esta arma, y luego devolvemos true.

Si el jugador no puede recargar, devolvemos el false.


Ahora tenemos que añadir recarga al rifle. Abre Weapon_Rifle.gd y añade las siguientes variables de clase:

const CAN_RELOAD = true
const CAN_REFILL = true

const RELOADING_ANIM_NAME = "Rifle_reload"

Estas variables son exactamente las mismas que las de la pistola, sólo que con RELOADING_ANIM_NAME cambiado a la animación de recarga del rifle.

Ahora tenemos que añadir reload_weapon a 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

Este código es exactamente el mismo que el de la pistola.


Lo último que tenemos que hacer con las armas es añadir 'reloading' al cuchillo. Añade las siguientes variables de clase a Weapon_Knife.gd:

const CAN_RELOAD = false
const CAN_REFILL = false

const RELOADING_ANIM_NAME = ""

Ya que ambos no podemos recargar o rellenar un cuchillo, fijamos ambas constantes en false. También definimos "RELOADING_ANIM_NAME" como una string vacía, ya que el cuchillo no tiene animación de recarga.

Ahora necesitamos añadir reloading_weapon:

func reload_weapon():
    return false

Ya que no podemos recargar un cuchillo, siempre regresamos false.

Añadiendo la recarga al jugador

Ahora necesitamos añadir algunas cosas a Player.gd. Primero necesitamos definir una nueva variable de clase:

var reloading_weapon = false
  • reloading_weapon: Una variable para rastrear si el jugador está tratando de recargar el arma o no.

A continuación, tenemos que añadir otra llamada a la función _physics_process.

Añade process_reloading(delta) a _physics_process. Ahora _physics_process debería verse algo como esto:

func _physics_process(delta):
    process_input(delta)
    process_movement(delta)
    process_changing_weapons(delta)
    process_reloading(delta)
    process_UI(delta)

Ahora tenemos que añadir process_reloading. Añade la siguiente función a 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

Repasemos lo que está pasando aquí.

Primero, comprobamos que el jugador no está tratando de recargar.

Si el jugador está tratando de recargar, entonces obtenemos el arma actual. Si el arma actual no es igual a null, llamamos a su función de reload_weapon.

Nota

Si el arma actual es igual a null, entonces el arma actual está UNARMED.

Finalmente, colocamos reloading_weapon a false porque, independientemente de si el jugador recargó con éxito, hemos intentado recargar y ya no necesitamos seguir intentándolo.


Antes de que podamos dejar que el jugador se recargue, necesitamos cambiar algunas cosas en process_input.

Lo primero que tenemos que cambiar es el código para cambiar las armas. Necesitamos agregar un chequeo adicional (if reloading_weapon == false:) para ver si el jugador está recargando:

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

Esto hace que el jugador no pueda cambiar de armas si está recargando.

Ahora tenemos que añadir el código para activar una recarga cuando el jugador presione la acción de reload. Añade el siguiente código a 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
# ----------------------------------

Repasemos lo que está pasando aquí.

Primero nos aseguramos de que el jugador no esté recargando ya, ni esté tratando de cambiar de armas.

Luego comprobamos si la acción de reload ha sido presionada.

Si el jugador ha presionado reload``$, entonces obtenemos el arma actual y revisamos para asegurarnos de que no es ``null. Luego comprobamos si el arma puede recargarse o no usando su constante CAN_RELOAD.

Si el arma puede recargarse, entonces obtenemos el estado actual de la animación, y hacemos una variable para rastrear si el jugador ya está recargando o no.

Luego revisamos cada arma para asegurarnos de que el jugador no esté ya jugando la animación de recarga de esa arma.

Si el jugador no está recargando ningúna arma, ponemos reloading_weapon a true.


Una cosa que me gusta añadir es que el arma se recargará sola si intentas dispararla y se queda sin munición.

También necesitamos agregar una condición adicional de "if" (is_reloading_weapon == false:) para que el jugador no pueda disparar el arma actual mientras la recarga.

Cambiemos nuestro código de disparo en process_input para que se recargue al intentar disparar un arma vacía:

# ----------------------------------
# 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
# ----------------------------------

Ahora verificamos que el jugador no esté recargando antes de disparar el arma, y cuando tengamos 0 o menos munición en el arma actual, ponemos reloading_weapon a true si el jugador intenta disparar.

Esto hará que el jugador intente recargar cuando intente disparar un arma vacía.


Con eso hecho, ¡el jugador puede ahora recargar! ¡Inténtalo! Ahora puedes disparar toda la munición de sobra para cada arma.

Añadiendo sonidos

Por último, añadamos algunos sonidos que acompañan al jugador disparando, recargando y cambiando armas.

Truco

No hay sonidos de juegos en este tutorial (por razones legales). https://gamesounds.xyz/ es una colección de "música y sonidos de dominio público o libres de derechos de autor adecuados para juegos ". Yo usé Gamemaster's Gun Sound Pack, que se encuentra en Sonniss.com GDC 2017 Game Audio Bundle.

Abre Simple_Audio_Player.tscn. Es simplemente un Spatial con un AudioStreamPlayer como su hijo.

Nota

La razón por la que se llama un 'simple' reproductor de audio es porque no tenemos en cuenta el rendimiento y porque el código está diseñado para proporcionar sonido de la manera más simple posible.

Si quieres usar audio 3D, para que suene como si viniera de un lugar en el espacio 3D, haz clic con el botón derecho del ratón en AudioStreamPlayer y selecciona "Change type".

Esto abrirá el navegador de nodos. Navega a AudioStreamPlayer3D y selecciona "change". En el código fuente de este tutorial, usaremos AudioStreamPlayer, pero puedes usar opcionalmente AudioStreamPlayer3D si lo deseas, y el código proporcionado a continuación funcionará independientemente del que hayas elegido.

Crea un nuevo script y llámalo Simple_Audio_Player.gd. Adjúntalo al Spatial en Simple_Audio_Player.tscn e inserta el siguiente código:

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()

Truco

Al poner position en null por defecto en play_sound, lo hacemos un argumento opcional, lo que significa que position no necesariamente tiene que ser pasado para llamar a play_sound.

Repasemos lo que está pasando aquí:


En _ready, obtenemos la :ref: AudioStreamPlayer y conectamos su señal de finished a la función destroy_self. No importa si es un nodo AudioStreamPlayer o AudioStreamPlayer3D, ya que ambos tienen la señal terminada. Para asegurarnos de que no está reproduciendo ningún sonido, llamamos a stop en el nodo AudioStreamPlayer.

Advertencia

¡Asegúrate de que tus archivos de sonido no estén en bucle! ¡Si está configurado en bucle, los sonidos seguirán reproduciéndose infinitamente y el script no funcionará!

La función play_sound es lo que llamaremos desde Player.gd. Comprobamos si el sonido es uno de los tres posibles, y si es uno de los tres sonidos ponemos el flujo de audio en AudioStreamPlayer al sonido correcto.

Si es un sonido desconocido, imprimimos un mensaje de error en la consola y liberamos el reproductor de audio.

Si estás usando un AudioStreamPlayer3D, quita el # para establecer la posición del nodo de reproducción de audio para que se reproduzca en la posición correcta.

Finalmente, le decimos al AudioStreamPlayer que toque.

Cuando el AudioStreamPlayer termine de reproducir el sonido, se llamará destroy_self porque conectamos la señal finished en ready. Detenemos el AudioStreamPlayer y liberamos el reproductor de audio para ahorrar recursos.

Nota

Este sistema es extremadamente simple y tiene algunas fallas importantes:

Un defecto es que tenemos que pasar un valor de string para reproducir un sonido. Aunque es relativamente simple recordar los nombres de los tres sonidos, puede ser cada vez más complejo cuando se tienen más sonidos. Lo ideal sería colocar estos sonidos en algún tipo de container con variables expuestas para no tener que recordar el nombre(s) de cada efecto de sonido que queramos reproducir.

Otro defecto es que no podemos reproducir efectos de sonido en bucle, ni música de fondo, fácilmente con este sistema. Debido a que no podemos reproducir sonidos en bucle, ciertos efectos, como los sonidos de pasos, son más difíciles de lograr porque entonces tenemos que hacer un seguimiento de si hay o no un efecto de sonido y si necesitamos o no seguir reproduciéndolo.

Una de las mayores fallas de este sistema es que sólo podemos reproducir sonidos de Player.gd. Idealmente nos gustaría poder reproducir sonidos de cualquier script en cualquier momento.


Una vez hecho esto, abramos Player.gd de nuevo. Primero necesitamos cargar el Simple_Audio_Player.tscn. Coloca el siguiente código en la sección de variables de clase del script:

var simple_audio_player = preload("res://Simple_Audio_Player.tscn")

Ahora tenemos que ponerle una instancia al simple reproductor de audio cuando lo necesitemos, y luego llamar a su función play_sound y pasarle el nombre del sonido que queremos reproducir. Para hacer el proceso más simple, vamos a crear una función create_sound en 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)

Repasemos lo que hace esta función:


La primera línea instancia la escena Simple_Audio_Player.tscn y la asigna a una variable llamada audio_clone.

La segunda línea obtiene la raíz de la escena, y esto tiene una gran (aunque segura) suposición.

Primero obtenemos el nodo SceneTree, y luego accedemos al nodo raíz, que en este caso es el Viewport bajo el cual se está ejecutando todo el juego. Entonces obtenemos el primer hijo de Viewport, que en nuestro caso resulta ser el nodo raíz de Test_Area.tscn o cualquiera de los otros niveles proporcionados. Estamos haciendo una gran suposición de que el primer hijo del nodo raíz es la escena raíz bajo la que está el jugador, lo que no siempre puede ser el caso.

Si esto no tiene sentido para ti, no te preocupes demasiado por ello. La segunda línea de código sólo no funciona de forma fiable si tienes varias escenas cargadas como hijos del nodo raíz a la vez, lo que raramente ocurrirá en la mayoría de los proyectos y no ocurrirá en esta serie de tutoriales. Esto es sólo potencialmente un problema dependiendo de cómo manejes la carga de escenas.

La tercera línea añade nuestra escena recién creada Simple_Audio_Player para ser un hijo de la raíz de la escena. Esto funciona exactamente igual que cuando estamos produciendo balas.

Finalmente, llamamos a la función play_sound y pasamos los argumentos pasados a create_sound. Esto llamará a la función play_sound del script Simple_Audio_Player.gd con los argumentos pasados.


Ahora todo lo que queda es tocar los sonidos cuando queramos. ¡Añadamos primero el sonido a la pistola!

Abre Weapon_Pistol.gd.

Ahora, queremos hacer un ruido cuando el jugador dispara la pistola, así que añade lo siguiente al final de la función fire_weapon:

player_node.create_sound("Pistol_shot", self.global_transform.origin)

Ahora, cuando el jugador dispare la pistola, tocaremos el sonido Pistol_shot.

Para hacer un sonido cuando el reproductor se recarga, necesitamos añadir lo siguiente debajo de player_node.animation_manager.set_animation(RELOADING_ANIM_NAME) en la función reload_weapon:

player_node.create_sound("Gun_cock", player_node.camera.global_transform.origin)

Ahora, cuando el reproductor se recargue, tocaremos el sonido Gun_cock.


Ahora añadamos sonidos al rifle. Abre Weapon_Rifle.gd.

Para reproducir los sonidos cuando se dispara el rifle, añada lo siguiente al final de la función fire_weapon:

player_node.create_sound("Rifle_shot", ray.global_transform.origin)

Ahora, cuando el jugador dispare el rifle, tocaremos el sonido Rifle_shot.

Para hacer un sonido cuando el reproductor se recarga, necesitamos añadir lo siguiente debajo de player_node.animation_manager.set_animation(RELOADING_ANIM_NAME) en la función reload_weapon:

player_node.create_sound("Gun_cock", player_node.camera.global_transform.origin)

Ahora, cuando el reproductor se recargue, tocaremos el sonido Gun_cock.

Notas finales

../../../_images/PartThreeFinished.png

¡Ahora tienes armas con munición limitada que reproducen sonidos cuando las disparas!

En este punto, tenemos todos los fundamentos de un juego FPS funcionando. Todavía hay algunas cosas que sería bueno añadir, y vamos a añadirlas en las próximas tres partes!

Por ejemplo, ahora mismo no tenemos forma de añadir munición a nuestros sobrantes, así que al final se nos acabará. Además, no tenemos nada a lo que disparar fuera de los nodos RigidBody.

En Parte 4 añadiremos algunos objetivos a los que disparar, junto con algunas recogidas de salud y munición! También vamos a añadir soporte para joypad, para que podamos jugar con los mandos de la Xbox 360 por cable!

Advertencia

¡Si alguna vez te pierdes, asegúrate de leer el código de nuevo!

Puede descargar el proyecto terminado para esta parte aquí: Godot_FPS_Part_3.zip