Not sure if this is a bug, or if I’m misinterpreting the documentation. I am expecting that, in the onAfterWorldMatrixUpdateObservable callback, when I return an object with position, rotation and scale of the current eventData, the next time the observable callback is called, these values are available in eventState to compare against.
When logging just the eventState, I see these values appear in lastReturnValue - but when I actually try to access them, it comes as undefined. It seems the desired lastReturnValue is only getting resolved/assigned after the callback has run.
What I’m trying to achieve: compare current value, vs prev values, so I can conditionally run some code only if eg. positionX has changed
First of all, this is a PG that is used just to make it work.
GPT says the reason is as follows:
In Babylon.js’s Observable, eventState.lastReturnValue is “the return value of the observer that executed immediately before.”
While the current callback is running, the return value of the current callback has not yet been applied. In other words, when you access eventState.lastReturnValue inside the callback, it still holds the previous value.
However, the object printed by console.log("eventState:", eventState) is shown with the latest state when DevTools later expands the object reference. So, when you open it in the developer console, it already looks like the value returned by your callback (after the callback has finished).
On the other hand, console.log("lastReturnValue:", eventState.lastReturnValue.positionX) immediately evaluates and prints the scalar value at that moment. Since the current return has not yet been applied, it still shows the old lastReturnValue, which doesn’t have positionX.
So why is positionX missing?
At the moment when the current callback is executing, eventState.lastReturnValue is still the previous return value from another observer, and that value did not contain positionX. Your callback’s return { positionX: ... } only gets applied to eventState.lastReturnValueafter the callback finishes.
thank you @11128 - is using a timeout the idiomatic way to do this? If so, it might be helpful to update the documentation to mention this.
Normally I’d be hesitant to rely on a timeout - though I see you are using a 0-second timeout, so it looks more like a deferral, rather than relying on a set time.
Will leave this open for a few more days to see if someone else gets back with anything - at which point I’ll just mark your answer as the solution.
Although this method is effective for delayed processing (forcing processing of
state updates) in state changes (simple method of pushing to the next order in the stack),
I personally do not think it is a good method for general use.
Of course, there is nothing as simple as this method.
Alright, I’ve played around with this a bit more, and I feel fairly certain something isn’t working right here.
@11128 I found that using your timeout actually does not work as expected, unfortunately. It does not seem to be returning the last value - instead, it is returning the current value!
sphere.onAfterWorldMatrixUpdateObservable.add((e, eventState) => {
setTimeout(() => {
// Both return the same value!
console.log("after tick lastReturnValue:", eventState.lastReturnValue.positionX);
console.log("after tick current value:", e.absolutePosition.x);
}, 0);
return {
positionX: e.absolutePosition.x,
};
});
The only way I’ve been able to get around this is by setting a variable outside of the callback to hold the desired return values:
However, when using this approach - I found even more issues. If I update a mesh’s absoluteScaling, it occasionally returns eventData with absolutePosition x, y and z of 0 - even if they are not 0 - which then breaks the values I’ve stored in the prevAttributes variable.
Here is a playground demonstrating all of the above:
Alright, so I’ve resolved my specific case, but I think the above issues are worth looking into still - even if at least getting a final say on whether everything I’ve mentioned above is expected behaviour.
In the end I resolved my own issue by debouncing the observer callback calls - as follows:
I tried using the debounce with the built in/expected functionality of returning the desired previousAttribute, rather than assigning it to an external object - but this didn’t work.