How to do hit detection with an Area2D bullet and KinematicBody2D characters in a TileMap?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By s1m0n-w

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 get_slide_collision() 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 get_slide_collision() 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.

:bust_in_silhouette: Reply From: p7f

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.

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

s1m0n-w | 2020-09-13 09:48

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

;–;

SamRedLing | 2021-06-08 00:00

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!

s1m0n-w | 2021-06-08 10:11