Tessalator Posted November 20, 2021 Share Posted November 20, 2021 I have an architecture with an async system that runs in "user-time" in parallel with the "World". This system is a Control Panel for the world. It has multiple initialization stages. Some that run before the Engine is started (Engine.Main...), and some after. Conceptually, I am doing something like this: static async Task Main(){ await Control.PreEngine(); await Engine.Main(...) await Control.PostEngine(); await Engine.ShutDown(); ... Cleanup } Of course (ignoring awaits) this would stop at Engine.Main since it is a blocking void. And, since it needs the main thread I can't do a lot to get around it (e.g., Task.Run(Engine). I'm able to get something working, but it feels kludgy. Engine callbacks help. The Engine exposes many update events (e.g., Engine.CALLBACK_INDEX.BEGIN_PHYSICS), but it doesn't expose startup events (e.g., Init). To get to a Control.PostEngine() I use a callback, but I have to create it. Ideally (in my mind), Engine would have startup-cycle events like "EngineInitialized" and "WorldReady". But it doesn't, so we fake them. To get a "WorldReady", I create a one-shot on an update event. { Control control = // TaskCompletionSource StartTcs = new(); IntPtr StartCallback = Engine.AddCallback(Engine.CALLBACK_INDEX.BEGIN_PHYSICS, Release); await StartTcs.Task; // faux WorldReady control.WorldReady() /* ********** Locals ********** */ void Release() { StartTcs.SetResult(); _ = Engine.RemoveCallback(Engine.CALLBACK_INDEX.BEGIN_PHYSICS, StartCallback); } } ** Is there a better event to use with this approach? To get start-cycle events (like Init) I use wedges: class WorldWedge : WorldLogic { Control control = // Init(){ // promote WorldInit control.WorldInit(); Engine.RemoveWorldLogic(...) } } Engine.AddWorldLogic(...) I use one for World and one for System. Is there more direct way to integrate with the startup cycle? Basically, I'm looking to trigger on these conditions: 1) Engine "Started" ( I can access Engine functions) 2) Init Complete - (System and/or World) 3) World "Ready" (i.e., I can access Objects (created in Init) in the World) 4) Update Loop Starting. Something like a global "Game Starting" event. 3 & 4 are similar. 3 is useful for starting things like late-binding. 4 is essentially a gate. Link to comment
alexander Posted November 22, 2021 Share Posted November 22, 2021 Hi Tessalator, Of course (ignoring awaits) this would stop at Engine.Main since it is a blocking void. And, since it needs the main thread I can't do a lot to get around it (e.g., Task.Run(Engine). You can write your own Engine.Main() function: void MyMain() { Eninge.AddSystemLogic(system_logic); Engine.AddWorldLogic(world_logic); while (!Engine.IsDone) { Engine.Update(); Engine.Render(); Engine.Swap(); } } Basically, I'm looking to trigger on these conditions: 1) Engine "Started" ( I can access Engine functions) Unfortunately, you can do it only through the SystemLogic.Init() method. 2) Init Complete - (System and/or World) System -> SystemLogic.Init() method. World -> There is a World.AddCallback(World.CALLBACK_INDEX.POST_WORLD_INIT, ...); method for this. PRE_WORLD_INIT - the world has been initialized, but WorldLogic.Init has not been called yet. POST_WORLD_INIT - right after all WorldLogic.Init calls. 3) World "Ready" (i.e., I can access Objects (created in Init) in the World) Same as in the previous point - through POST_WORLD_INIT callback. 4) Update Loop Starting. Something like a global "Game Starting" event. Please use Engine.AddCallback(Engine.CALLBACK_INDEX.BEGIN_UPDATE, ...); for this. Best regards, Alexander Link to comment
Tessalator Posted November 23, 2021 Author Share Posted November 23, 2021 (edited) Hi Alexander, I had overlooked `World.AddCallback`. But I'm having trouble understanding how to use them for this. I'm not see delegates on them that take a function. The tip about my own main is good. Thanks. What I'm doing is this: // My Pre Engine.Update(); // My Post Engine.Render(); Engine.Swap(); while (!Engine.IsDone) { Engine.Update(); Engine.Render(); Engine.Swap(); } I found most of what I wanted to do early can occur after update completes the first time. It's also a good checkpoint. I'm running render and swap for safety, but do I need to run them here? Do you have a developer program that would give me access to the C# code? I'm writing a low-level framework and I doing a lot of guessing about what Unigine is doing. Edited November 23, 2021 by Tessalator Link to comment
alexander Posted November 24, 2021 Share Posted November 24, 2021 I had overlooked `World.AddCallback`. But I'm having trouble understanding how to use them for this. I'm not see delegates on them that take a function. Oops. There was a bug in the engine. PRE/POST_WORLD_INIT and PRE/POST_WORLD_SHUTDOWN didn't work. I've fixed it in the next version of the SDK. I'm running render and swap for safety, but do I need to run them here? Yes, of course. You can run Update/Render/Swap anywhere, but they must be called sequentially (an Update spawns threads and a Swap stops them). Best regards, Alexander Link to comment
Recommended Posts