Need HELP understanding Signals/Collisions

Godot Version

v4.2.1.stable.official

Hello to all,

I am trying to recreate a 2D game I use to play back in the late 1990’s as a personal project over the Christmas/new year break where you play as a fish & try to eat smaller fish while avoiding larger fish. If the player collides with a fish smaller then themselves they will eat that fish & slightly increase in size but if a larger fish collides with the player, the player will respawn reverting back to its original size.

I have now hit a scripting road block in my beginner coding journey & for the last two days haven’t been able to progress. I’ve been trying to get my player & enemy script to talk to each other. I will provide as much info as I can & I am hoping someone can give me some help.

I am trying to make a 2D game. I have 3 scenes (main, player and enemy). Each scene has their own parent node, children & script. My main scene is where everything happens (play space). In my main scene I have added the player & enemy. The player and enemy nodes contain: Character2D, Sprite2D, CollisionShape2D, Area2D as well as a collisionShape2D linked to that Area2D, and their own script.

I have giving my player input controls, managed to get the enemy to randomly spawn along the y axis and travel across the x axis at speeds determined by their size. I can make the player respawn any sized enemy & increase the players scale by 0.01 by collision but that is as far as I can get. I can’t seem to get the player or enemy script to talk to each other and determine scale as a factor when the collide function happens. I also think that because the player & enemy scales are constantly changing this might trip me up even more as there won’t be a consistent scale. Through my googling I think this is where signals come into play but I’m really struggling to wrap my head around signals & their interaction with player/enemy collisions. The more I read into it the more confused I get.

I have added a (_on_area_2d_body_entered) signal to both the player and enemy. As I think this signal is best suited for what I would like to achieve but I am happy to try another signal if there is another best suited for the job. From here I assume I give the player & enemy script to each emit a signal regarding their scale information, then in the main scene script is where I receive those signals to complete the script?

My lame understanding example: player send signal to main scene telling it “this is my information and code” & enemy does the same. Then main scene receives each signal & says “ok, I have both your information, so I can have you interact with each other on my scene & determine what happens when you both collide into each other”.

Please keep in mind I am quite new to Godot (started using it 4 days ago). I have uploaded a screenshot of my mainscene layout to give you an idea how everything is set out. My player & enemy are in their own groups also.

Any help would be wonderful.

TLDR: The player & enemy scale is constantly changing & I would like my player & enemy to know each others scale when colliding to determine what happens depending on that scale. eg: player eats smaller enemy & grows by 0.01 & enemy respawns otherwise larger enemy eats player & player respawns to original scale.

the body_entered signal on Player script should have the “body” retrievable as a parameter,
use it to know which area (enemy) has entered to player’s area2d
add a script to the area2d of the enemy too, inside the script get the “enemy size” from enemy’s main script.
so when the enemy entered the player, the player can know what size the enemy
by body.enemy_size

1 Like

I think I might have also played this game lol, or a version of it.

The on_body_entered signal passes the collided body as a parameter.
So if you connect the player’s area2D on_body_entered signal, the collided body should be the enemy if you setup your collision layers/masks properly.
fish_game

In this case, the enemy doesn’t even need to have a Area2D, just the player will do:

image
One thing to note is that the area2D for the player needs to be a bit bigger than the actual collision shape so it can detect properly.
image

And it’s a very simple player script:

extends CharacterBody2D


const SPEED = 300.0


func _physics_process(_delta: float) -> void:
	# Get the input direction and handle the movement/deceleration.
	# As good practice, you should replace UI actions with custom gameplay actions.
	var direction := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
	
	if direction != Vector2.ZERO:
		velocity = direction * SPEED
	else:
		velocity = Vector2.ZERO
	move_and_slide()


func _on_area_2d_body_entered(body: Node2D) -> void:
	print("Enemy Scale : %.2f" % body.transform.get_scale().x)
	print("Player Scale: %.2f" % body.transform.get_scale().x)
	
	# Player area collides with self
	# Use this if, or change the collision layer/mask
	if body == self:
		return
	
	if scale.x > body.scale.x:
		# Can eat
		body.queue_free()
		## Increase player size
		scale *= 1.1
	else:
		# Player eaten
		## Respawn()/ResetGame() code here
		print("Player eaten")
		queue_free()

This will work if all your fish sprites are the same sizes, and use the scale to make them bigger or smaller.

If you have bigger fishes with bigger sprites, then you might need to create a custom enemy class to store its ‘real size’ value, and check for that instead.

1 Like

Hey zdrmlpzdrmlp, thank you so much for the response. That’s great advice, I will give this a try make sure to incorporate it into the player & enemy script & fingers crossed it helps provide the player with the enemy scale as needed.

Hey Gustjc, absolutely fantastic points, demonstration & explanations. I think this will defiantly put me on the right track & help achieve what I want to do.

I will have a play around and hopefully (touch wood) get past my road block & get it to work and move onto the fun stuff (creating character design & sounds). It will take me a while to dissect everything you added but I will keep you all posted if any more questions arise.

The code should work, just wanted to add a caveat:

This works as long as all fish use identical sprite/same exact sprite size to start with and then you make them larger/smaller by modifying Scale properly only. If you have different fish sprites of different sizes, use get_rect() method instead to get the smallest rectangle that encloses the object. Compare areas of player’s rectangle (size.x*size.y = area) to fish’s rectangle to know which one is bigger.

I don’t think this is right, it will boot player from the level. Instead, just reset player’s position and scale back to default values.

1 Like

After a few hours implementing your suggestions & help (with a lot of trial and error with the code I had) I was able to get the code to work how I wanted. YAHOO!

Now the player & enemy both output their sizes (I added the print (“Enemy Scale : %.2f” % random_scale) script to both the enemy & player scene & that printed each characters size.

Because of this I was able to get the collision2D to determine the player and enemy scale then decide who to despawn upon collision. THANK YOU SO MUCH Gustjc, I will be able to sleep at night & will send you my finished game when done :slight_smile:

1 Like

Oh yeah that’s right, get_rect is a better solution. I actually work mostly with 3D so I’m a bit unfamiliar with 2D in Godot.

I know, that’s why I left the comment above there. That queue_free() was just for the gif to show the player disapearing.


Glad you got it working.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.