I created a behavior tree system that is defined completely within c++.
AI entities have a brain which spins up a behavior tree.
The nodes in the behavior tree are heap allocated, which I believe does have some noticeable performance cost.
The reason for this was to easily use virtual methods and inheritance of nodes.
But I think I should investigate managing the memory so that they are all allocated in a contiguous block.
Though I am not certain that is worth doing without a bit more profiling.
The problem I observed is that it is much faster to encode logic within branches (ie if statements) than it is to use the structure of the tree to perform logic.
I think that will be true regardless of how I create the true.
But perhaps worthwhile for a refactor.
All that being said, the performance is not bad with a large number of ships.
The behavior tree nodes are partly event based; using the delegates I created as a way to implement communication events.
For example, nodes listen to updates to the tree's memory (eg like blackboards in other behavior trees).
When a reference to something in memory changes, a delegate is broadcast, and nodes can respond to the change (such as updating cached state, etc.).
However there is some ticking involved in querying state. That can be converted to being event based but that has not been done yet.
When a node in the tree finishes, it broadcasts an event and causes the tree to process another node.
I added a compile time flag to turn on debug logging, but it can be compiled out and removed from the executable.
You can also filter which AI entity you are debugging with a little bit of work with breakpoints and setting values directly in the debugger.