For a while I assumed that their poor accuracy was mostly due to limited facings, but I'm increasingly convinced that they tend to aim behind their target. It also affects Volkov, who has 40 facings.

Could it be that the vector that adjusts their aim point based on the target's velocity is in the wrong direction?

Aim test.PNG
Aim test.PNG
xml
AIAimTest.xml
assumedpseudonym 24 Jul 2015:

 This happens with AI ships with 120 facings as well.
 This does seem to be an issue, and it’s generally pretty obvious on the intro screen: Get a couple of opposing gunships against each other and the game will occasionally get bored with the one that has focus and call for a new ship before either is destroyed — particularly when they both just try circling one another.
 It’s not just against moving targets, either. Often one of the best defenses against a swarm of gunships without tracking or omnidirectional weaponry is to simply stop, not move, and shoot them when they swoop in front of you.

nms 24 Jul 2015:

Maybe they're failing to account for their own velocity then?

digdug 30 Jul 2015:

we definitely have to try with 2 gunships with fireAccuracy= "100"

As most AI ships have 70-90 I'm expecting them to miss.

george moromisato 15 Aug 2015:

I think the problem is that we can't predict acceleration changes. The current algorithm works only if the target is consistently moving in a straight line.

But if the target is accelerating (e.g., in a circle) we can't predict the final velocity (because the target could stop accelerating).

This is very hard to fix, unfortunately.

nms 15 Aug 2015:

It's definitely not just due to target acceleration. AI ships fairly consistently miss-aim at targets that are moving in a straight line or - as assumedpseudonym pointed out - stationary.

My latest hypothesis is that once the difference between the angle they're facing and the optimal angle gets below a certain value they start firing, but stop trying to fix their aim. This makes sense when they have limited facings, but the angle doesn't seem to get smaller for ships with more facings.

Also, if there's a delay (even one tick) between when they calculate the optimal angle and when they fire, that could cause them to fire behind a moving target.

nms 31 Jan 2016:

Edit: I didn't actually find the problem, but I did find a simpler formula for CalcInterceptTime:

Metric CalcInterceptTime (const CVector &vTarget, const CVector &vTargetVel, Metric rMissileSpeed, Metric *retrRange)

//	CalcInterceptTime
//
//	Returns the time that it would take to intercept a target
//	at vTarget, moving with velocity vTargetVel, with
//	a missile of speed rMissileSpeed. Returns < 0.0 if the missile cannot
//	intercept the target.
//
//	The formula for interception is:
//
//			A +- B sqrt(C)
//	t	=	--------------
//				  D
//
//	Where	A = B rVi
//			B = 2 rRange
//			C = rMissileSpeed^2 - rVj^2
//			D = 2 (C - rVi^2)

	{
	Metric rRange = vTarget.Length();
	if (rRange == 0.0)
		return 0.0;

	CVector vPosNormal = vTarget / rRange;

	if (retrRange)
		*retrRange = rRange;

	//	Compute the orthogonals of the velocity along the position vector

	Metric rVi, rVj;
	vTargetVel.GenerateOrthogonals(vPosNormal, &rVi, &rVj);

	//	Figure out the inside of the square root. If this value is negative
	//	then we don't have an interception course.

	Metric C = rMissileSpeed * rMissileSpeed - rVj * rVj;
	if (C < 0.0)
		return -1.0;

	//	Figure out the denominator. If this value is 0 then we don't
	//	have an interception course.

	Metric D = 2 * (C - rVi * rVi);
	if (D == 0.0)
		return -1.0;

	//	Compute A and B

	Metric B = 2 * rRange;
	Metric A = B * rVi;

	//	Compute both roots

	Metric Z = B * sqrt(C);
	Metric R1 = (A + Z) / D;
	Metric R2 = (A - Z) / D;

	//	If the first root is positive then return it

	if (R1 > 0.0)
		return R1;

	//	Otherwise we return the second root, which may or may not
	//	be positive

	return R2;
	}

That formula comes to:

t = (2 rRange * rVi +- 2 rRange * sqrt(rMissileSpeed^2 - rVj^2)) /
           2 (rMissileSpeed^2 - rVj^2 - rVi^2)

  = rRange (rVi +- sqrt(rMissileSpeed^2 - rVj^2)) /
           (rMissileSpeed^2 - rVj^2 - rVi^2)

