I am trying to implement an attack system in my game, which should be straightforward. However, I’m having some trouble, probably related to Godot 4’s lifecycle system, and I’m not sure how to fix it. Here is my code with some explanation:
In my WeaponScene i have something like this:
func activate(degree: float):
self.set_rotation_degrees(degree)
if (attack_cooldown_timer.is_stopped() and attack_duration_timer.is_stopped()):
attack_cooldown_timer.start(melee_cooldown_attack)
if (self.get_overlapping_bodies().size() > 0):
self.get_overlapping_bodies().map(take_damage)
So whenever the player presses the attack button, I want to set the proper weapon rotation and then get the bodies that collide with it.
However, get_overlapping_bodies() returns the bodies that were colliding before the rotation, not after. Am I doing something that is considered an “anti-pattern”? At first, I thought that set_rotation_degrees was some kind of Promise or asynchronous, but I guess that’s not the case. It seems to be about the collision bodies being calculated in different frames or just before the function call.
Anyone can link me some documentation which will help me why it doesn’t work? or tell me some tips how to handle this kind of situation?
Hey! I just ran into this same problem in my own project. I presume you are using an Area2D as ‘self’ in the code snippet. If so, the documentation can be found here. The documentation says this about get_overlapping_bodies:
For performance reasons (collisions are all processed at the same time) this list is modified once during the physics step, not immediately after objects are moved. Consider using signals instead.
There are two ways to get around this. The first is to use signals like it says and keep track manually of all the overlapping bodies.
The other is to create a new function that you call to check overlapping bodies (we’ll get to the new function requirement in a moment) similar to this:
func getBodies(fromArea: Area2D) -> Array: #fromArea is the area2D of which you check the overlapping bodies of
await get_tree().physics_frame #wait for the area to update the bodies it has
if fromArea: #just to prevent errors if the body gets deleted during the await
return fromArea.get_overlapping_bodies() #get the new set of overlapping bodies
else:
return [] #if it doesn't exist, it isn't touching anything
This method will wait until the area gets the new set of bodies after the physics step/physics frame and return it. The reason it needs to be a new method is because of how await works. The await keyword pauses the method it is in and comes back to it when the signal after it is called. In the meantime, the rest of parent functions (functions that call it) can still run.
This should fix your issue. Let me know if it doesn’t.
Enjoy!
P.S. Every version (as far as I know) of Godot comes with a copy of the documentation downloaded. To access it, either in code, type the node type and command/control(I think) click to open its documentation. You can also use the same shortcut on methods to open the method documentation. Either that, or add a new node of the type you want or find one you already have, right click it, and at the bottom click ‘open documentation’. It’s a little more restrictive than online, as you can’t search the whole documentation, but everything is there.
Yes, self is indeed an Area2D. My first idea was to use the body_entered signal, and it worked, but it caused some other issues, so I tried another approach (the one we’re trying to solve now).
Unfortunately, after using your code snippet, it still seems like get_overlapping_bodies() returns “outdated” values. Or maybe I am doing something wrong? I’m attaching a video so everything should be clear.
If you don’t exactly know what’s going on, you can add the keyword ‘breakpoint’ anywhere in your script to view the code executing. It can be very helpful as a debugging tool. You can also click ‘remote’ above the scene tree while the game is running to see the active scene tree in the game.
Once again, thank you for the valuable advice regarding my solution. I understand why it works now, and I also understand why the previous one didn’t. But it just seems to me that there must be some other, simpler solution—this one feels a bit “roundabout” to me. In any case, I think it’s an important lesson, and in the future, I’ll know what’s going on in a similar situation and won’t be as surprised as I was this time. And maybe, with experience, I’ll discover better practices
Thanks!