You aren’t thinking enough how humans actually do this in real life (as suggested by IADaveMark).
A human can be in any state, focusing on any specific task, and still prioritize dodging incoming fire. In other words, following, pursuing, and fleeing are actual
states (a human tends to focus on only one of these at a time—blending doesn’t make sense), and dodging is a
persistent behavior.
States:
- Follow: You simply follow the target without aggression. The state-specific code would be basically just to keep up with the target up to a certain range. It tracks when no longer in range and then resumes finding paths to the target and adjusting speed in order to get back into range.
- Pursue: Actually attack the target. While this sounds like “Follow + Attack” it is a completely different routine/state. You would use the same A* routine to find a path to get into attack range (which will be larger than the Follow range), but once in range the movement algorithm is designed for positional strategies, setting up shots, hiding behind objects, flanking, etc. It’s a completely different movement routine plus a routine for deciding when to fire, etc.
- Flee: Another movement routine, except that you are trying to get away from the target.
Each of these states will at some point generate a force vector that should be applied to the ship, because they largely are designed to move the ship (some can also trigger bullets etc.)
Persistent behaviors can generate their own vectors as well, and a factor can be applied to decide how much of each vector to apply to the ship.
The “Dodge Bullet” persistent behavior would check for incoming bullets and generate a force vector that would push it out of the way.
The “Dodge Missile” persistent behavior would do the same but might have a more advanced technique for getting out of the way of homing missiles (looking for the nearest asteroid and hiding behind it, turning to shoot it, etc.)
In addition to returning a dodge force vector, each routine would return a value from 0 to 1 indicating the urgency of this dodge. Just as humans take incoming fire more seriously the closer it gets (and if it is on a collision course).
This urgency value is used to decide how much of the dodge force to apply in relation to the regular state force.
The whole routine would look like this:
- Run current state logic, get a default force vector “defaultForce”.
- Run the dodge behaviors, get all of their force vectors and their urgency values.
- Combine the dodge vectors into 1 based on their relative urgencies and reduce to a single urgency value.
- For example by summing proportionally to each urgency value, or by picking only the most urgent value, etc. Your choice.
- Use this urgency value in a lerp() between defaultForce and dodgeForce. lerp( defaultForce, dodgeForce, dodgeUrgency ).
- Apply this final force to the ship.
This mimics how humans behave. We keep an attack or flee overall objective in mind, but we never forget to dodge and we never forget our goal just because we are dodging. Dodging isn’t a state. It’s a persistent behavior.
L. Spiro