I'm making an "Asteroids" clone in Godot 3.2.3

the way that the Bullet is spawned in-front of the Player ship is simple : the main game node will find the Player node's position and rotation and then instance the bullet in-front of the Player

func shooting():
var new_bullet = bullet.instance()
add_child(new_bullet)
new_bullet.position = $Player.position
new_bullet.rotation = $Player.rotation
$laser_sound.play()

works fine in game but a game crashing bug occurs if the player is pressing the "shoot" button right when they get hit by an Asteroid and i get this error

"Invalid get index 'position' (on base: 'null instance')." and it's pointing at new_bullet.position = $Player.position

so yeah what's happening is that the game is trying to find the player's position in order to spawn a new bullet but the player has just been removed, so the game just crashes since "Player" no longer exists

this is how i handle the Player dying

func _on_Player_area_entered(area):
emit_signal("player_killed")
queue_free()

i thought an easy solution would be to simply take away the Player's ability to press the "shoot" button and therefor not spawn a bullet right before the player is removed from the game, but none of the methods for disabling player input are actually working as intended...

func _on_Player_area_entered(area):
emit_signal("player_killed")
get_tree().get_root().set_disable_input(true) #i tried putting this here but it didn't do anything
set_process_unhandled_input(false) #and this one did nothing either...
queue_free()

how can i resolve this issue ? and tell me if you have any other method for resolving it

thank you !!!

  • Jesusemora replied to this.
  • buzzbuzz20xx your problem is you are not using a state machine.
    a state machine is simple, you have a variable that represents the state of the object, in this case 0 would be player can move and shoot, and 1 would be player is dead.

    var state : int = 0

    and then when pressing the button you have to check if the state is 0, otherwise do nothing

    if state == 0:
         fire()
    func input_event(event):
         if state == 0:
              if event.is_action_pressed("Fire"):
                   #etc etc etc

    so when the player dies, the state has to be set to 1.

    there are other problems with your code, for some reason you are making the game spawn shots from OUTSIDE the player. I would instead put a function in the player that when executed does the shooting.

    you should also always check if a reference exists before using it, you can do it this like:

    if player:
         #do something

    or

    if player != null:
         #do something

    also don't overuse signals, they are good but for some reason tutorials LOVE using signals for everything, even when signals are redundant. Then something like this happens.
    when using signals you have to be very careful.

    func shooting():
    var new_bullet = bullet.instance()
    add_child(new_bullet)
    new_bullet.position = $Player.position
    new_bullet.rotation = $Player.rotation
    $laser_sound.play()

    no. put your player in a reference.

    @onready var player = $Player
    @onready var laser_sound = $laser_sound
    
    func shoothing():
         if player:
              var new_bullet = bullet.instance()
              add_child(new_bullet)
              new_bullet.position = player.position
              new_bullet.rotation = player.rotation
              laser_sound.play()

    func _on_Player_area_entered(area):
    emit_signal("player_killed")
    get_tree().get_root().set_disable_input(true) #i tried putting this here but it didn't do anything
    set_process_unhandled_input(false) #and this one did nothing either...
    queue_free()

    no. you are already emitting a player_killed signal, just change the state when the signal is received by the input node.
    In the input, check if the state is 0 before executing any code.
    If the player_killed signal is received, change the state to 1.

    buzzbuzz20xx your problem is you are not using a state machine.
    a state machine is simple, you have a variable that represents the state of the object, in this case 0 would be player can move and shoot, and 1 would be player is dead.

    var state : int = 0

    and then when pressing the button you have to check if the state is 0, otherwise do nothing

    if state == 0:
         fire()
    func input_event(event):
         if state == 0:
              if event.is_action_pressed("Fire"):
                   #etc etc etc

    so when the player dies, the state has to be set to 1.

    there are other problems with your code, for some reason you are making the game spawn shots from OUTSIDE the player. I would instead put a function in the player that when executed does the shooting.

    you should also always check if a reference exists before using it, you can do it this like:

    if player:
         #do something

    or

    if player != null:
         #do something

    also don't overuse signals, they are good but for some reason tutorials LOVE using signals for everything, even when signals are redundant. Then something like this happens.
    when using signals you have to be very careful.

    func shooting():
    var new_bullet = bullet.instance()
    add_child(new_bullet)
    new_bullet.position = $Player.position
    new_bullet.rotation = $Player.rotation
    $laser_sound.play()

    no. put your player in a reference.

    @onready var player = $Player
    @onready var laser_sound = $laser_sound
    
    func shoothing():
         if player:
              var new_bullet = bullet.instance()
              add_child(new_bullet)
              new_bullet.position = player.position
              new_bullet.rotation = player.rotation
              laser_sound.play()

    func _on_Player_area_entered(area):
    emit_signal("player_killed")
    get_tree().get_root().set_disable_input(true) #i tried putting this here but it didn't do anything
    set_process_unhandled_input(false) #and this one did nothing either...
    queue_free()

    no. you are already emitting a player_killed signal, just change the state when the signal is received by the input node.
    In the input, check if the state is 0 before executing any code.
    If the player_killed signal is received, change the state to 1.

      thank you very much for this very thorough answer !

      solved the issue, i simply just added a line that checked if the Player isn't null before creating a new bullet

      Jesusemora you should also always check if a reference exists before using it, you can do it this like:
      if $player != null:
      #do something

      2 months later

      Jesusemora Hey Jususemora! I'm curious why didn't you use the "match" instead of "if" statement?
      is it real that "match" costs more performance?

        GorEldeen why didn't you use the "match" instead of "if" statement?
        is it real that "match" costs more performance?

        that's a rookie mistake, you should not worry about performance until you have something working, that comes later when cleaning the code and improving it.
        but also, if match is anything like switch in other languages, you only ever get better performance if there's like 10 cases, and even then the performance difference is too small to matter.
        the most important things to optimize are physics and graphics, THEN code, and code only affects performance when it's executed wrong, like every frame, or with a very large recursion, or a very complex algorithm. a modern computer can run any code in nano-seconds.

          a month later

          Jesusemora Ah I see, sorry I just love always picking the best practices to avoid learning more stuff in the future, the hardest part about writing code is getting motivation to change it in the future, thanks for the reply tho!