Here's my analysis:
(Vsi and Vsj are the radial and tangential components of the shot velocity relative to the firing ship.)

rRange + t * rVi = t * Vsi

t * rVj = t * Vsj -> rVj = Vsj {unless t = 0 -> rRange = 0}

Vsi^2 + Vsj^2 = rMissileSpeed^2 -> Vsi = +- sqrt(rMissileSpeed^2 - rVj^2)

t = rRange / (Vsi - rVi) = rRange / (+- sqrt(rMissileSpeed^2 - rVj^2) - rVi)
{unless Vsi = rVi -> rRange = 0}

Note that since rRange is always nonnegative and we only care about solutions where t is nonnegative, we always want the positive square root, which results in a smaller value for t when there are two positive solutions.

Edit: My formula can be shown to be equivalent to George's by multiplying the numerator and denominator by (sqrt(rMissileSpeed^2 - rVj^2) +- rVi).

nms 1 Feb 2016:

I created a mod with versions of the Wolfen and EI500 that have 120 facings, 100 accuracy, and rapid-fire forward weapons with 0 damage to observe them on the intro screen. Then I created versions where the ship speed, ship acceleration, and shot speed are all divided by 10 (sometimes with the ship rotation rate and acceleration also reduced, sometimes not).

They are clearly adjusting their aim continuously, but all of them seem to miss by about the same distance, so my theories about not adjusting their aim or timing issues seem to be ruled out.

Maybe I'll start checking helper functions for things like unit mismatches or coordinates that aren't relative to the center of the ship...

nms 10 Mar 2017:

I'm leaning towards the conclusion that the problem here isn't actually in the aiming code, but rather in the maneuvering/behavior code. That is, most of the time ships are not even trying to turn to the proper aim angle, but towards whatever angle the maneuvering code tells them to face. And that seems to be somewhere behind the target. If I'm right, the AI needs to be changed to prioritize aiming over maneuvering in most circumstances where their target is in range.

