Marvin Wärzner explained how Aesir Interactive GmbH approached the game's dynamic weather with diverse ground types and changing conditions in Unreal Engine 5 to deliver an immersive horseback exploration of Mongolian landscapes.
In case you missed it
You may find these articles interesting
Intro
You sit on your trusty horse and ride through the calm and beautiful Mongolian landscape. Suddenly, the wind picks up, and the foliage around you starts dancing in the wind; clouds start to form, which causes the sky and your surroundings to darken, and leaves start blowing in the wind. You are on your way to deliver important ingredients to the shaman of a nearby tribe.
While you spur your horse to ride faster, you notice the first raindrops hitting the ground, and before you know it, it starts pouring as if the sky has been split in half. The ground softens and gets muddier, making it more difficult for your horse to ride, and the strong wind slows you down as you look out but fails to spot some cover as the visibility gets much worse in this harsh rainstorm. Time is short, so you push ahead, and after carefully altering your route to minimize the impact on the cargo and the strain on your beloved horse, you are finally rewarded as the clouds clear and the first beams of sunlight start to warm your face. Coming over the last steep hill, you manage to spot your destination on the horizon.
Time for the last push! This is part of the vision that we have for Windstorm: The Legend of Khiimori, and our weather system plays a big part in making it a reality. In this article, I want to shed some light on how our team at Aesir Interactive GmbH is bringing this idea to life.
Weather & Wind Systems
Let's start off at the core of it all: Our wind and weather game services. Game services are basically our internal implementation of lightweight subsystems that manage a specific feature of our game. The game services are implementing an interface that lets the game service manager start, stop, or optionally also tick them. Our other game systems can then communicate with these services to get information or request changes in behavior. If a service is configured to be tick-able, it can then update its logic at predefined intervals.
Our weather and wind logic are split into their own services to maximize the control we have over their individual behavior and to keep things modular and code concerns separated. Both services are keeping track of their current state, which refers to a certain weather type or a certain wind configuration, respectively. The states and their properties get updated and interpolated based on a random interval that is within a certain range configured by our game designers, but we also have trigger volumes and other mechanisms in place to manually request a change to a specific state, which might be desired to force a change in weather for gameplay reasons. Requests are added to a queue based on priority and are then processed by the game service. Other systems in the game can subscribe to different events that are relevant to these changes to then alter their own behavior.
We have multiple different weather states that can influence the world around the player. Different wind intensities and changes in direction will have an effect on your riding experience but also may influence other actions like the flight path of an arrow when practicing archery or the sense of smell, which many animals rely on. Changes in temperature will have an impact on the survival of you and your horse and may affect certain aspects of the game world as well. Of course, different weather will have a noticeable impact on gameplay and visuals. All these behaviors are driven by getting the relevant data from the game services and taking it into account in their logic.
Ground Type System
Another feature that is very deeply connected to the weather state is our ground-type system. When it starts raining, for instance, a regular patch of earth might turn into muddy terrain, which makes it more difficult to traverse or make stones more slippery. Prolonged freezing temperatures can harden the ground as well, which in turn will make the mentioned mud easier to traverse again. To achieve this, we are mapping the surface type defined by the physical materials of our landscapes to specific ground type definitions based on multiple weather-related factors. This would mean that sand, for example, would be considered wet sand when it is raining.
Different ground types will have a different effect on the riding experience. The softer ground will require more effort from the horse, leading to a higher stamina drain, while some may be slippery, which can make the horse feel out of balance. We are using the gameplay ability system together with its attributes to manage all these different effects on the characters in our world. We rely heavily on gameplay tags to represent different states, items, and effects, and we combined them with data tables to get information about the represented entities very easily while at the same time allowing us to tweak balancing data in a straightforward way.
Enhancing The Immersion
To make sure the player gets the correct feedback, we spend a lot of time creating appropriate SFX and VFX animations for the interactions with the ground types. We created an actor component which asynchronously gathers information about the surface beneath the characters feet which we then can use to spawn the correct effects for our different ground types.
Mud will create visual splashes and play a corresponding sound, while sand will leave behind dusty clouds. Dirt will also attach itself to the hooves of the horses, which contributes to the dirtiness of the horse but fear not, as you could just wait for it to rain or ride through some water to clear the dirt in a pinch if you are not able to clean it by hand, which can be done as well. Our talented animators spent a lot of time creating immersive animations that communicate how the horse keeps up under different influences. We also rely quite a bit on procedural animation to make sure our characters and horses feel alive and reactive.
The effects for our ground types are mapped in a data table with entries for SFX, VFX, decals, and haptic feedback, which we can assign per surface type.
Everett Gunther's Ultra Dynamic Sky Plug-In
The basis for our weather visualization is a well-known plug-in called Ultra Dynamic Sky. Together with its Ultra Dynamic Weather feature, it provided us with the perfect starting point. Using it saved us valuable development time. With its gorgeous realistic visuals and plenty of ways to customize its behavior, it served as a great foundation for what we had in mind. After all, in game development, it is important to choose your battles, as it is not always worth reinventing the wheel. Especially if the wheel already works quite well.
Naturally, we made a lot of changes to the plug-in to make it work with our weather systems and customize its looks for our purposes. We also heavily modified its landscape effects feature to support all our ground types so that characters not only leave trails in snow but other "deep" surfaces as well, like sand and mud.
Setting Up Foliage Wind
When it comes to the visualization of the wind in our game world, it was very important for the vision that the wind intensity and direction directly influence the foliage animations to make sure our rendition of Mongolia feels immersive and that the movements of our trees and plants make it easy for the player to gather information about the current wind influence just by looking at the environment. We are utilizing the Pivot Painter 2-based approach to animate the hierarchy of trunks, branches, and leaves on our trees and larger foliage using vertex animation. There is a lot of info out there on how it works, but to quickly sum it up: Pivot Painter 2 allows for efficient and flexible vertex animation by storing pivot points and deformation information in textures which can then be sampled in a shader to drive complex vertex animation which reduces the need to do complex calculations at runtime.
The savings from using this approach are necessary as we are using Nanite for our foliage which results in higher vertex counts. For this reason, we try and keep the instructions of the vertex shader path of our materials as cheap as possible and make use of the world position offset disable distance to stop foliage from eating away precious frame time if they are further away from the player.
For grass and other small foliage, we use a simpler but still texture-based approach to ensure the direction and intensity of the wind are clearly visualized. To achieve that, a custom-made noise texture is panned in the direction of the wind at a speed that matches the current wind intensity. This noise then modulates the overall bending of the foliage, which we calculate based on the height and strength factors of the plant and the wind. This approach results in a relatively cost-efficient but visually pleasing foliage animation, which makes riding through open fields even more enjoyable.
This graph shows the material function which creates the world position offset for the wind effects on small foliage. The directional wind noise offset is generated outside of the material to avoid issues with large numbers when using the time node.
With all these things coming together, we hope to provide players with an immersive experience that makes for a fun and rewarding journey throughout Mongolia. All these features are still in active development as we are constantly tweaking and improving aspects of them to make all of them work together in the best possible way. We hope these insights prove useful to you and look forward to you trying it out for yourself!