For a mobile game I’m working on I decided to implement an octree as a programming exercise. The purpose of the octree would be to store nodes in my waypoint graph, to efficiently find which waypoint a unit was on. For various reasons, a regular grid was not the optimal choice. Just creating the waypoint graph was a problem in C#/Unity, because if each waypoint stored references to its neighbours I would get serialization depth warnings and my waypoint graph would not serialize properly. This is because the serialization avoids reference loops by aborting serialization when the serialization recursion gets too deep. However the serialization recursion doesn’t differentiate between problems like instances A and C referencing each other, versus recursion depth due to tree and graph structures.

The solution to this was simple. I put all the waypoints in a list and their neighbours were stored as indexes of that list. As it turns out this is the best way to do any serialization of trees and graphs. When I had to make a octree I followed a similar idea: put all the octree nodes in a list. For most operations, a node may need access to the list of nodes in order to get to the children. I settled on just passing a reference to the list of nodes for any operation that needed the list of nodes. It’s fairly inelegant, but I preferred it to the alternate solutions:

Do not serialize the references to the children, and on deserialization get the references using the indexes of the children. Problem: I didn’t want to essentially double the memory needed to store the references to the children. I was constrained for memory.

Give all nodes a non-serialized reference to wherever the list of nodes is stored. Set this reference upon deserialization. Problem: I prefer to only let the nodes be accessible when they should be needed, instead of at any time. Also these kinds of deserialization operations can cause jarring performance hiccups. I don’t like to add to the enormous amount of things that already have to happen when the game starts up.

Have the nodes have some kind of getter to get a specific node. Problem: I don’t want the overhead of a function call every single time a operation needs a node.

When I was making the octree, I realised that when I serialize the octree I can’t have the actual waypoints in the octree, because the serialization will duplicate the waypoints instead of having them reference the ones in the waypoint graph. So it made sense to have the nodes be integers that reference indexes in the waypoint list. The issue is I wanted for my octree to be generic, so that I could reuse it in other projects. Usually, you would declare a generic octree as follows:

public class Octree<T> where T : BoundingBox {…}

BoundingBox would be a interface with a method like Bounds GetBounds()

A integer is a defined primitive type in C# (and every language I know of) so I can’t make it extend a interface. My solution was to have the Octree constructor require a pointer to a method that gets the bounds given in instance of T. Then, I defined a method in my waypoint graph class that would, given a index i, return the bounds of that waypoint. This worked perfectly. I see it as the best solution possible in this particular unusual instance.

There is a issue to bear in mind, namely that this workaround can cause efficiency problems. If we can get the bounding box straight from the instance of T, and T is responsible for updating the bounding box, we still maintain efficiency since we’re just getting a bounding box. In the case of my workaround, I had to keep remaking the bounding boxes because the waypoints don’t actually have their own bounding boxes. In this case I avoided an efficiency disaster because all the bounding box instantiation and waypoint insertion did not happen at runtime, so i could afford the minor delay. Also, the bounding boxes of the waypoints all had the same size, so I could pre-calculate that.

Finally, something to remember when using Octrees. There are two ways to handle objects lying on the bounds of where you’d usually split an octree node. One is to divide the node and put the object in all the children. Another is to store the object in the node rather than split it. I went for the second option. If you do go for the second option, make sure that the horizontal divisions of your octree do not line up with the ground, or most of your nodes will end up in the root node and you will end up killing your efficiency.