The Riftbreaker is a base-building, survival, action RPG in which the player explores an unknown planet at the far reaches of the Milky Way galaxy. Galatea 37 is full of lush jungles, unknown flora, fauna, and a violent atmosphere. To make the exploration element even more interesting we’ve implemented an interactive foliage system for the game. This article is going to describe the technical aspects of implementing this system.
The Riftbreaker runs on the latest version of our proprietary game engine - The Schmetterling 2.0. We’ve created this engine while working on our previous games Zombie Driver and X-Morph: Defense. We are very excited about this project and we would like to share the knowledge that we’ve gathered while working on some of the more interesting features of this game.
We highly encourage you to take a look at the high-resolution versions of the gifs in this article. The links are under the pictures.
There are a lot of things that go into creating a realistic environment. We always want our game worlds to feel as if they were real and there were actual physical forces affecting everything around the player. Today we are going to show you what it takes to achieve such a feeling by taking a look at our vegetation and the systems that affect it. This is a larger topic, so we will divide it into smaller pieces, so stay tuned for further parts.
The debug commands allow us to observe how the vegetation behaves in various conditions.
Let's begin by taking a look at the grass. While it might not seem like much, walking around the world filled with tufts of grass certainly beats a simple texture that only tries to imitate it. Our level designers have various models to choose from, but in general, they are divided into categories based on their size. We distinguish between small, medium, large and very large models. The size obviously changes the look and feel of the area the grass is placed in, but the size is relevant also because of the forces that we apply to it - wind, bending, and shockwaves.
Let’s take a look at each of those forces one by one, starting with the wind. Our engine currently allows us to place two kinds of wind forces on the scene - local and global. Local wind only affects objects in a specific radius. An example of such a wind force is a tornado. Here’s an example, where you can clearly see the limit of the tornado’s effect on the grass:
The tornado is an example of a weather effect with local wind properties.
Global wind, on the other hand, affects all the entities on the scene. It is responsible for all the swaying that occurs naturally, without any outside interference. Regardless of the wind type, it is regulated by a whole lot of parameters, which allow us to simulate the flow of air. We specify the minimum and maximum wind strength, then randomly choose the value from within that range. The strength changes in intervals specified by us in another variable. Wind direction operates on similar principles. We set the general direction, the maximum deviation from the original angle and the frequency of changes. Thanks to all these parameters we achieve an approximation of realistic wind behavior that sets the vegetation in motion.
If the wind is too strong, the trees start breaking instead of bending.
Shockwaves come into the picture whenever something explodes (and you know how we feel about explosions). Shockwaves come in two flavors as well. The first one is instant. It applies the force to all objects within a radius of the explosion. The amount of force and the radius depend on the size of the explosion. The other type of shockwave is called physical shockwave. It is a separate effect and occurs after the instant shockwave takes place. It takes the form of a ring that increases in diameter over time, affecting the grass tufts further away from the center of the explosion. The shockwave gradually decreases in strength as it gets further away from the center.
Large explosions are accompanied by powerful shockwaves, adding to the feeling of power.
The bending force bends the vegetation away from the source, which can be any entity that has the BendingComponent in its properties. We apply this component to things like the mech, enemies, and bigger projectiles, such as rockets. The strength of the bending force gets progressively weaker with the distance from the source. The example below shows you the vectors of the bending force and the affected sphere.
The debug options allow us to observe the range of each object's bending force.
Of course, if we applied wind, bending and, shockwaves together at once we would only get a glitchy mess as a result, as they would clash against each other. Instead of that, whenever game logic gets updated, we take a weighted average of all the forces, giving them a different factor of importance. The result of this operation is then applied to the vegetation. Based on this, plants in The Riftbreaker can exist in 3 states:
- Idle - no forces other than the wind affect the entity,
- Bending - when a bending force is currently being applied by another object,
- Recovering - the bending force is no longer being applied and the object is returning to the idle state.
Science time!. In order to achieve the effects of the grass swaying in the wind and its springiness, we need to apply some math. We approximate the restoring force by using this function:
The bending effect is, in turn, a vertex shader. It manipulates the vertex of the object and bends it according to a parametric square function. The values of bending and restoring vary between the different models, which gives each of them a unique feel. Our system gives us the ability to change the following:
- Recovery time - how much time it will take to go from the ‘bending’ state to ‘idle’ state,
- Oscillation damping - how much speed the plant will lose with each cycle,
- Number of oscillation phases - how many times the mesh will travel back and forth before returning to idle,
- Slope factor - the pseudo ‘springiness’, how fast the plant will move after being bent.
Naturally, you want the bigger plants to feel more massive. By raising the values of the functions mentioned above, the graph of the function will stretch and more oscillations will appear before returning to idle. On the other hand, smaller vegetation items need lower values to behave more springy. Getting the values exactly right requires a lot of iterative work, but the final effect more than makes up for it.
We wouldn’t be ourselves if we did not give you the opportunity to set something on fire. That is where the FlammableComponent comes into play. Every entity with that component can be set on fire. We can tweak the probability of the object catching fire if it’s currently being affected by a fire source, such as a flamethrower, as well as the time it will burn once it is set ablaze. Every time the game logic is updated, the information is gathered which objects are currently in the range of open fire. Then, we perform a roll for each of those and if the result is positive, the object will catch fire. Some things are almost impossible to set on fire, while others need just a spark to burst out in flames.
The use of the flamethrower can lead to the uncontrollable spread of fire on the scene.
Once the entity is on fire, we give it another set of properties - BurningComponent. The affected piece of vegetation becomes the source of fire itself. With this component, we can control the range to which an object can spread the flames. The bigger the entity, the further it can spread the flames. We also control the burning rate, which determines how often an attempt to spread the fire is performed. Apart from the BurningComponent we also apply the proper particle effects and start dissolving the mesh. What is noteworthy - the spread of fire is affected by the wind. We slightly offset the center of the fire-spreading sphere according to the global wind properties.
Here's a link to the high-quality compilation of all the GIFs used in this article: https://youtu.be/F4tbyhkHAOg
Thank you for reading this overview of The Riftbreaker’s vegetation system. If you have any questions, feedback, or suggestions, let us know in the comments or on our Discord! www.discord.gg/exorstudios!