Simple RTS unit AI.

Started by
2 comments, last by NEXUSKill 12 years, 11 months ago
I've been putting off working on this because I can't seem to find a clean solution for it. I've made a simple AI that moves around on command, attacks enemies with whatever weapons it has, but I'm stumped as to how to write a pursue state without duplicating code from both the walk and attack states. Doing single states that handle their own thing is fine, but when trying to combine them in different ways makes me wonder how I'm going to go about implementing them.

For example, say you order units to move to a specific location but along the way the units encounter an enemy. You'd want them to take a few shots at them before continuing on, without pursuing them. The problem is with the states changing so often it's hard to keep the orders between the different states. The only other thing I can think of is that the global state handles orders, and tells the secondary state to work on those orders. But even that is kind of messy.

Has anyone worked on something similar?
Advertisement
Why not have a list of prioritised actions for each unit? For example, when the unit is travelling it has walk as priority0 action. As it moves it encounters units which it passively defends itself against as it walks, so it inserts a second action 'shoot' which has priority 1, so getting away is the higher priority. Now imagine it has priority 0 for attacking a unit and runs low on health, a priority 1 walk action could be inserted if it is losing the battle, causing the unit to retreat. So long as you always select the most vital action as highest priority this should create some interesting behaviour and this will allow for more than just two actions per unit at any one time. What do you think?
What I did was put "atomic" commands in the units (move_to, turn_to, attack etc.). Then there is an Order class that contains a step/execute method which is called when the unit is stepped. So the move command would look something like:

class MoveOrder {
Location destination;
...
void step()
{
if(unit.move_to(destination)) //move_to returns true if the location has been reached
this.setFinished();
}
}

The attack command looks like:

class AttackOrder {
Enemy target;
...
void step()
{
if(unit.is_in_range(target))
unit.attack(target)
else
unit.move_to(target.get_location());
}
}

The attack move looks like:

class AttackMoveOrder {
Location destination;
Enemy target;
...
void step()
{
if(!unit.is_in_range(target) || target == null)
target = unit.choose_target();
if(target != null)
unit.attack(target)
else if(unit.move_to(destination))
this.setFinished();
}
}


These orders are then pushed into the command queue of the units and only the top one is executed.

You still end up with some code duplication and the actual orders are slightly more complex than the examples here. But it at least allows me to write the orders in a very straight forward way and most of the units specific behavior is contained in the atomic commands.
I think you need to make a distinction between Tasks and Orders, accomplishing an order might require multiple tasks, certain tasks not strictly necessary to follow the order might be active non the less depending on a unit's state, for instance, an agent in aggressive stance ordered to move from A to B should shoot while moving if it comes across an enemy, a unit in a passive or evasive stance following the same order would avoid shooting, specially if that causes it to move slower, it would assume escape is preferable.

This way, Attack is an order, Shoot is a task needed to complete that order and if the target moves Chase is another task needed which is in itself completed through the Walk task along with the Face Target task, however Shoot can be performed in other contexts.
Go To is an order, Walk is a task, going to might include shooting without changing the move task.


Establish a clear hierarchy, implement tasks with the simplest possible logic and achieve complex behavior through their combination and ponderation.

Define which tasks can coexist and which tasks require others to cease before being performed, for instance, some units might be able to shoot while running and others might need to stop in order to shoot.

Apply weights to the importance of emergent tasks, for instance if a unit can heal, the importance of performing that task before attacking becomes increasingly high as the unit becomes more and more wounded, eventually the unit will cease fire and heal before resuming attack.

If you simulate reloading for instance, reloading becomes increasingly important as the weapon clip gets emptier, while under fire, the unit might prefer to shoot the weapon empty before reloading, but if it killed the last enemy and the clip is only half empty, it should still reload to be better ready for the next combat.

Game making is godlike

LinkedIn profile: http://ar.linkedin.com/pub/andres-ricardo-chamarra/2a/28a/272


This topic is closed to new replies.

Advertisement