Cannot spawn multiple bullet

Godot Version

4.2

Question

I’m trying to spawn a fireball every 3 second. But if the player level up, it will shoot 2 fireball instead. I did that with the await code but I heard that it is not a good practice.

func shoot_fireball(times_to_shoot: int = 1):
 for i in range(times_to_shoot):
  var new_fireball = FIRE_BALL.instantiate()
  new_fireball.global_position = %shooting_fireball_point.global_position
  %shooting_fireball_point.add_child(new_fireball)
  $fireBall.play()
  await  get_tree().create_timer(0.2).timeout
 
func _on_fireball_timer_timeout():
 if fireball1:
  shoot_fireball(1)

 if fireball2:
  shoot_fireball(2)

 if fireball3:
  shoot_fireball(3)

so I modify my code but now it only shoot 1 all the time instead.

func shoot_fireball():
		var new_fireball = FIRE_BALL.instantiate()
		new_fireball.global_position = %shooting_fireball_point.global_position
		%shooting_fireball_point.add_child(new_fireball)
		$fireBall.play()
		$fireball_between_timer.start()
		
func _on_fireball_timer_timeout():
	if fireball1 and $fireball_between_timer.is_stopped():
		shoot_fireball()

	if fireball2 and $fireball_between_timer.is_stopped():
		for i in range(2):
			shoot_fireball()

	if fireball3 and $fireball_between_timer.is_stopped():
		for i in range(2):
			shoot_fireball()

how to fix this?

I think I need more context to understand your problem.

What do you mean by

But if the player level up, it will shoot 2 fireball instead.

Also, what is fireball1, fireball2, and fireball3 and how are they set in your code?

I’ve created a minimal project to try to reproduce your issue, but I’m not seeing a problem of only shooting once. Here’s how I structured mine:

image

fireball_spawner.gd

extends Node2D

@export var fireball : PackedScene


func _on_shoot_timer_timeout() -> void:
	var rng = RandomNumberGenerator.new()
	var num = rng.randi_range(1,3)
	print(num)
	shoot_fireball(num)

func shoot_fireball(times_to_shoot : int):
	for i in range(times_to_shoot):
		var new_fireball = fireball.instantiate()
		new_fireball.global_position = %ShootingPoint.global_position
		%ShootingPoint.add_child(new_fireball)
		%ShootTimer.start()

image

fireball.gd

extends Node2D

var y_dir
var rng = RandomNumberGenerator.new()

# Called when the node enters the scene tree for the first time.
func _ready() -> void:
	y_dir = rng.randf_range(-45.0,45.0)
	print(y_dir)

# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
	position.x += 300 * delta
	position.y += y_dir * delta

And here’s my output.

Thank you, GodofGrunts, for taking the time to answer my question. I’m new to Godot, but I’ll do my best to follow your code. In the meantime, I’ll provide more context.

This is my fireball.gd code

extends Area2D

var travelled_distance = 0
var target_enemy = null

const SPEED = 150
const RANGE = 1000

func _process(delta):
	var direction = Vector2.RIGHT.rotated(rotation)
	position += direction * SPEED * delta

 # If there is a target enemy, home towards it
	if target_enemy and target_enemy != null:
		look_at(target_enemy.global_position)
	#else:
		#target_enemy = find_closest_enemy()
	travelled_distance += SPEED * delta
	if travelled_distance > RANGE:
		queue_free()

func _ready():
	target_enemy = find_closest_enemy()
 
func find_closest_enemy():
	var enemies = get_tree().get_nodes_in_group('Enemies')
	var closest_enemy = null
	var min_distance = INF
	for enemy in enemies:
		var distance = global_position.distance_squared_to(enemy.global_position)
		if distance < min_distance:
			min_distance = distance
			closest_enemy = enemy
	return closest_enemy

func _on_body_entered(body):
	queue_free()
	if body.has_method("take_damage"):
		body.take_damage(1)

Nothing special, I tried to implement the steering homing fireball, but it doesn’t work, so I’ve left it as is for now. Below is my player.gd script that I use for fireball spawning.

extends CharacterBody2D
#fireball
const FIRE_BALL = preload("res://scenes/fire_ball.tscn")
var fireball1: bool = false
var fireball2: bool = false
var fireball3: bool = false
var fireball4: bool = false
var fireball5: bool = false

func shoot_fireball(times_to_shoot: int = 1):
	for i in range(times_to_shoot):
		var new_fireball = FIRE_BALL.instantiate()
		new_fireball.global_position = %shooting_fireball_point.global_position
		%shooting_fireball_point.add_child(new_fireball)
		$fireBall.play()
		await  get_tree().create_timer(0.2).timeout
	
func _on_fireball_timer_timeout():
	if fireball1:
		shoot_fireball(1)
	if fireball2:
		shoot_fireball(2)
	if fireball3:
		shoot_fireball(3)
	if fireball4:
		shoot_fireball(4)
	if fireball5:
		shoot_fireball(5)

func update_exp(amount: int):
	$expCollect.play()
	currentExp += amount
	if currentExp >= maxExp:
		$levelUp.play()
		currentExp = 0
		maxExp += 5
		print("lvl up")
		levelUp.emit()
	expChanged.emit()

After the player levels up and chooses to pick the fireball skill again, fireball2 will be set to true. The function will then shoot 2 fireballs instead of just 1 when the fireball timer times out. (I’ve set the fireball timer to 3 seconds, so it will automatically fire fireballs every 3 seconds). I’ve set the fireball_between_timer to 0.5 because I believe the issue lies in the for loop. It may be spawning 2 or 3 fireballs at the same time, causing only one fireball to be visible in the game.