Hi, I’m new to Godot 4 (2D) but I have my years on programming various language for fun and profit. I am trying to understand why things happen the way they do in order to master Godot.
I’m now trying to move around “stuff” and interact with them. (I use the term stuff to avoid using Godot’s keywords).
What I’ve created for now is a CharacterBody2D based on various videos I’ve seen. (I could have used StaticBody2D or anything else, I’m not entire sure on what some of the part of the documentation means but that’s something I can change later)
Now the questions:
Why if I create for example an Enemy as CharacterBody2D I have to give it two collision shapes? One is for itself and one is for it’s Area2D. Am I doing the the Collision wrong?
My CharacterBody2D has an Area2D for interaction. On this Area I use the Signals “area_entered/exited” and “body_entered/exited”. If I check the interaction between two of my Enemy I will probably use just one of the signals, since both enemy have a body and an area. I am not sure why there is a difference, but this I suppose it’s due to the fact that there are collisionshape on both the body and the area.
Main question: Why when I create programmatically an Enemy I get a signal for Area Entered and if I check what’s happening with: print(self.get_parent().name,body.name)
I will see that the self.parent is the World, while the body is the enemy itself? The signal is for the enemy, so I’d expect that the signal is emitted when something enter the enemy’s area. body.name should contain the name of the object entered in the area of the enemy and not the enemy’s body.
CharacterBody2D only needs one collision shape, you don’t have to add an Area2D, it depends on what you’re trying to do.
Usually the “main” collision shape of the CharacterBody2D serves to collide with objects, so it can’t fall through the floor or walk into the wall…
It can also serve as a “hurt box”, to detect when an enemy hits your character.
You can add the extra Area2D to serve as a “hit box”, if your character has a melee weapon for example, you can put the Area2D collision in front of the character to represent the weapon’s reach, and detect if there’s an enemy in reach.
But generally the difference between areas and bodies is that areas aren’t meant to collide with objects, they just detect when something entered/exited them. While bodies meant to collide with each other (floor, walls, in game objects…)
As for question 3, the Area2D does detect the “main” collision shape of the CharacterBody2D if they overlap. You have to set up different collison layers to avoid that, can’t really remember if there’s another way to prevent it.
Every CharacterBody2D requires a collisionShape, that one is mandatory. The basic collisionShape helps the CharacterBody2D to detect floor and compute velocity. Everything else is a bonus. I believe you added the additional Area2D in order to detect a different kind of collision. Maybe the area2d is tied to the enemy’s weapon that is capable of inflicting damage upon touching the player. Or maybe it’s a pickup range so it detect items that lie on the ground.
There is a distinction between area_entered/exited and body_entered/exited as one is detecting area2Ds and the other is detecting bodies (as in: RigidBody, CharacterBody). You need to set the collisionLayers and Masks appropriately so that the areas can only detect the things it needs to detect while disregarding everything else.
If your Enemy can detect itself, this means that his collisionLayer and Mask for the area2D is the same. It can be detected by those who mask his layer AND he is detected others who are on the layer. The result: he is including itself in the collision detection.
If the Enemy is added to the sceneTree, then if the body were to detect itself and print self.get_parent(), then it would naturally print out the SceneTree root. Feel free to check it out. Run the game and then change to the Remote tab on the left side of the editor that replaces your NodeTree. This is your current state of your SceneTree.
If the collisionlayer + mask is as intended but you still don’t want it to detect itself, then just exclude the edge case: if body == self: return
Thanks for your answer @Monday . I added Area2D because I did not find how to have the signals for collisions.
So theoretically I should remove my Area2D and its collision shape, but I don’t know now how to detect when two enemies collide.
I am missing the signals so I read the docs about CharacterBody2D and I start to suspect that’s not what I need, but then again why having a CollisionShape if there is no signal to manage it? Is it used only to detect walls? If it moves only with move_and_collide() then I can’t use it on a Path?
Say my Enemy can be moved by click and hold the left mouse button. once I moved it, if it’s in a specific place, it get attached to a path, if not, it moves around randomly (don’t know yet how) and on this case it should be able to detect other Enemies. Is it two different types of Nodes (or shall i call it Scene?) If it’s on a path, a CharacterBody2D is fine, if not, I should use something that detects other bodies.
@Locher
thanks as well, I got my mistake about using get_parent() before you wrote it. Glad that you confirmed my suspicions.
Usually for characters you want to use move_and_slide, because move_and_collide will not move the character when it collides with anything (even if it only collides with the floor)
Instead of signals, CharacterBody2Ds have some functions that you can use to get collision info. You can check if the enemy has collided with something like this:
move_and_slide()
for i in get_slide_collision_count():
var collision = get_slide_collision(i)
print("I collided with ", collision.get_collider().name)
Generally move_and_slide or move_and_collide should be used to move character bodies because changing their position property directly can cause physics issues. But I don’t know about paths. I would assume it’s ok / possible to move a CharacterBody2D on a path, but I’ve never done it myself so not sure…
move_and_slide in _process give hilarious results if the CharacterBody2D is on a PathFollow. I might have done a mistake here and there, but I suppose it shouldn’t be used this way.
If I add
if is_instance_of(get_parent(),PathFollow2D):
return
everything goes back to normal, until a pathfollower hits another nonfollower. At that point it starts to fall.
I might have to check how to manage a top-down 2d game, since removing the gravity from _process isn’t enough.