Tutorial de Godot – Parte 15: Agregar sonidos y música usando nodos AudioStream

Los sonidos envolventes del juego y la música de fondo juegan un papel muy importante para determinar el éxito o el fracaso de cualquier juego. En este tutorial, aprenderemos cómo agregar sonidos y música a nuestro juego usando la familia de nodos AudioStream .

El sistema de audio de Godot

Para reproducir sonidos, el sistema de audio de Godot utiliza elementos llamados buses de audio . Un bus de audio es un «lugar» a través del cual se canaliza el audio. Un bus puede modificar los datos de audio aplicando uno o más procesadores de efectos y luego enrutarlos a otro bus.

Los buses de audio se pueden encontrar en el panel inferior del editor de Godot:

De forma predeterminada, solo hay un bus de audio, el bus maestro (siempre es el que está más a la izquierda). Este es el bus que envía el audio a sus altavoces.

Los otros buses de audio se pueden enrutar de manera flexible a otros buses. El enrutamiento solo puede pasar de los autobuses a la derecha a los autobuses más a la izquierda, para evitar bucles infinitos.

Al usar un nodo para la reproducción de audio, debe elegir a qué bus enviar el audio (por defecto, es el bus maestro). Para nuestro juego simple, solo usaremos el bus maestro.

La interfaz de audio de Godot utiliza principalmente la escala de decibelios, la misma que utilizan los profesionales del audio. La escala de decibelios (dB) es una escala relativa (logarítmica). Por cada 6 dB, la amplitud del sonido se duplica o se reduce a la mitad.

Importar sonidos

Haga clic en el botón de abajo para descargar todos los activos de sonido que necesitamos para el juego.

Descargar “SimpleRPG Sonidos y Músicas”music_sounds.zip – 3 MB

Contiene música ©2019 Joshua McLean ( mrjoshuamclean.com )
Con licencia de Creative Commons Attribution 4.0 International

En el panel Sistema de archivos , cree una nueva carpeta llamada Sonidos e importe en ella todos los archivos de audio descargados.

Música de fondo

En la escena principal del juego, agregue un nodo AudioStreamPlayer a Player y cámbiele el nombre a Music .

La familia de nodos AudioStream se utiliza para enviar sonidos a los buses de audio. AudioStreamPlayer es el reproductor de flujo estándar no posicional . El flujo de sonido puede provenir de muchos lugares, pero normalmente se carga desde el sistema de archivos. Godot admite archivos de audio WAV (.wav) y Ogg Vorbis (.ogg).

Arrastre el archivo mountain-trials.ogg a la propiedad Stream en el Inspector , luego active la reproducción automática para reproducir la música cuando comience el juego. Puede ajustar el volumen de la música a su gusto utilizando la propiedad Volumen Db . Para la música de fondo, lo configuré a -6 Db. Finalmente, configure Pausa → Modo en Proceso , para que la música no deje de reproducirse cuando el juego está en pausa.

La música se reproducirá en bucle porque, de forma predeterminada, los archivos Ogg Vorbis se importan con la propiedad Loop activada.

Agregue otro AudioStreamPlayer a Player y cámbiele el nombre a MusicGameOver y arrastre el archivo night-chip.ogg a la propiedad Stream en el Inspector . Usaremos este nodo para jugar el juego con música cuando el jugador muera.

Ahora abra el script Player.gd y edite la función hit() para detener la música de fondo y reproducir el juego sobre la música:

func hit(damage):
	health -= damage
	emit_signal("player_stats_changed", self)
	if health <= 0:
		set_process(false)
		$AnimationPlayer.play("Game Over")
		$Music.stop()
		$MusicGameOver.play()
	else:
		$AnimationPlayer.play("Hit")

Detener y reproducir un sonido es muy simple: simplemente llame a las funciones stop() y play() de AudioStreamPlayer .

Esto es lo que sucederá cuando el jugador muera:

sonido de ataque

Agreguemos los efectos de sonido a partir del ataque del jugador. Agregue un nodo AudioStreamPlayer2D a Player y cámbiele el nombre a SoundAttack .

