The HUD now has helpful elements like health, energy, and game stats.
But I found the debug lines to the ships that were attacking me, actually made the game more enjoyable.
So I polished that up and made a real HUD system that has the debug lines.
To make the ships more visible, I added a red highlight over them using the stencil buffer.
I also created a tool that let's me get a bird's eye view of AI dog fighting.
Having that made it much easier to iterate and change AI behavior.
There were several issues when AI actually fought a player, that were not present in AI vs AI dogfights.
The AI ships were stay too close to the player.
So I added some logic to help space the AI out.
I made this more graceful by having the effect applied weighted on distance.
But tweaking the AI proved difficult.
It was either too hard or too easy.
I decided to blend between the easy AI and the hard AI over time.
This made the enemy ships feel much more dynamic.
Another problem I encountered was that the player could end up in a state that it didn't naturally get much action with the AI controlled ships.
So I created a system checked every so often if the player was in combat.
If it wasn't, then it would assign a nearby ship to engage the player.
This created a constant feeling of pressure on the player, that it was being attacked.
While I added the ability for objectives to function in a previous update, they didn't have a natural targeting system.
But just having objectives target nearby ships isn't as simple as it sounds.
Querying an arbitrary number of spatial hash grid cells each frame is expensive.
So rather, I came up with the idea to amortize the targeting over multiple frames.
I was already doing a walk over all the ships each frame.
So I decided that for this walk, it would process 4 ships for targeting.
This meant that for a given frame, only a few objective processing actually happened.
This works and I can't even notice it with the objectives.
They just work at no noticeable performance cost.
That is, instead of being O(ships + objectives) is O(ships + 4) which is O(ships);
And since each frame already walks O(ships) we're not adding anything to the runtime complexity.
I also needed the AI to naturally take down the enemy carrier ship.
That can only happen if the ship has all its objects (turrets, satellites, and generators) destroyed.
So now enemy AI fighter ships will do bomb runs on the carrier ship objectives.
This is implemented as a special case in the AI fighter behavior tree.
One problem is that the player could just destroy many objectives without really being challenged.
To solve this, when a carrier ship loses an objective, it spawns a new fighter ship to fight the player that destroyed the objective.
This newly spawned ship have their "avoidance" so the fly closer to the carrier ship; this makes them much more deadly towards the player, at the cost of that they sometimes collide with the carrier ship.