NeuroGoblin
NeuroGoblin7mo ago

Re-subscribing to delegate events on scene change

Hey yall, my app systemetically changes the scene of all clients in a given order. The first time this happens (from scene A->B) it works great. However, after that (B->C etc), my delegate event (IsSceneChangingDidChange) does not fire. I can also see that the OnRealtimeModelReplaced() method does not run again. I'm not sure how to manually resubscribe to this delegate to listen for model changes when a new scene is loaded. My RealtimeView component this script is attached to is persistent and a singleton. Here is the method in question: protected override void OnRealtimeModelReplaced(SceneSyncModel previousModel, SceneSyncModel currentModel) { if (previousModel != null) { // Unregister from events previousModel.isSceneChangingDidChange -= IsSceneChangingDidChange; } if (currentModel != null) { if (currentModel.isFreshModel) { currentModel.nextScene = nextScene; } // Set local variables based on model nextScene = model.nextScene; currentModel.isSceneChangingDidChange += IsSceneChangingDidChange; } } Thanks in advance!
14 Replies
kapa.ai
kapa.ai7mo ago
Hi @NeuroGoblin. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- The OnRealtimeModelReplaced() method is called when a RealtimeComponent is first created and successfully connected to the server, or instantly if it's already connected. It provides a copy of the model and if the RealtimeComponent was previously mapped to a different model (e.g., when switching rooms), it provides a reference to the previous model to allow your component to unregister from events source (https://normcore.io/documentation/realtime/synchronizing-custom-data#creating-a-realtimecomponent-subclass). In your case, if OnRealtimeModelReplaced() is not being called again when you switch from scene B to C, it might be because the RealtimeComponent is not being recreated or reconnected to the server. This could happen if the RealtimeComponent is attached to a GameObject that is not destroyed when loading a new scene. One way to ensure that OnRealtimeModelReplaced() is called again is to manually destroy and recreate the RealtimeComponent when loading a new scene. However, this might not be the best solution depending on your specific use case. Another possible solution is to manually call OnRealtimeModelReplaced() when a new scene is loaded. You can do this by subscribing to a scene loaded event in Unity and then calling OnRealtimeModelReplaced() with the appropriate arguments. However, please note that these are general suggestions and might not work perfectly in your specific case. It's recommended to thoroughly test any changes to ensure they work as expected. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai7mo ago
<@196555254496493568> kapa.ai is still learning and improving, please let us know how it did by reacting below
NeuroGoblin
NeuroGoblin7mo ago
@Normcore Assistant if I manually call OnRealtimeModelReplaced() on scene load, how can i supply it with the correct parameters of SceneSyncModel previousModel, SceneSyncModel currentMode ?
kapa.ai
kapa.ai7mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- The OnRealtimeModelReplaced() method takes two parameters: previousModel and currentModel. These are instances of your RealtimeComponent's model class (SceneSyncModel in your case). The previousModel is the model that was previously associated with your RealtimeComponent, and currentModel is the new model that is now associated with it. In the context of your application, you would need to keep track of the current and previous models. You could store the current model in a variable every time you create or update it. Then, when you're about to create or update the model again, you can use the stored model as the previousModel and the new model as the currentModel. Here's a basic example of how you might do this:
SceneSyncModel previousModel = null;
SceneSyncModel currentModel = null;

void UpdateModel(SceneSyncModel newModel) {
// Store the current model as the previous model
previousModel = currentModel;
// Update the current model
currentModel = newModel;
// Call OnRealtimeModelReplaced
OnRealtimeModelReplaced(previousModel, currentModel);
}
SceneSyncModel previousModel = null;
SceneSyncModel currentModel = null;

void UpdateModel(SceneSyncModel newModel) {
// Store the current model as the previous model
previousModel = currentModel;
// Update the current model
currentModel = newModel;
// Call OnRealtimeModelReplaced
OnRealtimeModelReplaced(previousModel, currentModel);
}
In this example, UpdateModel() is a method you would call whenever you want to update the model. You would pass the new model as a parameter to this method. Please note that this is a simplified example and might not cover all edge cases. You should adapt this to fit the specific needs and structure of your application. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai7mo ago
<@196555254496493568> kapa.ai is still learning and improving, please let us know how it did by reacting below
NeuroGoblin
NeuroGoblin7mo ago
If anyone else has a response That'd be awesome !
Mechabit
Mechabit7mo ago
if this is a persistent model then you don't need to register again on scene change you could try changing the scene change property to an int instead of a bool, that way you can keep triggering it by incrementing the int because by default no message is sent if the model hasn't been changed (for instance changing a true to a true)
NeuroGoblin
NeuroGoblin7mo ago
hmm okay that makes sense - alternatively i could make sure the bool is set to false onsceneloaded?
Mechabit
Mechabit7mo ago
you could also set the skip equality check parameter in the model property or you could add a check for scenemanager.active scene against the scene name property to check you are in the same scene good for late joiners
NeuroGoblin
NeuroGoblin7mo ago
Thanks @Mechabit - incrementing the int worked well for some reason i could init the bool to false but it didn't want to fire delegate changes after that even after explicitly toggling true / false
Mechabit
Mechabit7mo ago
odd the int method works more generally if multiple people access it
Michał Wróblewski
I've got one questions @NeuroGoblin, why do you require a room with Persistence? Is it (case A) because of scene changes and you need to preserve data when changing scenes or (case B) do you need it stored "forever" for future connections to the room? I'm asking because if it's the case A, I had that in my project - an escape room with 3 chapters and players progression from chapter 1 through 3. I discussed that here and it's better to have a separate Realtime instance connected to a "session room" where all users are connected to it and the connection is continued when scene changes - so .e.g. you can keep voice chat active when scenes change. I'll try to search where I discussed that so you can check how this case matches yours.
NeuroGoblin
NeuroGoblin7mo ago
Hey @Michał Wróblewski , thanks for the question - Essentially the setup here is to have an 'Admin' room (and corresponding Realtime component) that persists through all scenes and all clients remain connected to. The purpose of this is to send global events such as synchronised scene changes, notification messages, etc. As the users progress through the experience, they will then connect to additional rooms where they can see other players' avatars, etc (this part has already been built). I believe my current Admin setup is the same as the last part of your message, where there is a separate instance (AdminRealtime) that persists through all scenes, and then players simply connect to RoomA, RoomB etc as they progress, to sync the showing of avatars, etc.
Michał Wróblewski
All good then, it wasn't clear but I felt similar vibes - good choice anyway and good luck