This section hopes to explain some of the design of the AI code for the units in YARTS.
Basic description
All of the vehicles in YARTS have a "brain" that controls how that paricular unit moves.
This is like a driver in the vehicle telling the vehicle what to do, as the vehicles cannot move
on their own. The brain receives the orders from a "player" (ai or local) and follows the
instructions of that order. There are various types of orders - not just to move the vehicle
to a particular point but also to attack, guard, etc. These will all be dealt with below.
Detail
At each timestep every brain looks at its order queue where its orders are kept and retrieves all
the new orders. It then decides which order to carry out the orders. Most of the orders overwrite
all the existing orders but some orders do not. For example when doing a formation move the units
are told where they are going but they are also given a "Delayed turn order" so that when they get
there they are all facing the same direction. This means that on receipt of a delayed turn order,
the other previous orders are not deleted.
When an order is received, it is classified, the orders that clash with it are deleted and the order
is given to its "holder". The holder is an "OrderState" that is individual to each type of order,
so a moveOrder is given to the moveOrderState. (The brain has one "order state" per type of order)
A particular orderState knows about its type of order and gets information from the order, e.g. the
order might contain a direction and a distance to move or a set of co-ordinates. The orderState knows
what information it's expecting and how to use the information contained in the order.
Once the brain has all the new orders it decides which order should be made the currentOrder. The
brain then carries out the currentOrder whether this be a turn order, a move order or whatever.
The brain calls action from the currentState and it decides what it wants to do and tells the brain.
The brain then gives the unit one set of orders and instructions about what it is to do. The
instructions the unit gets are the raw instructions like speed up, slow down, fire your weapon, turn
left, turn right, etc. If the brain tells the unit to fire its weapon, it literally means fire the
weapon regardless of whatever direction it is facing - just fire now.
History
The design of the brain (AI) has gone though many iterations in a trial and error process to see
what design best fits the game. Building up experience in game programming and OO design also
played a part.
The Architecture
Fig 1. - Hierarchy of main unit AI classes
The Brain has a group of data structures that are all derived from a single State. The "State"
class is the class that all other states are derived from. In this abstract base class there are two
abstract functions:
Functional States
The functional states are states that do not have orders but are actually the movers and shakers.
This means that the functional state are the primitives that move the unit. These Functional
States are the moveState, turnState, attackState, stopState and collisionState. Even some of
these functional states have other functional states in them. For example the attackState has to
be able to fire on a target and also to move within range of that target, so instead of
duplicating the moveState code the attackState has a moveState (so that if it wants to move it
tell the moveState the where it wants it to move to and calls the moveStates action command).
This means that if you want to move anywhere you use the moveState - which is also where
the collision avoidance code lives. If this code detects that a collision will happen, the
moveState calls the collisionState into action, which will try to avoid the collision.
Order States
The Order States are where the orders reside inside the brain. The order states know how to
decode their orders and have the particular functional states that will allow the order states
to carry out the order. For example the attackUnitOrderState only has an attackState (functional
state), it tells the attackState the co-ordinates of the unit it wants to attack and calls action()
on the attackState. It does NOT have the moveState, the turnState or the ability to tell the unit
to fire.
For every type of orderState (e.g. a moveOrderState) there only needs to be one copy of each actual
state (e.g. moveState) For example, there is only one attackState (functional state) and the
attackUnitOrderState and the attackPositionOrderState have access to it. This means that the
functional states have to be told at each timestep what they are to do. But it cuts down the
complexity nicely when you know that the only state that can actually tell the unit to move (via
the brain) is the moveState.
The Present state of the brain
The AI code has been redesigned a few times and I am now happy with the way it performs and do
not foresee the need to re-write/re-design it - some of its benefits are still coming out.
Improvements
Possible improvements to the ai section come in the addition of new orders. The order in
production at the moment is the collision avoidance order. This order as you can imagine is
not an easy order to implement, as it has to do a lot of prediction work, then instruct the unit
to turn to a particular direction. So far, this order has taken the majority of the time coding
the AI - over a period of 15 months(!)
Most of the brain re-writes have been triggered by the writing of the collisionState. Now that
the brain is in a stable condition, the collision code becomes the main focus once more. The
collision code already works, but it is not finished to a completely usable (perfectionist!)
standard. I have solved most of the major problems like :