(2D) Checking if an object is on top

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

I have a Super Mario mechanic where you jump on the heads of certain enemies to kill them. I have an ok system that, when an enemy and the player collide, checks if the player is currently falling and is not on the ground.

# When coming into contact with something, check if Player
func _on_Area2D_body_entered(body):
if body.get_name() == "Player":
	# If Player is falling
	if body.motion.y > 0 and body.is_on_floor() == false:
		# Deduct health from self equal to Player's damage
		hp -= body.dmg
		# Make the player bounce
		body.motion.y = -body.bounceHeight
		# If health is less than or equal to 0, die
		if hp <= 0:
			body.getPoints(points)
			queue_free()
	else:
		body.takeDamage(dmg)

It works, but I’d like a more precise way to check if the player is actually on top of the enemy, as now you can be just a pixel off of the ground and if you touch the enemy it counts as a hit and the enemy dies.

Thanks for reading!

Could you check to see whether the height of the player’s jump is almost equal to the enemy’s height? Maybe you could use that to figure out whether they played has hit the enemy.

Ertain | 2018-08-06 16:15

Some ideas not sure if they archive what you want.
Well you could check if the y cordinate of the player is around the area of the head of the enemy area. Allà body.position.y >= enemy.postion + enemy.height - offset

Or you could add a diffrent collision for the top part and see which one got activated first.

coffeeDragon | 2018-08-07 14:24

y not just add an area2d node on top of ur enemy make it collision one way and then
make use of the body_entered signal to check if a body enter the area
u can make it more precise by adding ur player to a group like"player" then when the signal is called check if the body belong to the group player and if it does your enemy is dead if not ur enemy lives and u dont worry about those pixel error

code | 2018-08-08 08:37

:bust_in_silhouette: Reply From: markopolo

One approach would be to use a raycast or multiple raycasts. I’m only familiar with 2d, so for 3d ymmv.

Depending on the size of your player and your enemies, add a single raycast or multiple raycasts pointing up from your enemies (or down from your player, wherever is more convenient). When you detect a player-enemy collision, use force_raycast_update and is_colliding to make sure the player is above the enemy.

To make sure the raycast doesn’t detect things like the ground and other terrain objects, adjust it’s collision mask to only interact with objects on the correct layer (enemies or player, depending).

Does that make sense / help?

Yeah, that would work. Thanks.

(Sorry for taking so long to respond lol)

Marmot | 2018-08-26 11:55

:bust_in_silhouette: Reply From: Alezpecial

After playing around a bit with raycast I figured out a way to solve this without messing around with collision masks (which you should probably learn but I think this is a great alternative for some reasons). Of course, I’ve only tested this in 2d.

I have a scene with a KinematicBody2D (Goomba or any other enemy) and in it’s _ready function I’ve added the next line of code which will later be handy in the Mario scene:

add_to_group("enemy")

Now in my Mario scene with a KinematicBody2D node, to which I’ve attached a RayCast2D node (the “stomp detector”), I have the next code in the _physics_process function:

$RayCast2D_Stomp.force_raycast_update()
if $RayCast2D_Stomp.is_colliding():
	var collider = $RayCast2D_Stomp.get_collider()
	if collider.is_in_group("enemy"):
		collider.die()
		jump(jump_speed * 0.75)

As you can see, you simply get the collider that the raycast has detected and check if it is in a certain group, if so, you go ahead and execute the code you want. This is very handy since you can even call functions from the collider, in this case, i called the die function from the Goomba node Mario has stomped on.