June was an interesting month. I randomly wondered if I could add a dragon to Windward just for the fun of it. It took only a few minutes to find a suitable model on the Unity's Asset Store and about half an hour to rig it up to animate based on movement vectors. I then grabbed a flying ship from Windward, replaced its mesh with a dragon and gave it a shot. It immediately looked fun, so somehow I ended up spending the next several weeks adding tough dragon boss fight encounters to Windward, complete with unique loot that changed the look of some key in-game effects based on what the player has equipped. The dragon fights themselves were a fun feature to add and made me think back to the days of raiding in WoW. Ah, memories.
With the odd detour out of the way I had another look at the various prototypes I had working so far and decided to narrow the scope of the next game a bit. First, I'm not going to go with the whole planetary scale orbit-to-surface stuff. Reason being the size of it all, mainly. The difficulties in dealing with massive planetary scales aside, if a game world is the size of Earth, even at 1/10th the scale, there's going to be a tremendous amount of emptiness out there. Think driving across the state for a few hours. Entertaining? To some maybe. But in a game? Certainly not.
But anyway... game design decisions aren't worth talking about just yet. Once the game project is out of the prototype and design stage, maybe then.
The past two weeks I actually spent working on integrating a pair of useful packages together -- Ceto ocean, and Time of Day. Both are quite excellent and easy to modify. Ceto ocean kit in particular occupied most of my time -- from optimizations to tweaks. I integrated it with the custom BRDF I made earlier, fixed a variety of little issues and wrote a much better and robust buoyancy script, which is a far, far better way of doing ship mechanics than the weird invisible wheel approach I was taking for Windward. I'll likely post a video about it later.
With my focus on optimizations, I've been keeping an eye on what it would take to have an endless terrain streamed in around the player and the results have been promising. In Windward, the trees were actually generated by instantiating a ton of individual trees in the right places, then subdividing the region into smaller squares, followed by merging all the trees in each square into a group. The process worked fine, but had two drawbacks.
First, it was using Unity's Mesh.CombineMeshes() function, which while works well, requires the objects to be present and doesn't allow per-vertex modifications for things like varying colors between trees. Second, with the merging process taking just over 100 milliseconds, it's really not suitable for streamed terrains. A 100 millisecond pause is very noticeable during gameplay. And so, I set out to optimize it.
The first approach I tried was using custom mesh merging to see the effect. It was predictably slower -- almost 170 ms.
Next I spread the actual code that was performing combining of mesh data into a separate thread:
While spread out across multiple frames 95 ms on the first frame was still way too much. Thinking about it I first focused on the small stuff. I first replaced mesh.colors with mesh.colors32 and then moved the matrix creation code into the part that's done on a separate thread instead of out in the main one. With a few other minor changes, such as replacing Generic.List with TNet.List, the update was down to below 70 ms:
Getting closer. The next step was to eliminate the interim instantiation of objects. After all, if all I want is the final merged mesh, why instantiate game objects first only to merge and remove them? It makes a lot more sense to skip the instantiation part altogether, and just go straight to merging, doesn't it? The mesh data can be retrieved from the original prefabs. This also fixes another issue I noticed: I was apparently pulling the mesh data from every object individually by calling mesh.vertices and other functions on each of the MeshFilters' meshes. Adding a cache system into place would save a ton of memory. Perhaps you've noticed those 15 MB+ memory allocations in the profile snapshots above -- and this was the reason.
With the changes in place, the cost of merging 2205 trees was down to 16.9 milliseconds with memory usage down below half a meg:
In this case the trees themselves are just an example as they are very simple and I will likely replace them with something that looks better. Still, for the sake of a test, they were perfect. Who knows what I may end up using this script for? Random vegetation? Rocks? Even just debris in the city ruins -- either way, this multi-threaded optimized merging script should now be up for the task and the extra variation in color hues makes this approach look much better than Unity's built-in mesh merging. All in all, another handy tool.