Hi, I’m using Godot Version 4.3 for my shoot-em-up, but I’m having another problem. I added an explosion system to my ships, and when all the ships are destroyed, it should display the label “CompletedLevelLabel.” This stops the AudioStreamPlayer Level1BackGroundMusic and plays the AudioStreamPlayer LevelCompletedOST, but when all the ships are destroyed, nothing happens.
Here is the video that illustrates the problem.
Here is the code for forming and destroying ships.
extends Node2D
# --- Instanciation de la formation ---
@export var enemyun_scene: PackedScene = preload("res://ennemyun.tscn")
@export var ennemydeux_scene: PackedScene = preload("res://ennemydeux.tscn")
# Formation : 3 rangées et 5 colonnes
var rows: int = 3
var cols: int = 5
# Position de départ (locale) et espacement entre les ennemis
@export var start_position: Vector2 = Vector2(150, 100)
@export var spacing: Vector2 = Vector2(150, 120)
# Tableau stockant, pour chaque colonne, la liste des ennemis instanciés
var columns: Array = []
# --- Paramètres de déplacement de la formation ---
@export var enemy_speed: float = 100.0 # Vitesse horizontale de la formation
var direction: int = -1 # -1 pour aller vers la gauche, 1 pour aller vers la droite
func _ready() -> void:
# Initialisation du tableau des colonnes
columns.resize(cols)
for i in range(cols):
columns[i] = []
# Instanciation des ennemis dans une formation 3 × 5
for row in range(rows):
for col in range(cols):
var enemy_instance: Node
# Pour la rangée du milieu (row == 1), on utilise ennemydeux, sinon enemyun.
if row == 1:
enemy_instance = ennemydeux_scene.instantiate()
else:
enemy_instance = enemyun_scene.instantiate()
# Positionnement en coordonnées locales
enemy_instance.position = start_position + Vector2(col * spacing.x, row * spacing.y)
add_child(enemy_instance)
columns[col].append(enemy_instance)
# Connexion des timers pour le tir par colonne
for col in range(cols):
var t: Timer = Timer.new()
t.wait_time = float(col + 1)
t.one_shot = false
t.autostart = true
add_child(t)
match col:
0: t.connect("timeout", Callable(self, "_on_column0_timeout"))
1: t.connect("timeout", Callable(self, "_on_column1_timeout"))
2: t.connect("timeout", Callable(self, "_on_column2_timeout"))
3: t.connect("timeout", Callable(self, "_on_column3_timeout"))
4: t.connect("timeout", Callable(self, "_on_column4_timeout"))
# Connexion aux signaux des murs (adaptés à votre arborescence)
var left_wall = get_node("../LeftWall")
var right_wall = get_node("../RightWall")
if left_wall:
var left_callable = Callable(self, "_on_left_wall_entered")
if left_wall.is_connected("area_entered", left_callable):
left_wall.disconnect("area_entered", left_callable)
left_wall.connect("area_entered", left_callable)
else:
push_error("LeftWall non trouvé !")
if right_wall:
var right_callable = Callable(self, "_on_right_wall_entered")
if right_wall.is_connected("area_entered", right_callable):
right_wall.disconnect("area_entered", right_callable)
right_wall.connect("area_entered", right_callable)
else:
push_error("RightWall non trouvé !")
# --- Gestion de fin de niveau ---
# Connecte le signal "node_removed" pour surveiller la disparition des ennemis.
get_tree().connect("node_removed", Callable(self, "_on_node_removed"))
# On s'attend à ce que dans la scène se trouvent les nœuds suivants :
# - CompletedLevelLabel (Label) : caché au départ.
# - Level1BackGroundMusic (AudioStreamPlayer).
# - LevelCompletedOST (AudioStreamPlayer).
func _physics_process(delta: float) -> void:
# Déplacement simple du conteneur de la formation
position.x += enemy_speed * direction * delta
func _on_left_wall_entered(_area: Area2D) -> void:
if direction < 0:
print("Collision avec LeftWall – inversion vers la droite.")
direction = 1
func _on_right_wall_entered(_area: Area2D) -> void:
if direction > 0:
print("Collision avec RightWall – inversion vers la gauche.")
direction = -1
func fire_from_column(col: int) -> void:
var col_enemies = columns[col]
var bottom_enemy = null
# Recherche de l'ennemi le plus bas dans la colonne
for enemy in col_enemies:
if not is_instance_valid(enemy):
continue
if bottom_enemy == null or enemy.position.y > bottom_enemy.position.y:
bottom_enemy = enemy
if bottom_enemy != null:
print("Colonne", col, "-> bottom enemy:", bottom_enemy.name)
if bottom_enemy.has_method("fire_laser"):
bottom_enemy.fire_laser()
else:
print("L'ennemi", bottom_enemy.name, "n'a pas la méthode fire_laser()")
else:
print("Aucun ennemi valide dans la colonne", col)
func _on_column0_timeout() -> void:
fire_from_column(0)
func _on_column1_timeout() -> void:
fire_from_column(1)
func _on_column2_timeout() -> void:
fire_from_column(2)
func _on_column3_timeout() -> void:
fire_from_column(3)
func _on_column4_timeout() -> void:
fire_from_column(4)
# --- Gestion de fin de niveau ---
# Appelé quand un nœud est retiré de l'arbre.
func _on_node_removed(node: Node) -> void:
if node.is_in_group("enemy"):
var remaining = get_tree().get_nodes_in_group("enemy")
if remaining.size() == 0:
_all_enemies_destroyed()
func _all_enemies_destroyed() -> void:
# Tous les ennemis ont disparu : affiche le label, arrête le BGM, lance l'OST de fin, et passe au niveau 2.
var label = get_node("CompletedLevelLabel")
var bgm = get_node("Level1BackGroundMusic")
var completed_ost = get_node("LevelCompletedOST")
label.visible = true
bgm.stop()
completed_ost.play()
if not completed_ost.is_connected("finished", Callable(self, "_on_level_completed_finished")):
completed_ost.connect("finished", Callable(self, "_on_level_completed_finished"))
func _on_level_completed_finished() -> void:
get_tree().change_scene_to_file("res://level2.tscn")