What changed
0 fixes1 addition7 changes0 removals
- Maps
- Performance
- Balance
- Gameplay
changedWelcome to Minor Deity!Below I provide some details on the technical details involved in allowing huge maps with millions of objects to run smoothly, and surprisingly fast.
changedBursted JobsBehind the scenes, the data structures have been designed from the start to be suitable for bursted jobs on parallel threads, managing the memory in a way that allows an enormous amount of calculations to be performed per frame. I had to create tailor-made functionality to handle many of the things that Unity usually handles automatically "in general" for you behind the scenes. While this was a lot more work, and technically more difficult to accomplish, it does allow me to implement it exactly as I need it, cut out any "general case parts" that aren't required, and make excessive use of bursted jobs on threads, plus some specific tricks to improve performance. It also taught me a lot about the fundamentals of how displaying complex games work, which was fascinating to learn.
changedMy Own LOD and Culling SystemsI designed my own Level-of-Detail system, that allows fine-tuning of the displayed quality of different groups of visual elements. You can set the qulaity of trees, small vegetation, buildings, people and animals, etc. separately, depending on the capacity of your PC and what is most important to you, visually. I can also turn groups on and off to test the impact of each of them on frame rate, allowing me to improve the performance by fine-tuning different groups.
changedMy Own LOD and Culling SystemsI also perform the culling "manually", including the implementation of a few tricks to reduce the work required to determine which meshes/objects are visible to the camera at any moment. As a first step, the terrain is divided into up 64 chunks, and I determine whether each chunk is visible to the camera. Then, for each object, I keep track of which terrain chunk it finds itself on, if applicable, and cull the object if its terrain chunk isn't visible. Furthermore, the terrain also has an underlying hex map (with up to 160,000 hexes) used to manage "macro characteristics", for example various climate values at that hex. Objects can also be confined to a hex, allowing me to use hex-based culling for all of the small objects on the hex. This, paired with bursted parallel jobs - on my PC I have access to 18 threads - allows me to have up to 10 million individual objects spread across a huge map, and still be able to cull them in a performant manner. Of course, you can still not display too many of them at a time, but it allows huge, fairly densely populated maps that you can jump around in.
addedSelecting ObjectsAs part of the culling process, I also determine whether objects are currently "under the mouse cursor", so that any of these objects can be selected, if required. This does not require the use of colliders on the objects.
changedManual, Combined Render CallsI've combined certain objects into a single mesh-and-material, with each object being assigned a certain sub-range of the vertices and triangles of the mesh, allowing me to create a long list of position-rotation-scale matrices for the visible objects, paired with a list of sub-vertices of the mesh for each object, which is sent to the GPU in one render call. For example, all ~145,000 flower objects in the stress-test seen below was rendered with a single draw call.
Minor Deity changes
changedBelow I provide some details on the technical details involved in allowing huge maps with millions of objects to run smoothly, and surprisingly fast.
changedBehind the scenes, the data structures have been designed from the start to be suitable for bursted jobs on parallel threads, managing the memory in a way that allows an enormous amount of calculations to be performed per frame. I had to create tailor-made functionality to handle many of the things that Unity usually handles automatically "in general" for you behind the scenes. While this was a lot more work, and technically more difficult to accomplish, it does allow me to implement it exactly as I need it, cut out any "general case parts" that aren't required, and make excessive use of bursted jobs on threads, plus some specific tricks to improve performance. It also taught me a lot about the fundamentals of how displaying complex games work, which was fascinating to learn.
changedI designed my own Level-of-Detail system, that allows fine-tuning of the displayed quality of different groups of visual elements. You can set the qulaity of trees, small vegetation, buildings, people and animals, etc. separately, depending on the capacity of your PC and what is most important to you, visually. I can also turn groups on and off to test the impact of each of them on frame rate, allowing me to improve the performance by fine-tuning different groups.
changedI also perform the culling "manually", including the implementation of a few tricks to reduce the work required to determine which meshes/objects are visible to the camera at any moment. As a first step, the terrain is divided into up 64 chunks, and I determine whether each chunk is visible to the camera. Then, for each object, I keep track of which terrain chunk it finds itself on, if applicable, and cull the object if its terrain chunk isn't visible. Furthermore, the terrain also has an underlying hex map (with up to 160,000 hexes) used to manage "macro characteristics", for example various climate values at that hex. Objects can also be confined to a hex, allowing me to use hex-based culling for all of the small objects on the hex. This, paired with bursted parallel jobs - on my PC I have access to 18 threads - allows me to have up to 10 million individual objects spread across a huge map, and still be able to cull them in a performant manner. Of course, you can still not display too many of them at a time, but it allows huge, fairly densely populated maps that you can jump around in.
addedAs part of the culling process, I also determine whether objects are currently "under the mouse cursor", so that any of these objects can be selected, if required. This does not require the use of colliders on the objects.
Welcome to Minor Deity!
Below I provide some details on the technical details involved in allowing huge maps with millions of objects to run smoothly, and surprisingly fast.
Bursted Jobs
Behind the scenes, the data structures have been designed from the start to be suitable for bursted jobs on parallel threads, managing the memory in a way that allows an enormous amount of calculations to be performed per frame. I had to create tailor-made functionality to handle many of the things that Unity usually handles automatically "in general" for you behind the scenes. While this was a lot more work, and technically more difficult to accomplish, it does allow me to implement it exactly as I need it, cut out any "general case parts" that aren't required, and make excessive use of bursted jobs on threads, plus some specific tricks to improve performance. It also taught me a lot about the fundamentals of how displaying complex games work, which was fascinating to learn.
My Own LOD and Culling Systems
I designed my own Level-of-Detail system, that allows fine-tuning of the displayed quality of different groups of visual elements. You can set the qulaity of trees, small vegetation, buildings, people and animals, etc. separately, depending on the capacity of your PC and what is most important to you, visually. I can also turn groups on and off to test the impact of each of them on frame rate, allowing me to improve the performance by fine-tuning different groups.
I also perform the culling "manually", including the implementation of a few tricks to reduce the work required to determine which meshes/objects are visible to the camera at any moment. As a first step, the terrain is divided into up 64 chunks, and I determine whether each chunk is visible to the camera. Then, for each object, I keep track of which terrain chunk it finds itself on, if applicable, and cull the object if its terrain chunk isn't visible. Furthermore, the terrain also has an underlying hex map (with up to 160,000 hexes) used to manage "macro characteristics", for example various climate values at that hex. Objects can also be confined to a hex, allowing me to use hex-based culling for all of the small objects on the hex. This, paired with bursted parallel jobs - on my PC I have access to 18 threads - allows me to have up to 10 million individual objects spread across a huge map, and still be able to cull them in a performant manner. Of course, you can still not display too many of them at a time, but it allows huge, fairly densely populated maps that you can jump around in.
Selecting Objects
As part of the culling process, I also determine whether objects are currently "under the mouse cursor", so that any of these objects can be selected, if required. This does not require the use of colliders on the objects.
Manual, Combined Render Calls
I've combined certain objects into a single mesh-and-material, with each object being assigned a certain sub-range of the vertices and triangles of the mesh, allowing me to create a long list of position-rotation-scale matrices for the visible objects, paired with a list of sub-vertices of the mesh for each object, which is sent to the GPU in one render call. For example, all ~145,000 flower objects in the stress-test seen below was rendered with a single draw call.
I hope some of you found this interesting - there's a lot more where this came from! ːsteamhappyː Discord
Gideon