I have searched for the title on the board but I couldnt find anything similar.
Here is my situation, I have made a dice roll scene that will be additionally loaded into any scene that requires it, which could be the Loot scene, the Combat scene or the Dialogue scene, I want to additionally load this scene so that i can have its lighting, physics, camera all configured only for that, for example it could be that the scene it is being called from uses a perspective while I want the dice roll to be seen from an orthogonal point of view. How should I communicate the result of the dice roll with the scene that called for it?
Ooh. This is spicy territory, as in Unity there is no one clear way to do this (as there is no one clear way to do asset > scene communication or viceversa).
With that in mind:
The simplest way could be: have a Component in your dice roll scene, say DiceRollController
. The component exposes an event public event Action<int> OnDiceRoll;
with the result of the throw.
When this scene is loaded, whoever is interested can do a:
FindObjectOfType<DiceRollController>().OnDiceRoll += RespondToDiceRoll;
and hook up to the event. When the scene is unloaded, donât forget to remove that listener.
This is a simple and easy way.
Other interesting ways could be to use ScriptableObjects as a âbridgeâ or âglueâ between scenes, to expose an event not on a script but in a ScriptableObject. That way, instead of using FindObjectOfType()
you would just reference the same SO on both sides, but pretty much do the same thing with the event.
Thereâs plenty of libraries out there that do this, from Unity Atoms to SOAP to my own (mine is on the simple side, but also simple to use; the others are more fully featured but more complex).
Thereâs also videos talking about this pattern.
Thereâs also other patterns, of course, but I tend to think that many of them are overengineered â unless youâre building a full game and you have very clear what the plan is.
Hope it helps!
The easiest, lowest effort, and probably for now best implementation is simply:
public static DiceRollResult LastDiceRollResult { get; private set; }
If you want to wait for the dice roll result to finish, then you can have a public static bool DiceRollFinished
you set false before loading the screen, and set to true when itâs done, and then your main scene can just poll that to know when it should continue.
You can do this with events or ScriptableObjects or whatever. Iâd suggest implementing the one that looks nicest to you, but the two static fields is a very low effort approach thatâs going to be pretty simple to implement, easy to understand how works, and easy to debug.
It might be that you end up needing a more complex system later due to new requirements (different kinds of dice rolls or whatever), but wait by introducing complexity in your solution until you actually need it.
The scriptable object approach is basically exactly the same as what I first thought, but I would still need to manually add the scriptable object to the scripts that need a dice roll. I am wondering if there is a better looking way so that I can have a âstatic scriptable objectâ of sorts which I can call and return an event that will fire when the roll is finished without having to manually link it to every script that needs to use the dice scene. I hope i was clear enough in my explanation, if its not possible i guess I will go the scriptable object route.
Thank you, I think I will go the scriptable object route if i cant get it to work as I want, it seems cleaner to me.
Only one advice here. Make it work first, keeping the solution as simple as possible. It may be ugly static event or property, nobody cares. If your project grows enough, you may consider other measures of communication - will it be a simple ScriptableObject or complex solution like Zenject or some signaling framework, youâll just refactor the stuff depending on your needs.
You can load SO from Resources.Load<>, just make sure its the same instance for everyone.
Well I know how to make it work in a few different(imo ugly) ways, but I care about how it works just as much as if it works. That being said, I think i have to read a bit about dependency injection and see if thats closer to what I want. Thank you for bringing it up.
I guess thats as close to what i have in mind as it can get, Ill try and see in a few hours when Im back hame, thank you.
^ ^ ^ I want this on a teeshirt.
TBH it should be a EULA that every game software engineer has to agree to every time they start a new game or join an existing team.
âI promise not to over-engineer this stuff.â
Again, not one solution⌠everyone creates their own ScriptableObject singleton implementation.
However, one thing you can do is to add the reference to this SO to the script as a Default Reference:
This way every time you add the script to a GO, the SO is already referenced. (it doesnât work with scripts already added, unless you right-click > Reset them).
It is also quite non-orthogonal in that it is an editor-only facility⌠itâs definitely handy, but if your scripts evolve to contain .AddComponent<T>()
calls and you expect those to self-attach, they wonât.
Referencing @Baste simple static
property above, that is always the solution with the fewest moving parts. Coupled with setting it in OnEnable()
and nulling it in OnDisable()
you can have a robust central locator system in seconds.
My main complaint with dragging a single shared SO around is⌠why? What does it get you? âOh to get away from static stuff.â Great! You got away from static stuff but now you impose a future-and-forever cost that everybody who wants access to this has to drag that one correct instance in.
âYabbut when I want to make a different one for testing I can just make a new SO instance!â they say. âYes, but now you have to ALSO find every single place that might be dragged into prefabs and scenes and anything else and replace it.â
And I just keep coming back to:
Also, these are not âmaybeâ scenarios. I have wrestled with many many projects plagued by this drag-everything-everywhere mindset⌠itâs great for the original team, but when a third party inherits it and has to figure out what particular âlingua-dragga-francaâ is in use, it can be very costly. I am left with the feeling that thereâs always one other rarely-accessed place I forgot to drag it in, whereas with a static property, the compiler will happily tell me (modulo conditional compilation directives) the problem.
I personnaly go for the scriptable object way, without using any static/singleton at all.
If you choose it, watch this:
And then read this e-book:
Create modular game architecture in Unity with ScriptableObjects | Unity (sad this is behind a âget accessâ step)
Iâve tried the scriptable object route in the past. For very small projects itâs fine, but as a project grows it becomes hard if not impossible to easily interpret the web fo connected values. That Unite video does provoke some interesting ideas, but we can do better nowadays.
I find that anything that you need to be global is either globally accessible data (often configuration data, but not always), or a system with which you can interface with globally.
For the former, I ended up creating a reusable/extendable scriptable object singleton system.
For the latter, I realised that it would help to actually represent a running game instance with an object, which I then split into two categories, firstly âGlobal Systemsâ for systems that need to start running as soon as you enter playmode or open the application, and then âGameplay Systemsâ for the ones that start running when actually loading into a game instance.
This gave me full control with how these systems are initialised (allowing me to inject data particularly), the order in which theyâre initialised, as well as being able to explicitly shut them down on demand. The last one is useful for unloading a save/game and going to main menu, as every system is given a clear opportunity to shut down and release everything, preventing any bugs when loading another save.
Really itâs just a more sophisticated set of singletons with which I can easily control the lifetime of via a configuration scriptable object singleton.
Yes weâre talking about communicating between scenes, but I found as the project grows itâs less about communicating between scenes, but more about communicating between systems, or creating systems you can easily access through their respective APIâs. Itâs been a useful approach without needing to go with something heavy like a DI framework.
I wouldnât say DI approach is heavy
. It may seem complicated, especially for hobbyists and beginners, but the sooner you grasp on the DI concept, the easier it will be to use it from the get go and to build very flexible applications with it. Good DI framework allows you to literally play Lego with your code, swapping parts for tests and experiments, not worrying that you have to re-write half of the project because suddenly some plug-in does not satisfy your needs and you decided to move to another solution.
In that sense Iâd say the ScriptableObject singleton approach is indeed heavy and clunky, because you obligated to create and use those assets instead of, say, providing some mock data hidden behind the interface.
One day Iâll play with a DI framework. But not today and not for this project. Bit too late to implement that now.
Most of my systems are just a facade around the actual implementation, which I can still just instance and use on their own for testing purposes.
You could do the same with configuration objects, by implementing a service system in it.
Hey I hope someone sees this. I have a question about this.
Why wouldnât you just make a prefab for this dice roll scene (a table, a cup and dice or smth like that) and instantiate it (or activate it) when you need it? Why would you want to use a separate scene for this?
This is not a suggestion per se, I am just curious.
I guess, because the Scene is an isolated container for the GameObjects, so if you unload it, you may be sure that nothing leaked form it, except in case if you moved objects from that scene to another. Prefab doesnât provide that level of safety: objects can be unparented, new objects could be spawned without references to them, etcâŚ
Separate scenes scale well to larger content, especially when you start using additive scenes.
Then all you need is a directory of those scenes and load the one you want.
Keep in mind scenes can be in the built game, but they can also be downloaded as DLC (downloadable content), and if you have a robust inter-scene comm system set up, it all Just Worksâ˘.
Hereâs more scribblings about additive scene loading:
Additive scene loading is one possible solution:
A multi-scene loader thingy:
My typical Scene Loader:
Other notes on additive scene loading:
Timing of scene loading:
Also, if something exists only once in one scene, DO NOT MAKE A PREFAB out of it. Itâs a waste of time and needlessly splits your work between two files, the prefab and the scene, leading to many possible errors and edge cases.
Two similar examples of checking if everything is ready to go:
A quick update of the stuff I found that made it really hard to continue this way, any solution the unsolved point would be welcome. My Unity version is 2022.3.20f1 for reference.
- You can not stack two cameras that have two different renderers, I wanted to have a 3d roll but 2d everything else, which didnt work, I just changed the 2d scene to also use the 3d renderer, that solved the problem and didnt cause any issue with the rendering apart from some minor shader problems.
- You can not stack two cameras from different scenes Unity says âcross scene references not allowedâ
- There are some problems with some mouse events and using multiple cameras, for example OnMouseUp event function is not called when there are two cameras active on the scene, this one was easy to bypass but it was incredibly frustrating to pinpoint, since it is not mentioned in any documentation or forum post I could find.
Seems like I may need to change my approach on this one and just make it a prefab because of the cross scene reference problem.