Problem with LeftWall and RightWall for my Shoot Em Up

Hi I’m creating a shoot em up on Godot 4.3. It’s a shoot em up a bit like Space Invader with the difference that the ships never go down. I put an Enemies node for the formation of the enemies which are composed of 15 ships in 3 rows and 5 columns. I added 2 Area2D which are LeftWall and RightWall with their CollissionShape2D. I encounter a problem however. For example when I eliminate the ships for example from the 5th column sometimes the ships return to the left before touching RightWall as if the ships of the 5th column were still present. Here is a short video to show the problem I encounter.

Here is the code that handles ship formation.

extends Node2D

@export var enemyun_scene: PackedScene = preload(“res://ennemyun.tscn”)
@export var ennemydeux_scene: PackedScene = preload(“res://ennemydeux.tscn”)

var rows: int = 3
var cols: int = 5

@export var start_position: Vector2 = Vector2(150, 100)
@export var spacing: Vector2 = Vector2(150, 120)

var columns: Array =

@export var enemy_speed: float = 100.0
var direction: int = -1

func _ready() → void:
columns.resize(cols)
for i in range(cols):
columns[i] =

for row in range(rows):
	for col in range(cols):
		var enemy_instance: Node
		
		if row == 1:
			enemy_instance = ennemydeux_scene.instantiate()
		else:
			enemy_instance = enemyun_scene.instantiate()
		
		enemy_instance.position = start_position + Vector2(col * spacing.x, row * spacing.y)
		add_child(enemy_instance)
		columns[col].append(enemy_instance)


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


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é !")

func _physics_process(delta: float) → void:

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

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)

Godot Version

Replace this line with your Godot version

Question

Ask your question here! Try to give as many details as possible

I take it both the player’s ship and the enemies inherit from Area2D? If so, if the player makes contact with the left wall, it will update variable direction. You can see this in the video. When the player touches the right wall while the enemies are moving right, they suddenly change direction.

You need to update the code to make sure the walls ignore the player’s ship.

(Also, please don’t post your code as plain text. Please post it in markdown format by enclosing your code with three backticks (i.e. ```).)

The player ship (Player) is related to the root node (Main), as is the node that instantiates the ship formation (Enemies) and the two Area2Ds (LeftWall and RightWall) that prevent enemy ships from leaving the screen. The player manages their own collisions using their own script to prevent them from leaving the screen.

I’ll still try to modify the script based on your feedback.

Yes, but what do both the player and enemies inherit from? If they both ultimately derive from Area2D, then if the player touches a wall it will influence the direction the enemies move in. Ownership of the player scene isn’t relevant.

I found the solution!

Indeed, my player ship was touching LeftWall and RightWall, and as in the script, the func _on_left_wall_entered and func _on_left_wall_entered functions cause the enemy ships to move in one direction, either left or right, at the slightest touch of an object.

I simply adjusted the CollisionShape2D of LeftWall and RightWall to prevent them from coming into contact with the player but still allow contact with the enemy ship formation.

Congratulations on finding a solution :slight_smile:

Another one would be to create a custom method in your player and enemy classes that varies in behavior. Something like this:

class_name Player
extends Ship

func set_left_direction() -> void: 
  # do nothing
  pass

class_name Enemy
extends Ship
class_name Ship
extends Area2D

# Default behavior
func set_left_direction() -> void: 
  direction = 1

func _on_left_wall_entered(_area: Ship) → void:
    if direction < 0:
	    print("Collision avec LeftWall – inversion vers la droite.")
	    set_left_direction()