0 votes

My current setup looks like this: I have one KinematicBody2D "Player" and multiple other KinematicBody2Ds "Enemy". Both the player and the enemies can shoot "Bullets", which are each an Area2D. The game area consists of a TileMap with solid wall tiles.

Right now, all the bullet does is that it destroys itself once it hits a body with body_enter. But now I want the player and enemies to receive damage if they are hit by a bullet, but I am seriously stuck. I've tried multiple different ways but none have worked.

  1. Use getslidecollision() on my enemies to detect when they have hit a bullet. This doesn't work since this only detects collisions with bodies, not areas
  2. Send out a "hit" signal from a bullet when it hits the player or the enemy. But this would mean that I would need to connect every bullets "hit" signal with every enemy so they can detect it, which seems horribly impractical
  3. Turn my bullets into RigidBody2Ds so I can use getslidecollision() on my enemies. But then the bullet can't detect when it hit's a wall, since a wall is not a RigidBody2D.

It seems like such a simple thing but I really can't wrap my head around this, so I could really use some help here.

in Engine by (15 points)

1 Answer

+2 votes
Best answer

I always do this. I add a hit() function on all the bodies scripts that can take damage.
Then when the area enters a body, i ask in the bullet script:

func on_Area2D_body_entered(body):
    if body.has_method("hit"):
        body.hit()
    queue_free() #or whatever you do to destroy the bullet

Then just connect the body_entered signal of the bullet to that function in the bullet script.
When the bullet hits an body, it will call the body's hit function if present.

To make it easier, you can make your enemies always inherit from a custom Enemy class, so you dont have to code the hit function for every new enemy.

I find this ways is nice, because the bullets makes damage to bodies, so is the bullet responsability to call the hit function of the body, to make damage to them. Not the body responsability to make damage to itself when hitting a bullet.

Also, you can have arguments to the hit fundtion, to specify other things. I usually have at least the damage value and the maker of the damage as parameters, but that's up to you.

by (3,382 points)
selected by

Just implemented it and it works like a charm. Didn't know about "has_method()" since I'm still learning, thank you so much!

Could you please send the entire code? Please!
I'm trying to do this for hours now

;--;

I haven't touched this project in a long while so I might not be able to explain everything, but here's what I did.

My enemies now have this method in their controller script:

func hit():
# do fancy hit stuff (animations, dmg etc.)
$FlashAnimationPlayer.play("flash")
hp -= rng.randi_range(2,4)
if hp <= 0 and alive:
    alive = false
    main_node.decrease_mob_count(1)
    $AnimationPlayer.play("die")
    yield($AnimationPlayer, "animation_finished")
    queue_free()

And my bullets correspondingly have this:

func _on_Bullet_body_entered(body):
if body.has_method("hit"):
    body.hit()
# other stuff below
$AnimationPlayer.play("explode")
alive = false
yield($AnimationPlayer, "animation_finished")
queue_free()

To connect the signals, I use this in my bullet controller script:

func _ready():
connect("body_entered", self, "_on_Bullet_body_entered") <-- this!
get_node("VisibilityNotifier2D").connect("screen_exited", self, "_on_screen_exited")

Hope this helps!

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.