###How did you make the tunnels?
I used geometry to write to the depth buffer, and "trick" the renderer to cut a hole in the terrain. See the tutorial [here](https://www.youtube.com/watch?v=cHhxs12ZfSQ)
###How did you write the networking code?
I used the Unity [low level networking layer](https://docs.unity3d.com/Manual/UNetUsingTransport.html) as a base, and wrote my own network management on top. See [this tutorial](https://gintas.io/unet-llapi/) as a good starting point.
You can look through the (depreciated) [unity source code](https://github.com/jamesjlinden/unity-decompiled/tree/master/UnityEngine.Networking) for their High level API for networking, and take methods out of their for your own purposes.
###Which data structure should I use?
The right one! Choosing the right data structure is very important. Consider the [costs involved](https://theburningmonk.com/2011/03/hashset-vs-list-vs-dictionary/) with adding, updating, and retrieving from your chosen data structure. What part will you be doing more?
###How to write performant network messages?
Most importantly, don't send information you dont need to! Consider both how and what you are sending. Consider when and how often. Consider why you are sending it.
Make sure you are considering the [size of the data](https://condor.depaul.edu/sjost/nwdp/notes/cs1/CSDatatypes.htm) you are sending. Consider joining your data into a byte array, and compressing that array before sending it.
I have been working towards consolidating data I need to send in byte arrays, and then splitting the byte array up into fragments to send to the client. This is because routers can only handle relatively small "packet" sizes (~1.4KB).
###You talked about "envelope cost" for network messages? What is this?
When you go to send a message you need to include some data to tell the client what the message is and how it is to be handled. I call this the "envelope cost", like the weight of the envelope when sending a letter. This is actually quite significant, and constant. Say 200B per message. So if you're sending 20B or 1000B of data, the envelope cost is the same. Hence - it's really important to make every byte count!
So what i have been doing is making a big byte array, putting all the data in it, breaking it into fragments, and sending the fragments to the client. I am even compressing (just once!) the overall byte array, balancing the cost of compressing against the saving of size.
###How did you draw so many trees?
The trees don't "exist" as a game object. I record their position, and I send an array of them to the GPU using Graphics.DrawMeshInstanced. See more at [this tutorial about how to do this](https://toqoz.fyi/thousands-of-meshes.html). There is even better ways to do this using compute shaders!
###What is "Garbage Collection" and why do you talk about it so much?
In a managed language like C#, you are not managing your memory allocation - the compiler does that for you. The "garbage collector" runs through used memory, and checks to see if anything you have allocated there is used - if not - it frees the memory up.
This reduces the complexity of your code substantially, however, there is a cost as to run the garbage collection the whole game has to stop. This causes the dreaded "freeze" many unity games have.
The best solution here is to be very careful about allocating memory. Follow [these recommendations](https://unity3d.com/how-to/unity-best-practices-for-engine-performance) from unity.
There is a [great post](https://medium.com/lonely-vertex-development/how-we-optimised-our-scripts-in-unity-447924863b3a) about optimizing strings as these can be a major source of garbage collection as strings are "immutable" (they can't be changed, only re-instantiated. Remember; a string is simply an array of numbers!
There is also some great unity optimization tips in this [good post about the subject](https://danielilett.com/2019-08-05-unity-tips-1-garbage-collection/)
###How did you do the UI?
I made a very controversial decision to ditch Unity's inbuilt UI, and use [Dear ImGui](https://github.com/ocornut/imgui). What is that? It's immediate UI. Instead of caching UI elements, I draw all the UI at once, every single frame. Instead of making UI widgets, I create the UI realtime in the code. This is not standard practice, especially for games.
It is working for this project, but it would not be a good idea for many. This is a good example where it is about making sure you use the right tool for the right job. Carefully consider, test, measure, evaluate.
###How do I make splines?
There are Unity Assets you can use, but consider your use case. What are you trying to use a spline for? How many splines do you need in your game? Splines can seem very scary to make, but actually, they're [rather straightforward](https://catlikecoding.com/unity/tutorials/curves-and-splines/). Work on getting something working using that tutorial, and stick to the minimum and most performant solution you can muster.
###How do you generate mesh on splines?
Follow [this library](https://github.com/benoit-dumas/SplineMesh) as a base as an approach to bending and positioning meshes along a spline. You might need to modify for your purposes, but it's a great place to start.
###Why do you use dictionaries so much?
I try to cache data where possible. After all, we have plenty of memory! So long as you are careful about what you use as a key in your dictionaries, they can be a powerful tool for a project.
Checkout this [post about the subject](http://www.somasim.com/blog/2015/08/c-performance-tips-for-unity-part-2-structs-and-enums/) for advice about how to correctly use structs and enums in dictionaries.
###Dictionary v Matrix Array
Sometimes you can use a matrix array rather than a dictionary. Looking up an array is basically free, whereas even with a well optimized dictionary there is a cost.
If you know for sure the size of your world, instead of storing things in a dictionary with a key - make a multidimensional array and use the x and y for the positions in the matrix.
I started with a Dictionary for ease of use, and once I got things ready - I switched over to a 2D array. It took me about 40 minutes to make the change! It's an example about going for "easy" first and then refactoring later once you have your use case sorted.