I often hear that using Event Dispatchers is the best way to send notifications from BP Actors to the Level Blueprint.
Based on that idea, I have been managing things like weather intensity changes and Skylight visibility (on/off) inside the Level Blueprint.
I once heard that “things that affect the entire level should be managed in the Level Blueprint,” but in practice this approach has caused many problems.
Weather changes can be triggered from many different places in the scene, and each of those Actors needs to send notifications via Event Dispatchers. Sending the events themselves is not the main issue, but the problem is that the Level Blueprint must bind to all of these dispatchers.
Binding all events in Event BeginPlay feels undesirable, and not just because of performance concerns, but also because the number of bindings grows very large.
Binding and unbinding dynamically is also possible, but it becomes very inefficient when the game supports loading from the middle of a stage, since the correct bindings must be re-established depending on the starting point.
This makes the whole system feel overly complex and fragile.
In this situation, what would have been the better architectural approach?
How should global systems like weather and lighting have been designed instead?
How do you usually manage weather systems?
Is it better practice to create a dedicated Weather Blueprint Actor and place things like the Directional Light and Sky Light inside it, then communicate with it via Blueprint Interfaces?
There are multiple ways to approach such a system depending on the complexity of the project requirements and the knowledge base of the development (i.e. C++ vs BP).
You can use Event Dispatchers, Interfaces in BP for sure, but the question remains ‘What to call exactly, and how to organise it?’
For example, if you want to control Time/Date/Weather states, should you be creating an event dispatcher for each? or aggregate multiple operations in one node/function etc?
I prefer to build my own system in C++ as a GameInstance Subsystem (Accessible globally basically), but again, that system must reference the Daylight actor itself (whether it’s a BP or just a directional light), but if I had to go with BP, I’d go with reducing my events to the bare minimum, and just pass the values as JSON strings and filter them in a global BP (i.e. {“weather“ : “clear“}, {“time“ : “12:00“}, etc)
However, you might want to check the brilliant ‘Ultra Dynamic Sky’ Game System on FAB, it has hundreds of params with very useful functions accessible globally in the game
In short, there’s no escaping Interfaces and event dispatchers, it’s a question on how to utilise them efficiently, I hope this helps.
I almost never use the Level Blueprint. Make a separate actor BP to manage this stuff. Or use GameState if it fits (GameState is a convenience actor provided for multiplayer networking the um, game state). It’s hard to re-use logic in the Level BP… if you make a specialty actor, you can just drag it to a new level.
For accessing one-off singleton actors in a level, using ‘GetActorOfClass’ is fast if you need to access a WeatherManager or whatever. Cache the ref if you need it often/per tick.
If you NEED an interface for this, sure. Honestly it may not be necessary. Interfaces are good for abstracting away and reducing coupling, but can also be a bit of a speed bump. For a global system that is always in memory, may not be the place. Depends on your design.
Same with events… if all your events are aimed at the weathermanager and nothing else, setting up multicast delegates and bindings may be extra error-prone work and upkeep vs just calling WeatherManagerRef->SetNewWeather(blah, blah). Totally depends on your game.
Using a subsystem like IAvArt points out is a really clean way too, I also prefer for a lot of non-networked systems. ie DialogueSubsystem. If youre doing c++ they are worth learning and really easy to set up.
i would suggest this, just easy to copy and paste between levels, it can register itself with a manager like the GameState for easy access and can replicate if you decide you want multiplayer.
“I hear a lot about Subsystems. I’m currently using the default Sky Sphere in UE5, but it seems pretty clunky to work with. If I’m going to struggle this much, using a dynamic sky would solve everything in one shot. Thanks!”
"Tutorial videos often do everything in the Level Blueprint, but I’ve had a sneaking suspicion for a while that it’s not very user-friendly. Hearing someone confirm that they almost never use the Level BP gives me a lot of peace of mind!
I’ve decided to use GetActorOfClass to spawn the weather before the game loads and then save that return value into a variable (cache the reference) for later use."
“Through these questions, I’ve learned about Subsystem instances and Messaging systems. I’ve decided that both are a bit too advanced for me at this stage, so I’d like to apply them to my future game projects instead. Thank you so much for your valuable insights!”
agreed the level blueprint is an outdated system in my opinion.
i would also say to basically never use this node, its not wrong exactly its just you should control your systems. ie have your GameMode or something spawn the weather system OR have your weather system register itself with the GameMode (or whatever manager you use)
I agree in case of using the GetActorOfClass frequently or on tick events. However, calling it once on BeginPlay and reference the Weather BP as variable is quite common approach.
That’s right actually, you can add validation and retry logic however.
It’s just good to let new users know that it’s possible to use different approaches to achieve certain functionalities (some approaches are better than others obviously), but it’s a learning journey, some might find interfaces and event dispatchers a bit overwhelming at the beginning.
Huh, for Level actors I’ve not run into this. I’m interested in what situation that might happen? I thought BeginPlay was called after everything in the loaded Level was Init/PostInit, but it’s been a while since I’ve really looked at it.
Not for loaded level actors. In Level.cpp ULevel::RouteActorInitialize you can see it stepping through the actors calling PreInitializeComponents on all actors, then InititalizeComponent,PostInitializeComponents on all Actors, then once the OwningWorld HasBegunPlay it calls DispatchBeginPlay for each Actor, which does the actual BeginPlay call.
Right, of course, I see. I thought Level Actors, by which i mean Actors you add to a level and save in editor (like a WeatherManagerBP), were what we were talking about here with the GetActorOfClass thing. You were talking about the BeginPlay on the LevelBP.
I was discouraging use of the Level BP in my original post… its useful sometimes for quick one offs/ gyms and prototypes, but I think it’s a sloppy place to do actual game dev.