Performance Optimization in World Streaming
We currently have a view distance of approx 4km (2.5 miles). It is impossible to stream all objects and terrain details within this range, so we are currently loading surrounding terrains and terrain details like grass, trees and so on. We stream 256 terrain chunks when they are needed. So about 1.000m surrounding the player are in high detail. Everything further than 1km (0.6 miles) are simplified meshes or billboards, rendered from terrains and objects.
The problem we still have is: grass, trees and details are rendered on all surrounding terrains. This has a severe impact on FPS. Therefore we reduced the generation of grass to the terrain chunks directly surrounding the player and within grass visual range (30 to 150m). Trees are now also rendered only within a visual range of approx. 1km. Furthermore, we optimized grass patch rendering and loading of chunk, by deactivating the rendering and rendering only when in visual range.
Quests and Tutorial
I just started working on the Valnir project this week as an intern. In my first week I mainly focussed on text-based tasks, such as rewriting the stories on our website and translating them. We just recreated the whole design of our website and wanted to make it more attractive to read.
Furthermore I worked on adding tasks to the beginner tutorial for the game itself. We are also thinking about adding a small quest-system to the game and I already devised some possible quests for people, who are new in the game.
The last thing I did this week was translating the game’s language into german, so that also our developer-homecountry has fun playing the game!
Performance & Bug Fixes
Performance
This week we made a huge step forward with optimizing the game to finally run better. Minh and me sat down and tried to identify the performance spikes we had. And we had a lot of them. They even were so bad that a simple rotation of the camera often froze the game for a couple of seconds completely. We struggled with these for a long time now, but we decided to finally put an end to these nasty spikes. We managed to identify a lot of the problems. However, we still have spikes when streaming new parts of the world into memory. We hope to minimize these problems in the future, too.
One crucial spike we identified came from the method Terrain.Details.BuildPatchMeshes. This method is used to build patches (small parts of the world) and populate them with grass. It seems that Unity constantly checks if the patches are still up to date and builds them accordingly if not. This resulted in very bad issues whenever the player moved or rotated his camera. In order to minimize it we first tried to adjust some settings with the terrain and deactivated grass on all the terrains except the one the player is on. Oh boy, did this result in much smoother framerate. There is an option that controls if the generation of the patches should be done on the fly or be cached. If you turn the option – CollectDetailPatches off in will cache the patches. This gives even better performance results, but will give a small spike once it gets turned off. What we ended up doing is adjusting the DetailDensity and DetailDistance depending on the range of the player to the terrain and turned CollectDetailPatches off. However, if the pre-caching causes spikes, too we might try to implement grass as normal objects instead by using the built-in terrain system.
Another spike we saw was caused by our water reflections for lakes. We temporarily turned it off completely till we optimized the script better.
Furthermore, we optimized a lot of code by applying some simple rules:
- Every method that runs multiple frames (called Coroutines) will now use a reference to the method instead of using strings to identify the method: StartCoroutine(„MyMethod„); -> StartCoroutine(MyMethod());
- Coroutines use so called Yielders (commands that tell the method how long to wait). Instead of allocating a new instance of the object every update of the coroutine. We pre allocated a number of common used Yielders in a static way: yield return new WaitForSeconds(1); -> yield return Yielders.Seconds1;
- We are now caching animation parameters in static way. Instead of allocating strings every update.