AudioStreamPlayer2D es una variante de AudioStreamPlayer que emite sonido en un entorno posicional 2D . Cuando el nodo está cerca de la izquierda de la pantalla, la panorámica irá a la izquierda. Cuando esté cerca del lado derecho, irá a la derecha.

Arrastre el archivo attack.wav a la propiedad Stream en el Inspector y configure Volume Db en 6. A diferencia de los archivos .ogg, los archivos .wav no se repiten de forma predeterminada.

Abra el script Player.gd y vaya a la función _input() . En el código que maneja la acción de entrada del ataque , debemos agregar el código para reproducir el sonido, inmediatamente después del código que reproduce la animación del ataque :

if event.is_action_pressed("attack"):
	# Check if player can attack
	var now = OS.get_ticks_msec()
	if now >= next_attack_time:
		# What's the target?
		var target = $RayCast2D.get_collider()
		if target != null:
			if target.name.find("Skeleton") >= 0:
				# Skeleton hit!
				target.hit(attack_damage)
			if target.is_in_group("NPCs"):
				# Talk to NPC
				target.talk()
				return
		# Play attack animation
		attack_playing = true
		var animation = get_animation_direction(last_direction) + "_attack"
		$Sprite.play(animation)
		# Play attack sound
		$SoundAttack.play()
		# Add cooldown time to current time
		next_attack_time = now + attack_cooldown_time

Ahora abra la escena Skeleton.tscn y agregue un AudioStreamPlayer2D al nodo Skeleton . Renómbrelo como SoundAttack . Arrastre el archivo attack.wav a la propiedad Stream en el Inspector y configure Volume Db en 6.

Abra el script Skeleton.gd e inserte en la función _on_AnimatedSprite_frame_changed() el código para reproducir el sonido del ataque:

func _on_AnimatedSprite_frame_changed():
	if $AnimatedSprite.animation.ends_with("_attack") and $AnimatedSprite.frame == 1:
		var target = $RayCast2D.get_collider()
		if target != null and target.name == "Player" and player.health > 0:
			player.hit(attack_damage)
		# Play attack sound
		$SoundAttack.play()

Dado que la animación de ataque del esqueleto es más lenta, el sonido se reproduce en el cuadro 1 de la animación para una mejor sincronización de audio/video.

muerte esqueleto

Agregue otro AudioStreamPlayer2D al nodo Skeleton y cámbiele el nombre a SoundDeath . Arrastre el archivo death.wav a la propiedad Stream y configure Volume Db en 6.

En el script Skeleton.gd , edite la función hit() para reproducir el sonido de muerte cuando el esqueleto muera:

func hit(damage):
	health -= damage
	if health > 0:
		$AnimationPlayer.play("Hit")
	else:
		$Timer.stop()
		direction = Vector2.ZERO
		set_process(false)
		other_animation_playing = true
		$AnimatedSprite.play("death")
		emit_signal("death")
		# Play death sound
		$SoundDeath.play()
		# 80% probability to drop a potion on death
		if rng.randf() <= 0.8:
			var potion = potion_scene.instance()
			potion.type = rng.randi() % 2
			get_tree().root.get_node("Root").add_child(potion)
			potion.position = position
		# Add XP to player
		player.add_xp(25)

Este es el resultado después de agregar los sonidos de ataque y muerte:

sonidos de bolas de fuego

Vuelva a la escena principal, agregue un nodo AudioStreamPlayer2D a Player y cámbiele el nombre a SoundFireball . Arrastre el archivo fireball.wav a la propiedad Stream y configure Volume Db en 6.

Abra el script Player.gd y vaya a la función _input() . En el código que maneja la acción de entrada de la bola de fuego , agregue el código para reproducir el sonido de la bola de fuego, inmediatamente después del código que reproduce la animación de la bola de fuego:

