I asked this before, but now I’ve created a small project to demonstrate the issue.
I suspect there are two bugs in the collision detection refresh.
If changing positions on the screen and immediately checking for collisions doesn’t work (and it shouldn’t), then a refresh is necessary. There are two ways to refresh before the check:
Awaiting the physics frame.
Forcing a raycast update.
My problem is that neither one of these methods works on its own. It’s only when I combine the two that it works.
I have this tiny project with two sideways Godot heads, each with a raycast pointing above. The heads move rightwards alternately, and there is an assertion on the expected result of checking raycast collisions.
There are two exported booleans, each utilizing one of the methods for refreshing the collision states. When only one is checked, an assertion fails. When both are checked, it works correctly.
The project is available on GitHub:
A video demoing the problems:
What do you think? Are these actually bugs, or am I not doing something correctly?
By the way, in my original project, the problem was that collisions were not detected. In this small project, the first assertion that fails is for a false detection.
I just watched this video that mentions raycast nodes.
32:40
Basically he says when using a racist node you can’t guarantee the order of when the raycast is updated.
Although it seems it’s a raycast could be behind a frame.
I tooke a look at your code and saw something maybe unintentional.
if (tick_stage):
_tick()
else:
_tock()
Should it be?
if (tick_stage):
await _tick()
else:
await _tock()
await does not block. So as soon as you call await physics frame it returns to the caller and continues, unless you chain your awaits. Although seeing how slow the timer is, it probably doesn’t matter.
The more glaring problem is that you are updating the position in process pass with a signal rather then in the physics frame itself. And the physics_frame signal is emitted before calling any _physics_process. So in some sense the physics raycast collision hasn’t happened yet.
It might be better to setup your own signal that comes after the raycast object physics call. Although I’m not sure that will be sufficient.
There is also a good explanation between process and physical process at: 2:41:40. Which could also be useful knowledge.
Hi @pennyloafers, Thank you for the reply.
I haven’t watch the video yet, I will watch it, thank you for the reference.
Regarding the timer signal, it is set to the physics, not idle, but should it matter if I refresh the collisions detection?
regarding the _tick and _tock, do you mean because they (might) have await in them I should call them with await? Since I don’t do anything after each of those function, I don’t need the stack frame to be saved because I don’t need the state of the calling function anymore; however, you may be right that it might be a good practice to use await when calling functions that have await in them, if performance is not an issue.
I think if you put the timer before the two heads it might work.
So I’m pretty sure this is what is happening. And it’s an order issue.
We have tick physics and tock physics (tick-tock), timer timeout signal, where position is updated and we await (move await), scenetree physics_frame signal (pframe), and the raycast assertion after pframe signal (assert).
Timer is the last to go in this scene, as it starts from the scene root and works down the tree.
I changed my mind on this since everything is being driven with the same physics process. I don’t think it’s important to do the secondary await in this case. (But i think it would be more correct to do so regardless. Because you could still be awaiting the signal and you could start the next tick-tock. But this could only happen if you had multiple timers calling that timeout function in a single frame)
Yep, changing the order didn’t do the trick, but awaiting physics frame twice did!
ok, what about force_raycast_update(), isn’t the whole point of its existace is for that case?
Hey @pennyloafers, in the video you referenced what is raycast_origin? Tried a bit of skiping backwards, but could find what it is set to? At 2:56:56 he is talking about raycast origins, they are nodes, but not raycast nodes; so just node containing raycasts?!
Darn… Something else is at play. Or I have assumed something wrong. I guess ill have to read the source… Although I trust the Dev in the video has the experience and he says he did have an iteration using force_raycast_update(), but ultimately just went ahead with direct query.
He does show briefly the raycast origin at some point in the video. It’s either a node3d or a marker3d I didn’t look to hard but yeah.
I think force_raycast_update() works as intended. You can move the raycast node itself and / or change it’s properties, then use it to cast another ray.
For example if you want to cast multiple rays for different collision layers in the same frame, etc…
But if you move an object, using force_raycast_update will still “detect” the object at it’s old position, until the physics frame gets updated. I think that’s normal.
I don’t know why simply waiting for the next physics frame doesn’t work though…
Updates the collision information for the ray immediately, without waiting for the next _physics_process call. Use this method, for example, when the ray or its parent has changed state.
the documentation says for example, not that it is the only case that it should work.
There was a change to process groups and nodes can be sorted based on priority.
What it looks like is if a node has children it might get promoted or penalized in priority. So re-ordering nodes in the same priority may not fix the issue based on the number of node children.
Since my suggestion didn’t work it could be that nodes with children are promoted? And that’s why the timer could be kicked down the road if they all share the same priority.
Could you try increasing the physics priority of the timer?
@pennyloafers, I thank you, but I don’t wanna go down this rabit hole of figuring out the priorities. I think I prefer trying the method used in the video you referenced.
Hmm, I didn’t know (remember) that the documentation says that, I was just going off of my experience. Yeah I agree that the description could clarify how it works (or doesn’t) when you move other objects.