I added this weapon to my test mod to see how the aim of a fixed weapon compares with a swivel:

	<ItemType UNID="&itSwivelComLaser;"
			name=				"communications laser"
			attributes=			"commonwealth, EI, energyWeapon, majorItem"
			  
			level=				"2"
			frequency=			"notrandom"

			value=				"550"
			mass=				"1250"
			  
			description=		""
			>

		<Image imageID="&rsItemsEI1;" imageX="96" imageY="96" imageWidth="96" imageHeight="96"/>

		<Weapon
				type=				"beam"

				minFireArc=			"350"
				maxFireArc=			"10"
				damage=				"laser:0"
				fireRate=			"2"
				lifetime=			"30"
				powerUse=			"0"
		>
			<Effect>
				<Beam
					beamType=			"laser"
					primaryColor=		"0x5f, 0xf1, 0x2a"
					secondaryColor=		"0x00, 0xff, 0x00"
				/>
			</Effect>
		</Weapon>
		
		<Events>
			<OnFireWeapon>
				(block ()
					(dbgLog (cat
						"Tick: " (unvGetTick)
						" | Ship: " gSource
						" | Facing: " (objGetProperty gSource 'rotation)
						" | Fire angle: " aFireAngle
						" | Difference: " (subtract (modulo (add 540 (subtract (objGetProperty gSource 'rotation) aFireAngle)) 360) 180)
					))
					nil
				)
			</OnFireWeapon>
		</Events>
	</ItemType>

In the screenshot above, the line of purple dots shows the facing (one tick old due to Most, but not all, instances of effects are not visible the tick they're created), the red shots are from a fixed weapon, and the green beam is the swivel secondary.

Here's some sample data. I don't see any particular pattern except that the ship doesn't lead its target enough with the fixed weapon, but aims the swivel weapon correctly. It can miss in either direction, depending on which way they're orbiting, and it doesn't tend to turn to the angle the swivel weapon fired at on the previous tick. In fact, sometimes when it happens to roughly match velocities with the target for a bit, it will continue facing in a direction where its fixed weapon shots miss without turning or thrusting. This seems to further rule out timing issues being the main problem. It's also worth noting that the weapon only has 10 degrees of swivel in either direction, but the AI seems to be able to fire it up to 16 degrees off center, unless it's already rotated, but (objGetProperty obj 'rotation) is getting data from the previous tick.

03/09/2017 23:11:48	Tick: 1255 | Ship: 342438312 | Facing: 162 | Fire angle: 166 | Difference: -4
03/09/2017 23:11:48	Tick: 1256 | Ship: 342438312 | Facing: 162 | Fire angle: 166 | Difference: -4
03/09/2017 23:11:48	Tick: 1257 | Ship: 342438312 | Facing: 162 | Fire angle: 165 | Difference: -3
03/09/2017 23:11:48	Tick: 1258 | Ship: 342438312 | Facing: 159 | Fire angle: 166 | Difference: -7
03/09/2017 23:11:48	Tick: 1259 | Ship: 342438312 | Facing: 159 | Fire angle: 165 | Difference: -6
03/09/2017 23:11:48	Tick: 1260 | Ship: 342438312 | Facing: 159 | Fire angle: 165 | Difference: -6
03/09/2017 23:11:48	Tick: 1261 | Ship: 342438312 | Facing: 156 | Fire angle: 165 | Difference: -9
03/09/2017 23:11:48	Tick: 1262 | Ship: 342438312 | Facing: 156 | Fire angle: 165 | Difference: -9
03/09/2017 23:11:48	Tick: 1263 | Ship: 342438312 | Facing: 156 | Fire angle: 165 | Difference: -9
03/09/2017 23:11:48	Tick: 1264 | Ship: 342438312 | Facing: 156 | Fire angle: 165 | Difference: -9
03/09/2017 23:11:48	Tick: 1265 | Ship: 342438312 | Facing: 153 | Fire angle: 165 | Difference: -12
03/09/2017 23:11:48	Tick: 1266 | Ship: 342438312 | Facing: 153 | Fire angle: 165 | Difference: -12
03/09/2017 23:11:48	Tick: 1267 | Ship: 342438312 | Facing: 153 | Fire angle: 165 | Difference: -12
03/09/2017 23:11:48	Tick: 1268 | Ship: 342438312 | Facing: 153 | Fire angle: 165 | Difference: -12
03/09/2017 23:11:48	Tick: 1269 | Ship: 342438312 | Facing: 153 | Fire angle: 165 | Difference: -12
03/09/2017 23:11:48	Tick: 1270 | Ship: 342438312 | Facing: 150 | Fire angle: 165 | Difference: -15
03/09/2017 23:11:48	Tick: 1271 | Ship: 342438312 | Facing: 150 | Fire angle: 165 | Difference: -15
03/09/2017 23:11:48	Tick: 1272 | Ship: 342438312 | Facing: 150 | Fire angle: 165 | Difference: -15
03/09/2017 23:11:48	Tick: 1273 | Ship: 342438312 | Facing: 150 | Fire angle: 165 | Difference: -15
03/09/2017 23:11:48	Tick: 1274 | Ship: 342438312 | Facing: 150 | Fire angle: 165 | Difference: -15
03/09/2017 23:11:49	Tick: 1313 | Ship: 342438312 | Facing: 150 | Fire angle: 165 | Difference: -15
03/09/2017 23:11:49	Tick: 1314 | Ship: 342438312 | Facing: 156 | Fire angle: 164 | Difference: -8
03/09/2017 23:11:50	Tick: 1315 | Ship: 342438312 | Facing: 165 | Fire angle: 163 | Difference: 2
03/09/2017 23:11:50	Tick: 1316 | Ship: 342438312 | Facing: 171 | Fire angle: 162 | Difference: 9
03/09/2017 23:11:50	Tick: 1317 | Ship: 342438312 | Facing: 174 | Fire angle: 161 | Difference: 13
03/09/2017 23:11:50	Tick: 1320 | Ship: 342438312 | Facing: 174 | Fire angle: 158 | Difference: 16
03/09/2017 23:11:51	Tick: 1347 | Ship: 342438312 | Facing: 141 | Fire angle: 125 | Difference: 16
03/09/2017 23:11:51	Tick: 1348 | Ship: 342438312 | Facing: 135 | Fire angle: 123 | Difference: 12
03/09/2017 23:11:51	Tick: 1349 | Ship: 342438312 | Facing: 129 | Fire angle: 120 | Difference: 9
03/09/2017 23:11:51	Tick: 1350 | Ship: 342438312 | Facing: 123 | Fire angle: 117 | Difference: 6
03/09/2017 23:11:51	Tick: 1351 | Ship: 342438312 | Facing: 123 | Fire angle: 113 | Difference: 10
03/09/2017 23:11:51	Tick: 1352 | Ship: 342438312 | Facing: 123 | Fire angle: 109 | Difference: 14
03/09/2017 23:11:51	Tick: 1372 | Ship: 342438312 | Facing: 36 | Fire angle: 26 | Difference: 10
03/09/2017 23:11:51	Tick: 1373 | Ship: 342438312 | Facing: 30 | Fire angle: 25 | Difference: 5
03/09/2017 23:11:51	Tick: 1374 | Ship: 342438312 | Facing: 27 | Fire angle: 23 | Difference: 4
03/09/2017 23:11:51	Tick: 1375 | Ship: 342438312 | Facing: 24 | Fire angle: 22 | Difference: 2
assumedpseudonym 10 Mar 2017:

 …the AI seems to be able to fire it up to 16 degrees off center…

 I’ve noticed similar results on ships with swivel on secondary weapons. If you have a secondary on the port side with angles from 0-180 and another secondary on the starboard side with angles from 180-0, the weapons can be fired with enough overlap to hit targets directly ahead of or directly behind the ship. Overswivel like this and your own observations sounds like a separate bug from improper target leading, and probably worthy of its own ticket.

nms 15 Apr 2018:

I finally made some significant progress on this:
https://github.com/kronosaur/Mammoth/pull/51

The first problem was that each ship randomly picked a range at which it would start aiming instead of maneuvering somewhere from 1/4 to 1/11 of the theoretical weapon range. I changed this to 1/1.04 to 1/1.11, which may be a bit too long, since the AI usually won't fire or can't get a fire solution near the theoretical max range. But the other changes I made make this less important.

Next, I found that the AI would stop adjusting their aim once they got close to aiming correctly (generally 5-10 degrees, depending on the weapon, I think). So giving them more facings actually made them more consistent about missing. I changed them to keep trying to face the correct angle.

Also, the AI would never thrust while aiming. I made them thrust if either they're farther than 1/3 of their best weapon's range and facing within 30 degrees of the aim angle or if they're within that range, the target's relative speed is greater than 1/3 of their max speed, and they're facing within 30 degrees of the target's relative velocity. This is an aggressive approach that prioritizes being able to hit the target over dodging, since the AI doesn't really know how to dodge and the increased accuracy means that keeping moving isn't enough to dodge by itself. Ideally, they should probably take a more cautious approach when attacking a slower and more powerful target by deliberately creating some transverse speed so they have to thrust toward the target to orbit it, but that would be a bit more complicated.

I also increased the maximum time the AI would use to determine their range from 50 to 100 ticks. While deliberate, this was what limited their range with slow, long ranged fixed weapons like howitzers, which they should now use at up to 100 light seconds.

There are still some cases where their behavior needs work:

- When attacking a capital ship, gunships tend to rapidly switch between trying to close to a point very close to the target and spiraling away because they're too close to the target.

- If a ship's main gun is destroyed, and enemies are nearby, it may drift because it doesn't know what direction to face.

- Often, ships try to retreat when they have no chance of getting away. And the spiral path doesn't change direction fast enough to dodge shots except at very long range. They need to consider what ships are attacking them, rather than just what they're attacking when deciding whether to run, and use a pattern with more zig-zagging if they are faster.

- Also, I'd like to improve the AI's ability to fire on secondary targets when out of range of the primary.

nms 11 Sep 2018:

I missed that there was a factor of 4 in the squared aim range, so my previous change made them think they had nearly twice as much range as they actually did. I'm attempting to add the fix to PR 63, but can't currently connect to github. This will make them think they have slightly less range than they do, as I attempted to do last time.

Edit: Actually, I'm not sure m_rPrimaryAimRange2 is even necessary. We already calculate the intercept time for the best primary weapon as long as there is one (regardless of whether we're nominally in range or not), so we could return whether there's an intercept solution through the series of calls and use that to determine if we're in aim range.

CAIBehaviorCtx::ImplementAttackTarget
CAIBehaviorCtx::ImplementFireWeaponOnTarget
CShip::IsWeaponAligned
CWeaponClass::IsWeaponAligned
CalcInterceptTime