Changing Dialogue UI Without Destroying Dialogue Manager Between Scenes
Posted: Wed Sep 22, 2021 5:37 pm
Hello,
I've been having some trouble getting scene transitions to work exactly how I want them to while using the dialogue system and I want to see if you have a suggestion for a better way to achieve my intended outcome. Forgive me for what's going to be a long post. I want to try to give sufficient details about the problems and what I've tried to fix them.
How I transition between scenes (in case it's relevant): I have a few different scene transition functions, each of which utilizes the SceneManager.LoadScene() function (using the default single mode, not additive). In each of my scenes, one of these functions gets called either in the final conversation node within the Dialogue window, in the "On Conversation End (Transform)" Conversation Event of the Dialogue System Events component attached to my dialogue manager object, or in the OnConversationEnd() function in a script attached to my dialogue manager object.
Initial Problem:
When loading a new scene, elements from the previous scene would remain visible and obstruct the view of the new scene. I made a couple quick mockup scenes using some built in pixel crushers assets to demonstrate what I'm describing:
Game View:
Zoomed Out Scene View:
Between the pictures, you can see that the bottom left corner of the previous scene is overlapping with the new scene. I realized that this was at least partially caused by the fact that the visual elements of the previous scene were children of the dialogue manager. Since the dialogue manager was set to "Don't Destroy On Load," these visual elements persisted between scenes. I don't know why the previous scene elements appear so much larger than those of the new scene, though. They are set to the same size within their own scenes.
Initial Solution:
My initial solution to this problem was to uncheck "Don't Destroy On Load" for my dialogue manager object so that the previous scene's elements would get destroyed and all that would be used in the new scene was the elements pertaining to that scene. Unfortunately, this required the (I think) computationally intensive acts of instantiating and destroying a dialogue manager object for each scene. Additionally, it meant my dialogue database variables would get reset between scenes, requiring me to save them at the end of each scene and load them almost immediately after, once the new scene had been loaded.
To try to remedy these unfortunate byproducts of my initial solution, I decided to recheck the "Don't Destroy On Load" option and move all of the objects that weren't part of the Dialogue UI out from under the dialogue manager in the hierarchy and onto a separate canvas under the Main Camera. This loads the new scene and destroys the objects from the previous scene that are not part of the dialogue UI, as intended.
New Problem:
Since I am no longer destroying the dialogue manager when changing scenes, the dialogue manager from the first scene with dialogue persists through the remainder of the scenes, meaning the dialogue UI associated with that dialogue manager is what's used in each of the subsequently loaded scenes. I have a default dialogue UI prefab that I use as the basis for most of my scenes, but most of the scenes have different variations of that UI, whether its different subtitle locations depending on the location of the corresponding actor's sprite or additional subtitle panels for when there are more than two actors involved in the conversation. Since the dialogue manager from the initial dialogue scene and, subsequently, its dialogue UI, are what are being used in each new scene that gets loaded, none of these custom dialogue UIs are used when they should be used.
Additionally, I have a variable that is set as a serialized field within a script attached to my dialogue managers. This object is manipulated based on conversation conditions. It is set as one of the objects in my hierarchy. If I leave that object under the dialogue manager in the hierarchy, the new scene uses the old instance of the variable from the previous scene when it should be using the object within the new scene. Conversely, if I move the object to the canvas that is not attached to the dialogue manager with the other visual elements, it causes an error in the script that tries to manipulate it since the instance that the script is trying to manipulate gets destroyed. A workaround for this is to check if the stored instance is null and, if so, use GameObject.Find() to get the object of the same name in the new scene hierarchy. While this works, I would prefer a solution that retrieves the actual object stored in the serialized field in the new scene's dialogue manager.
Secondary Problem:
I implemented a skip button in my dialogue using the example here and it worked perfectly when I was destroying/instantiating the dialogue manager with each scene change. Since changing to my second method where I don't destroy the dialogue manager, the skipping function does not stop at the end of a scene/conversation (I included the OnConversationEnd(){skip = false} method that was suggested after the initial post in that thread). I used a debug log message to check and confirm that the skip variable is getting set to false in the OnConversationEnd() function, but, for some reason, it continues to skip conversation lines when the new scene gets loaded. If the new scene has a conversation node split that requires a user response, the conversation stops as if the node before the split was the last node in the conversation. I did some additional testing by setting the subtitle panel of one of the actors in the new scene to a custom subtitle panel (the "correct" panel for that character under the dialogue UI object in that scene) and that caused the skipping to stop on that actor's first line of dialogue in the new scene. It seems that, for some reason, the skipping doesn't stop if the dialogue UI doesn't change.
Potential Solution:
The best solution I can come up with for my dialogue UI problem is to add all of my different possible subtitle panels to the list of subtitle panels in the Standard Dialogue UI component of the dialogue UI object in the first scene with dialogue. Since that dialogue UI is what's used in every subsequent scene, I can set each character's subtitle panel number to the appropriate index in that list. This seems very tedious and bothersome to have so many different subtitle panels in that UI, but it's at least doable, assuming I don't exceed an upper limit on the number of subtitle panels that can be added to the list.
An alternative solution I thought of was to set the subtitle panel number to "Custom" for each actor in each scene and just drag in the corresponding subtitle panel for that particular actor to the custom subtitle panel field. Unfortunately, this did not work as intended. When the new scene gets loaded, a clone of the custom dialogue panel gets added as a child to the corresponding actor's sprite and appears much larger than it should be:
Zoomed Out Scene View:
The blue arrow points to the custom dialogue panel. The red arrow points to the dialogue UI that persisted from the previous scene. The yellow arrow points to the current scene, showing how small it is in comparison to the other two.
Partially Zoomed In Scene View:
This allows you to more clearly see the canvas containing the non-dialogue UI elements of the new scene
Game View:
The response menu from the dialogue UI attached to the dialogue manager is visible, but the custom dialogue panel is not.
I can technically work around this issue by applying very small scaling factors to the custom dialogue panels and potentially adjusting their positional offsets, but this seems like a much more tedious solution than the subtitle panel list solution I mentioned above.
Ultimately, I don't like either of my solutions to the problems I've had because they seem pretty dirty/hacky, which makes me think there has to be a better way to do this. I'm also not sure what to do about the skip button issue. Do you have any recommendations of a better way to do what I'm trying to do? I think, ideally, I would want to somehow be able to apply the properties of the new scene's dialogue manager to the already existing dialogue manager instance, but I don't know if that is possible.
I've been having some trouble getting scene transitions to work exactly how I want them to while using the dialogue system and I want to see if you have a suggestion for a better way to achieve my intended outcome. Forgive me for what's going to be a long post. I want to try to give sufficient details about the problems and what I've tried to fix them.
How I transition between scenes (in case it's relevant): I have a few different scene transition functions, each of which utilizes the SceneManager.LoadScene() function (using the default single mode, not additive). In each of my scenes, one of these functions gets called either in the final conversation node within the Dialogue window, in the "On Conversation End (Transform)" Conversation Event of the Dialogue System Events component attached to my dialogue manager object, or in the OnConversationEnd() function in a script attached to my dialogue manager object.
Initial Problem:
When loading a new scene, elements from the previous scene would remain visible and obstruct the view of the new scene. I made a couple quick mockup scenes using some built in pixel crushers assets to demonstrate what I'm describing:
Game View:
Zoomed Out Scene View:
Between the pictures, you can see that the bottom left corner of the previous scene is overlapping with the new scene. I realized that this was at least partially caused by the fact that the visual elements of the previous scene were children of the dialogue manager. Since the dialogue manager was set to "Don't Destroy On Load," these visual elements persisted between scenes. I don't know why the previous scene elements appear so much larger than those of the new scene, though. They are set to the same size within their own scenes.
Initial Solution:
My initial solution to this problem was to uncheck "Don't Destroy On Load" for my dialogue manager object so that the previous scene's elements would get destroyed and all that would be used in the new scene was the elements pertaining to that scene. Unfortunately, this required the (I think) computationally intensive acts of instantiating and destroying a dialogue manager object for each scene. Additionally, it meant my dialogue database variables would get reset between scenes, requiring me to save them at the end of each scene and load them almost immediately after, once the new scene had been loaded.
To try to remedy these unfortunate byproducts of my initial solution, I decided to recheck the "Don't Destroy On Load" option and move all of the objects that weren't part of the Dialogue UI out from under the dialogue manager in the hierarchy and onto a separate canvas under the Main Camera. This loads the new scene and destroys the objects from the previous scene that are not part of the dialogue UI, as intended.
New Problem:
Since I am no longer destroying the dialogue manager when changing scenes, the dialogue manager from the first scene with dialogue persists through the remainder of the scenes, meaning the dialogue UI associated with that dialogue manager is what's used in each of the subsequently loaded scenes. I have a default dialogue UI prefab that I use as the basis for most of my scenes, but most of the scenes have different variations of that UI, whether its different subtitle locations depending on the location of the corresponding actor's sprite or additional subtitle panels for when there are more than two actors involved in the conversation. Since the dialogue manager from the initial dialogue scene and, subsequently, its dialogue UI, are what are being used in each new scene that gets loaded, none of these custom dialogue UIs are used when they should be used.
Additionally, I have a variable that is set as a serialized field within a script attached to my dialogue managers. This object is manipulated based on conversation conditions. It is set as one of the objects in my hierarchy. If I leave that object under the dialogue manager in the hierarchy, the new scene uses the old instance of the variable from the previous scene when it should be using the object within the new scene. Conversely, if I move the object to the canvas that is not attached to the dialogue manager with the other visual elements, it causes an error in the script that tries to manipulate it since the instance that the script is trying to manipulate gets destroyed. A workaround for this is to check if the stored instance is null and, if so, use GameObject.Find() to get the object of the same name in the new scene hierarchy. While this works, I would prefer a solution that retrieves the actual object stored in the serialized field in the new scene's dialogue manager.
Secondary Problem:
I implemented a skip button in my dialogue using the example here and it worked perfectly when I was destroying/instantiating the dialogue manager with each scene change. Since changing to my second method where I don't destroy the dialogue manager, the skipping function does not stop at the end of a scene/conversation (I included the OnConversationEnd(){skip = false} method that was suggested after the initial post in that thread). I used a debug log message to check and confirm that the skip variable is getting set to false in the OnConversationEnd() function, but, for some reason, it continues to skip conversation lines when the new scene gets loaded. If the new scene has a conversation node split that requires a user response, the conversation stops as if the node before the split was the last node in the conversation. I did some additional testing by setting the subtitle panel of one of the actors in the new scene to a custom subtitle panel (the "correct" panel for that character under the dialogue UI object in that scene) and that caused the skipping to stop on that actor's first line of dialogue in the new scene. It seems that, for some reason, the skipping doesn't stop if the dialogue UI doesn't change.
Potential Solution:
The best solution I can come up with for my dialogue UI problem is to add all of my different possible subtitle panels to the list of subtitle panels in the Standard Dialogue UI component of the dialogue UI object in the first scene with dialogue. Since that dialogue UI is what's used in every subsequent scene, I can set each character's subtitle panel number to the appropriate index in that list. This seems very tedious and bothersome to have so many different subtitle panels in that UI, but it's at least doable, assuming I don't exceed an upper limit on the number of subtitle panels that can be added to the list.
An alternative solution I thought of was to set the subtitle panel number to "Custom" for each actor in each scene and just drag in the corresponding subtitle panel for that particular actor to the custom subtitle panel field. Unfortunately, this did not work as intended. When the new scene gets loaded, a clone of the custom dialogue panel gets added as a child to the corresponding actor's sprite and appears much larger than it should be:
Zoomed Out Scene View:
The blue arrow points to the custom dialogue panel. The red arrow points to the dialogue UI that persisted from the previous scene. The yellow arrow points to the current scene, showing how small it is in comparison to the other two.
Partially Zoomed In Scene View:
This allows you to more clearly see the canvas containing the non-dialogue UI elements of the new scene
Game View:
The response menu from the dialogue UI attached to the dialogue manager is visible, but the custom dialogue panel is not.
I can technically work around this issue by applying very small scaling factors to the custom dialogue panels and potentially adjusting their positional offsets, but this seems like a much more tedious solution than the subtitle panel list solution I mentioned above.
Ultimately, I don't like either of my solutions to the problems I've had because they seem pretty dirty/hacky, which makes me think there has to be a better way to do this. I'm also not sure what to do about the skip button issue. Do you have any recommendations of a better way to do what I'm trying to do? I think, ideally, I would want to somehow be able to apply the properties of the new scene's dialogue manager to the already existing dialogue manager instance, but I don't know if that is possible.