Week 6 - Making Improvements
Week 6 - Making Improvements
Following the testing session last week, I made a ton of optimisations and improvements to the game. There was a concern amongst testers with the amount of lag, especially when using the bomb - so fixing this was the primary focus. I started by optimising the players kart model. Previously, all the voxels in the kart were being rendered as individual objects, but considering I only need to use those voxels when the player dies, I could get away with disabling them and using a surface mesh on the parent player object to represent the kart while they are actually playing. This alone boosted the average frame rate by approx. 30 fps, as I had just removed hundreds of objects and all their components from the scene. Of course, this wasn't going to fix the lag spikes caused by the bomb, but it did give me more headroom to work with.
I used the profiler to work out exactly what was contributing to such a massive drop in frame time, and the main culprit was instantiating hundreds of voxels at the end of the explosion all at once. I initially planned to use an object pool to get around this, instantiating everything I'd need at runtime and then reusing it during the game - but in the short term I just disabled them while I worked on getting the platform destruction to work properly.
I had spent the past couple of weeks trying to figure out how to implement localised platform destruction, where the bomb could blow holes in the stage. I couldn't see how it was possible, as with my current implementation (parallel arrays), I'd have to iterate through the entire array of voxels, every single frame, to update their matrix positions to send to the GPU. Of course, with an array of tens or hundreds of thousands of elements, sequentially seeking through it was far too slow. So, these were my options:
A) Use a compute buffer to do all of the computation and rendering on the GPU in parallel.
B) Multi-thread the operation to speed up the search.
C) Find another way to store the voxels which has a non-linear time complexity for searches.
Given option A requires compute shader support, which WebGL lacks, and with Unity's API not being thread-safe, I started researching common data structures and comparing the time complexity for each, which is when I found out about associative arrays. An associative array (or dictionary in C#) uses a hash table to index elements in the array based on key, value pairs. So, I could have the key be a voxel game object, and it's value be the matrix associated with that voxel. When I search the array for a voxel, it spits out the matrix I want with a time complexity of O(1), or a constant rate independent of the size of the array. This is EXACTLY what I needed to make interacting with individual voxels possible, and completely changed the game (literally).
So, I immediately began rewriting the code to use a dictionary to store all of the voxel data, and modified the destruction function to act on voxels directly hit by the bomb blast. The performance uplift was huge - going from a frame time of 50 - 60ms down to just 10 - 15ms when setting off the bomb. It completely eliminated the bottleneck, with the only limit now being how many voxels I can render, and how many collisions I can compute.
As I am currently rendering every voxel, regardless if it is visible or not, I thought I'd try to implement occlusion culling. I didn't think it was feasible without using GPU compute (I was right), but I did actually get it working using raycasting... at 3 fps. Failing that, I decided to just make the platform thinner, so I could shrink the voxel size without impacting performance. While I have plenty of overhead on my PC, it needs to be able to run on much less powerful hardware, so I am pretty limited on the LOD.
Now that the performance issues were solved (I hope), I wanted to add some more cool effects. I thought having the player be rebuilt out of voxels to respawn would be neat, and rather than do this with an animation I just added some logic to the respawn that moves each voxel back to it's initial local position each frame. This worked surprisingly well, and looked cool, but it could be better. So I added an offset to the height and made each voxel fall into place, trying to simulate the "Matrix digital rain" effect (following the project theme here). It was a little buggy at first, but I got it worked out in an afternoon and it looks pretty sick!
Now that I could do per-voxel destruction, I was able to re-introduce the barriers, and have them be broken into pieces when hit. They are essentially a copy of the platform with some changes to the collision matrix and tags, and I'll probably try to consolidate it into a single script before the final submission (which will fix some issues). They add a lot to the game as it makes each round go for much longer, and bouncing off the barriers adds a layer of strategy as well.
Lastly, I reworked the UI to make it much more intuitive to use; hopefully nobody gets soft-locked by it anymore. That's it for the additions I've made so far, I've tried to address all the major points of feedback, and there's still a couple of days left to work on it. I need to fix a few minor issues and also decide on a colour palette to use - perhaps add some post-processing as well. I would like to also get colour switching added to the UI if I have time. I wanted to make a stage model in MagicaVoxel to replace the level generator, but I'll add that only once everything else is done - if I do I'll be sure to update this devlog detailing it.
- Cody.
Dodgem.io
Status | Prototype |
Authors | Codemyster, levisgames, Jazzicon |
More posts
- Week 5 - Preparing for TestingMay 31, 2024
- Week 4 - Voxels and Optimisation PurgatoryMay 20, 2024
- Week 3 - Local Multiplayer and Controller SupportMay 19, 2024
- Week 2May 05, 2024
- Week 1May 05, 2024