Update and message dispatch order across proxies (DEF-2273)

Hi!

I have the following setup:
A collection proxy with an object that needs to be in sync with motion controlled from the main collection.

I can’t seem to get the position of that object update in the same frame as animation from the main collection’s on_update. Thus, the object appears to lag behind when animating.

I placed a bunch of print() statements and here’s what I got:

  1. on_update from a controller game object from the main collection fires. this is where I send a message to the secondary collection proxy.
  2. Messages from the main collection are processed.
  3. on_update from my render script fires. According to this should indicate that the transforms have already been updated and they’re frozen in place now.
  4. I finally get the message in my secondary proxy.

Any advice on how to solve this?

One solution would be to have a lua module that stores the controller object’s position instead of sending a message.

So:

  1. on_update in the main collection stores the new position in global_positions.lua
  2. on_update in the proxy reads the position from global_positions.lua and updates its own position accordingly

Collection proxies are not really designed to handle that tight interaction (as in syncing positions), on the other hand I can’t really see why that wouldn’t work and why it causes a frame delay, from the top of my head.

Anyway, I’m very curious about your use case, I’m suspecting that you are building things in a way it’s not meant to be used. Could you elaborate a bit on what you’re trying to achieve?

@GregorGullwi: That won’t work since the proxy’s on_update fires before the main collection’s on_update. And update order isn’t guaranteed anyway.

@Ragnar_Svensson: I suspect I’m not using things right as well, so here’s my usecase:

My game scene is an interrogation room with a table. Different characters can sit at the table depending on the level. A level usually requires at most 3 characters being loaded at a time from a total cast of around 10-15 characters. The characters are heavily animated using flipbook animations, each with their own atlases (some simpler ones may share an atlas), so I can’t afford the memory cost of loading all possible characters as game objects in the level collection and then just destroying the ones I don’t need.

Thus, I need to dynamically load specific game objects (characters) into the scene. I achieved this by creating a collection with one collection proxy for each character and a script that loads the one that’s needed on demand. I then use this collection in a factory to load all the characters that I need to have in the scene at one time.

Maybe I’m not using collection proxies as I should. Maybe I don’t fully understand when Defold loads a texture into memory. What would be the right way of implementing this in Defold?

1 Like

Ah I see, what you need is streaming and since we don’t support that yet, you are using collection proxies as a means to do that. It’s not the first time I’ve come across this workaround. :slight_smile: To avoid having the textures in memory, collection proxies is currently the only way to achieve this. However, they are meant to represent separate “worlds”, i.e. when you load levels or even when you running separate things simultaneously, like letting a level run in the background while a separate main menu is being shown floating on top. Could be worth mentioning that for each loaded collection proxy, you get the full resources to run it as a separate game, meaning each one currently allocates ~4mb for physics.
We have an issue in the backlog to look closer at the specific case of loading textures dynamically, since it’s quite common (and could be easier to fix than generic streaming of any content). There is an internal game which has similar requirements as you do, but it’s unfortunately too early to give an ETA when there is a fix for it.
The streaming aspect of this might also be an interesting use case for the native extensions, or what do you think @Mathias_Westerdahl?
Going back to your specific problem, I guess it’s out of the question to let one of the update-functions deal with the movement, and let the position sync take place purely in the others on_message? I guess that would fix the frame lag.

1 Like

Yup. I see. Anyway, in my current usage scenario, I am doing exactly what you described: I’m doing the movement in one of the update functions in the main collection proxy and sending a message to the character proxy, which should handle it in its on_message. In theory, this should work since messages are dispatched after all the update functions have been called and before transforms are calculated (if I correctly understood the lifecycle diagram). In practice, I somehow get the message next frame.

Give me a few moments and I’ll try to do a minimal working example in a new project to see if I can reproduce this or something else in my project might be at fault.

And I managed to reproduce it in a MWE:

Not sure what I should do next. Is this a bug? Did I miss something in how the update lifecycle works?

1 Like

Not sure if this works between collections, but have you tried calling go.set_position() from the main.script instead of passing a message?

I remember trying that and it not working some time ago. I don’t think you can do that even between sibling collections, much less across sockets. I can understand the design considerations (control should trickle down, not up), but it still feels like an artificial limitation. Anyway, messages are the right way of doing this, if they would work.

So, any idea why messages across collection proxies get a one frame delay? Is this a bug or should I stop trying to make this work and do my motion calculations in an external Lua module?

I’m running some tests currently to see what’s going on.

1 Like

Messages are delivered across to proxies during the same frame. I just tried with GO1 in main and GO2 in a proxy loaded collection.

GO1 sends GO2 a new position. The message arrives during the frame. However, it seems like the transform recalc of GO2 happens at the end of the frame and the position is updated on screen the next frame. I’m not sure if this is a bug or not though. @Ragnar_Svensson?

A workaround:

  • Calculate new position for GO1 and GO2 in GO1:s on_message() and store it in self.pos.
  • Send the new position to GO2 and have it update its position in on_message()
  • Finally set the new position in then update() function of GO1. This will delay the position one frame and sync it with GO2.
2 Likes

I think this is a bug after all, as the documentation specifies that the order is:

  1. For each proxy, do update()
  2. For each proxy, dispatch messages
  3. Calculate transforms
  4. Render

Thanks for the workaround. Delaying GO1 is pretty clever. I’m not in a hurry, though, as we won’t be releasing the game anytime soon.

I’m filing an issue about this. DEF-2273