Advertisement

3D Combat Best Architecture

Started by March 21, 2016 06:33 PM
4 comments, last by danjr26 8 years, 8 months ago

I am having a bit of trouble coming up with the best architecture for my 3D space shooter AI. I want it to be able to choose and attack enemies, dodge projectiles, missiles, and obstacles, and follow missions specific to each level (guard or attack specific entity, fly in formations, etc).

My first attempt was to create weighted vectors for all the various impulses and add them all together, then follow the resultant vector. This was effective at dodging other ships, but made accurate aiming difficult with all the noise, and the enemies ended up just circling around my ship if I stood still.

I then thought about using a state machine, with states like Pursue, Flee, Dodge Missile, Dodge Fire, etc. However, if a ship is being fired upon, I don't want it to completely forget about the ship it was following or the mission it was completing due to the state change, so I need at least some vector blending.

Is there an architecture out there which offers more of what I need? Or is my code destined to be convoluted and terrible?


I don't want it to completely forget about the ship it was following or the mission it was completing due to the state change, so I need at least some vector blending.

That's not necessarily true, you might be able to get away with a stack of states, and certain states might clear the stack, or just add to it. A dodge state could push itself onto the pursue state, then get popped after the danger has been avoided, and return to pursuing. You might have to experiment, I could see this being too binary, and you might need to start blending, but then you might fall into the trap of the first system you had.

Advertisement

You are conflating decision-making and movement. Those are not the same thing. The first thing you need to do is decide what you are doing (e.g. pursue, flee, dodge missile, dodge fire, etc.) and then decide how you are doing it. For example, the movement for pursue is very different than that of dodge missile.

I will give you the same advice I give everyone else... analyze very carefully how you play the game and think about what you would need to do to codify that.

Dave Mark - President and Lead Designer of Intrinsic Algorithm LLC
Professional consultant on game AI, mathematical modeling, simulation modeling
Co-founder and 10 year advisor of the GDC AI Summit
Author of the book, Behavioral Mathematics for Game AI
Blogs I write:
IA News - What's happening at IA | IA on AI - AI news and notes | Post-Play'em - Observations on AI of games I play

"Reducing the world to mathematical equations!"


You are conflating decision-making and movement. Those are not the same thing. The first thing you need to do is decide what you are doing (e.g. pursue, flee, dodge missile, dodge fire, etc.) and then decide how you are doing it. For example, the movement for pursue is very different than that of dodge missile.

Yes, the conflation was one of the problems with my first attempt. However, my problem in planning a better way is that I want some "decisions" to be blended (i.e. dodge fire and pursue) while others I do not (i.e. pursue and flee). What are your thoughts on the best way to go about this? Should I have separate states for each blended combination? Or something else?

Thank you for replying, by the way.

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

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

Thank you L. Spiro. That was exactly what I was thinking of.

This topic is closed to new replies.

Advertisement