Search code examples
game-enginegame-physicsgame-developmentgodotgdscript

Detecting if an enemy was shot by bullet


I am trying to learn Godot by making a simple 2D shooter, but am running into a problem. I do not know how to detect if an enemy has been shot by a bullet. My bullets are Area2D nodes with a Sprite, and CollisionShape2D node attached. My enemies are KinematicBody2D nodes with a Sprite, and CollisionShape2D node attached. The enemy also has a timer attached, but that is not important.

Here is the enemy code:

extends KinematicBody2D

var speed = 200
var velocity = Vector2(-0.5, 1)
var bullet_preload = preload("res://Enemy/EnemyBullet.tscn")

func _ready():
    randomize()
    velocity.y = [1, -1][randi() % 2]

func _physics_process(delta):
    var collision = move_and_collide(velocity * speed * delta)
    if collision:
        velocity = velocity.bounce(collision.normal)
    if position.x < -20:
        print("Enemy Despawned")
        queue_free()
    if $ShootDelay.time_left == 0:
        var bullet = bullet_preload.instance()
        bullet.position.x = position.x - 18
        bullet.position.y = position.y + 6
        get_parent().add_child(bullet)
        $ShootDelay.start()

Here is my player bullet code:

extends Area2D

var speed = 600
var velocity = Vector2(1, 0)

func _process(delta):
    position.x += speed * delta
    if position.x > 1044:
        queue_free()

func _on_PlayerBullet_body_entered(body):
    queue_free()

It may be important to note that the player bullet and enemy scenes are not in the same scene tree, and there is no way (that I know of) of sending a signal between them. They are both being instanced to my main root node that contains the entire game via code.

If I need to include other details, please let me know.


Solution

  • On your _on_PlayerBullet_body_entered you a body that has a reference to whatever the Area2D collided with, you can use it to communicate with it.

    For example, you can call a method on it. Which could, of course could be queue_free:

    func _on_PlayerBullet_body_entered(body):
        body.queue_free()
        queue_free()
    

    However, I'm going to suggest against it. Instead let us come up with a method that you can implement to have whatever behavior you want:

    func _on_PlayerBullet_body_entered(body):
        body.shot()
        queue_free()
    

    Of course, we have the problem that we are not sure if it has that method… So, let us check for it:

    func _on_PlayerBullet_body_entered(body):
        if body.has_method(shot):
            body.shot()
    
        queue_free()
    

    Then you would add that method on the objects that react to the bullet impact. Back to queue_free, it could be:

    func shot() -> void:
        queue_free()
    

    Or you could have it be something like this:

    var health := 5
    
    func shot() -> void:
        health -= 1
        if health <= 0:
            queue_free()
    

    Or, you could have it set a flag and deal with it on _physics_process or where and whenever appropiate:

    var was_shot := 5
    
    func _physics_process(_delta) -> void:
        if was_shot:
            queue_free()
    
    func shot() -> void:
        was_shot = true
    

    Of course, aside form queue_free you may want to instance some other scene (e.g. a corpse or an explosion) or play an animation or something like that.