To reason about the use of weapons, the AI has explicit representations of all the important 'objects':
-
The AI character is an Actor, who holds ammo clips, and carriers a number of weapons. The Actor carries multiple Weapons, but has only one of them in his hands (selected).
-
The Weapon solely represents the state of a weapon carried by the Actor: the weapon holds some ammo, is in a mode, may be engaged in a burst. The Weapon is of a specific WeaponType.
-
The WeaponType super-class defines the possible characteristics of WeaponType. All Weapons of a WeaponType have a certain fitness for some usage, feature the weapon modes, and have ideal burst lengths.
There sub-classes of WeaponType that define the exact properties and fitness for usage (as a virtual function), for example:
- the Hand Gun, which features single shot and automatic mode, and does not provide any restrictions on the burst length, since it doesn't have a significant recoil.
- the Assault Rifle, which features 3 round bursts, automatic mode and a 3xzoom, and has some recoil (so it suggests a restricted burst length).
- the Squad Automatic Weapon, which only has 'automatic' mode, and a significant recoil. It therefor is best fired in 5 round bursts.
-
The Threat is a potential target to aim for.
-
Aiming isn't done instantaneously and perfectly (that wouldn't be realistic). Instead, the Actor's aim depends on how well he sees the Threat, how long he has been looking at the Threat, etc. And if the Threat is hidden for just a moment by an obstacle, the Actor typically aims for the position the Threat will going to move to next, or the position the Threat will re-appear.
Instead of going through giant if-statements, weapon selection has become easier to write and understand:
Weapon Actor::SelectBestWeapon(distance, usage)
{
bestweapon = 0;
bestscore = 0;
for each weapon carried
{
clips = inventory[weapon->GetAmmoType()];
rounds = weapon->GetAmmo();
score = weapon
->GetWeaponType()
->GetFitnessForUsage(usage, distance,
rounds, clips);
if ( score > bestscore )
{
bestscore = score;
bestweapon = weapon;
}
}
return bestweapon;
}
The real gain is not so much in the simple weapon selection algorithm, but in the fact that we can take care of all the specific weapon's pecularities in its own WeaponType function (a member function overriding the virtual member function in WeaponType):
float SquadAutomaticWeapon::GetFitnessForUsage
(usage,distance, rounds, clips)
{
// not useful for anything
// but suppression and direct fire
if ( !(usage & (eDirectFire | eSuppression) )
return 0;
// fitness great for nearby
// less for long distance
float fitness;
if ( distance < 100 )
fitness = 1.0;
else
{
fitness = 100 / distance;
// correct for low on ammo
// since reloading is slow
if ( rounds < 10 )
fitness *= 0.75;
// if not much ammo in total
// reduce fitness some more
if ( (clips == 0) && (rounds < 50) )
fitness *= 0.75;
}
return fitness;
}
Note that the method deals in a simple and structured way with detailed properties of a 100 round belt-fed heavy machine gun. You don't run the risk of affecting other weapon's properties by changing it.
This same format, of having a dedicated method per weapon, allows you to take different matters into account when defining the fitness for a bolt-action sniper rifle.
The actual CGF for AQ2 code deals with more factors, including weapon add-ons (silencer, laser sight), weapon modes (3 round burst or full-auto, zoom levels), and game round duration (early in the game round, ammo conservation isn't that important).
The CGF WeaponType class also implements a lot more weapon properties, such as aiming preferences (hand guns cannot penetrate body armor, and should be aimed for the head or legs, sniper rifles aim for the largest portion of the body (torso), assault rifles on full auto aim for the feet since the muzzle will be kicked up, etc.).
This weapon model also enables simple and understandable weapon handling code:
Weapon Actor::HandleWeapon(threattracking)
{
if ( !threattracking)
{ // no threat, take care of the weapons
Weapon otherweapon;
if ( SelectedWeapon()->IsEmpty() )
SelectedWeapon()->Reload();
else
if ( otherweapon = GetWeaponNeedingReload() )
{ // select now, reload when active
SelectWeapon(otherweapon);
}
}
else
{ // threat! so aim and fire unless empty
AimResult_t aim; // tells how the aim and weapon is
// aiming also has bot turn towards target
aim = SelectedWeapon()
->AimAndDecideToFire(threattracking);
// test for good aim that isn't blocked
if ( eFire == (aim & (eBlocked | eFire)) )
SelectedWeapon->Fire();
else
if ( aim & (eEmpty) )
ReloadOrSwitchWeapon();
else
if ( aim & (eAim)) // thus eAim without eFire
{ // aims needs to improve or trigger should
// be released to reduce recoil (burst length)
/* do nothing but keep aiming */
}
}
}
By having the Weapon and (not shown) the WeaponType handle the fire, we again can handle the peculiarities of the weapon in a single method. The code above suggests how a Squad Automatic Weapon can be used without the recoil growing too large. Probably every burstlength shots, the Weapon just does not return the eFire bit, and the Actor will ease of the trigger.
The actual CGF for AQ2 implementation (as released in December 99) deals with a number of other factors, including prediction of threat movement, prediction of threat 're-appear locations', weapon mode switching during the attack, scanning during sniping operations. It also knows dirty tricks such as taking the time to steady its aim when attacking a threat in the back (since the threat won't attack immediately)...
Depending on the weapon aspects being simulated, this design can be extended. Some extensions are straightforward, but other extensions really result in more complex designs.
Clips
Some mods (including AQ2) have the Actor carry clips, not rounds. They reload clip by clip. Clips released from the weapons take 'home' the remaining rounds in that clip. Here, it makes sense to model the clips explicitly (since they probably can be picked up), and do the ammo computations with rounds in the weapon, and clip sizes.
Other mods (notably Counter-Strike) have 'virtual clips', and just shuffle additionals rounds (up to the clip size or ammo amount) into the weapon, when 'reloading' (CS seems to inherit the basic HL weapons code). Modeling the clips explicitly doesn't make that much sense.
Reload Activities
Most weapons will require one single (short) activity to reload it. There are, however, exceptions: (semi)-automatic shotguns and bolt-action (sniper) rifles typically combine internal ammo (round) storage with the ability to reload individual rounds. In that case, two modes of reloading are available: loading a single round, or loading a series of rounds. At ease, the Actor typically prefers loading a series of rounds, whereas in engaging a threat, the Actor will want to fire as soon as possible (after loading one single round).
Ammo Types
Some tactical shooters (notably R6: Rogue Spear) differentiate between different kinds of ammo. Examples including full metal jacket (FMJ) rounds that penetrate through targets and walls, jacketed hollow point (JHP) rounds that do not penetrate, subsonic rounds (less noise), slug / buckshot shotgun rounds, and tracer rounds.
Here it makes sense to model even the ammo types explicitly.
'Power up' Weapons / Long reload times
In a few shooters, some (but not all) weapons need a significant amount of time from the moment they are selected to the time they can be fired. Examples included: the gatling gun (HL TFC), heavy sniper rifles (R6 Rogue Spear), and hand grenades for which the pin is to be released first.
Similarly, once a weapon is being fired (i.e. the pin is pulled), it is important to continue to aim and fire in the direction intended.
Again, for the AI to take these into account, it is best to model these properties explicitly as an attribute / function call of the Weapon. In addition, the preferences for rapid weapon switching and activation should be taken into account when trying to select the best weapon (as an additional parameter in 'GetFitnessForUsage').
'Twin Weapons'
Some game feature 'weapons' composed of two guns. The AQ2 and CS akimbo pistols of course come to mind. These akimbos can be treated as a single weapon with two clips and twice the rate of fire. They do not cause the AI problems in considering these guns.
For combinations of an assault rifle and grenade launcher (such as the M4/M203 in CGF), or an assault rifle and shotgun (in Delta Force), things get more complicated.
A rifle/grenade launcher in theory is capable of direct (rifle / grenades) and arc trajectory (grenades, 'lobbed' over obstables) fire. Reload times differ for the clip and rifle grenade. A laser-sight and scope typically apply solely to the rifle, and is of little assistance in aiming the grenade launcher.
Thus actual fitness for a task depends very much on the availability of (two different kinds of) ammo, and whether rounds of these types are in the weapon.
I've tackled these twin weapons by taking into account two types of ammo in all WeaponType and Weapon instance interfaces (assuming weapons consuming yet another kind of ammo won't show up in a game).
Arc Trajectory Weapons
Already briefly mentioned above, arc trajectory weapons shoot projectiles that do not travel in an almost straight line, but instead along an arc (due to their slower speed). Examples include: hand grenades, throwing knifes, and rifle grenades.
These weapons, compared to rifles and hand guns, complicated matters by:
- having a range typically shorter than the map's dimensions
- having the actual range (partially) depending on the height difference between the attacker and the target
- having a minimum range (due to the damage radius) for rifle grenades and hand grenades
- being able to seriously damage the target when positioned several meters away from the target's actual position (for grenades)
- being able to cause friendly fire accidents (for grenades)
In other words, arc trajectory weapons are complicated to aim. Whereas for 'normal' rifles holds "what you see is what you can hit", trajectory weapons require checks for the complete trajectory.
In addition, the Actor may be required to aim in a direction different from the target: notably the pitch (up / down) angle will be different, and to lob grenades through a window to a spot near enough to the target, the yaw (left / right) angle might be different as well.
Handling these trajectory weapons requires some special provisions in the aiming and aim evaluation code.
For more info on aiming and vision, please visit this page.
William
date
|
D
|
description (incl. link if applicable)
|
Nov 22, 2000
|
+
|
extened complications with reload activities
|
Sep 17, 2000
|
+
|
listed possible extensions and complications
|
Sep 14, 2000
|
o
|
initial version
|