elif event.is_action_pressed("fireball"):
	var now = OS.get_ticks_msec()
	if mana >= 25 and now >= next_fireball_time:
		# Update mana
		mana = mana - 25
		emit_signal("player_stats_changed", self)
		# Play fireball animation
		attack_playing = true
		var animation = get_animation_direction(last_direction) + "_fireball"
		$Sprite.play(animation)
		# Play fireball sound
		$SoundFireball.play()
		# Add cooldown time to current time
		next_fireball_time = now + fireball_cooldown_time

Ahora, abra la escena Fireball.tscn y agregue un AudioStreamPlayer2D al nodo Fireball . Renómbrelo SoundExplosion . Arrastre el archivo explosion.wav a la propiedad Stream y configure Volume Db en 6.

Vaya al script Fireball.gd . Al final de la función _on_Fireball_body_entered() agregue este código:

# Play explosion sound
$SoundExplosion.play()

Recoger objetos y usar pociones.

Regrese a la escena principal y agregue otro AudioStreamPlayer2D a Player . Renómbrelo SoundObject . Arrastre el archivo object.wav a la propiedad Stream y establezca Volume Db en 6.

Ahora abra el script Player.gd y en la función _input() edite el código que maneja la poción bebiendo así:

elif event.is_action_pressed("drink_health"):
	if health_potions > 0:
		health_potions = health_potions - 1
		health = min(health + 50, health_max)
		emit_signal("player_stats_changed", self)
		# Play sound
		$SoundObject.play()
elif event.is_action_pressed("drink_mana"):
	if mana_potions > 0:
		mana_potions = mana_potions - 1
		mana = min(mana + 50, mana_max)
		emit_signal("player_stats_changed", self)
		# Play sound
		$SoundObject.play()

Luego, agregue al final de la función add_potion() el código para reproducir el sonido cuando se agrega una poción al inventario:

# Play sound
$SoundObject.play()

Queremos reproducir el mismo sonido cuando recojamos el collar necesario para completar la búsqueda de Fiona . Veamos una forma diferente de manejar las reproducciones de este sonido.

Abra la escena Necklace.tscn . Agregue un AudioStreamPlayer2D al nodo Collar y cámbiele el nombre a SoundObject . Arrastre object.wav a la propiedad Stream y establezca Volume Db en 6.

Ahora, ve al script Necklace.gd .

Lo más obvio que podemos hacer es agregar $SoundObject.play() a la función _on_Necklace_body_entered() para reproducir el sonido. Pero si intenta hacer esto, ¡el sonido no se reproducirá!

Esto sucede porque al final de esta función, el nodo Collar se elimina del árbol de la escena. Como resultado, el nodo SoundObject también se elimina y no reproduce el sonido. Por lo tanto, tendremos que conectarnos a la señal de finalización () del nodo SoundObject para eliminar el collar solo después de que el sonido haya terminado de reproducirse.

Conecte la señal finish () de SoundObject a Necklace y mueva la llamada queue_delete() a la función _on_SoundObject_finished() :

func _on_SoundObject_finished():
	get_tree().queue_delete(self)

Finalmente, edite la función _on_Necklace_body_entered() para reproducir el sonido:

func _on_Necklace_body_entered(body):
	if body.name == "Player":
		$SoundObject.play()
		hide()
		fiona.necklace_found = true

Ahora que el collar no se elimina de inmediato, permanecerá visible hasta que finalice el sonido. Para resolver este problema, lo hacemos invisible llamando a la función hide() .

Sube de nivel el sonido

Ahora solo nos queda añadir un último sonido: el sonido que nos avisa del avance de nivel del jugador.

Vuelva a la escena principal y agregue un AudioStreamPlayer al nodo LevelPopup . Renómbrelo SoundLevelUp . Arrastre level_up.wav a la propiedad Stream y establezca Volume Db en 6.

Abra el script LevelPopup.gd y al final de la función _on_Player_player_level_up() agregue este código:

# Play level up sound
$SoundLevelUp.play()

Conclusiones

En este sencillo tutorial aprendimos los conceptos básicos del sistema de audio de Godot y cómo agregar música y sonidos a nuestro juego usando los nodos de la familia AudioStream .