Posted May 27, 2021 by mattflat
I had to put some work into the code to get the units to hitting their enemys, but now it works quite fine:
The launching speed of the projectile is fixed, so the correct angles (in two directions) have to be calculated to hit the target just at the position it will be in a few seconds. Time in air in this example is nearly 5 seconds - the projectile just follows its ballistic curve, there are no adjustments made after it‘s been released. Works well as long as the target does not change its velocity vector.
The math behind it:
We have to know the position of the gun, the (launch) velocity of the projectile, the position and current speed of the target. We are looking for a vector representing the direction the projectile will be launched.
It's an iterative approach for a world without air resistance. Also, there are two solutions, one below (cannon) and one above (e.g. mortar) 45°; here, we are looking for the solution below 45°. There might be some problems if the y-axis-distance between gun and target is larger than the xz-plane-distance.
Three iterations of the whole thing, with three iterations of 1.a. each time, works just fine in my case.
My solution was implemented in Unity, but the idea behind it should work for other engines too.
Code example you can reuse for your project ("aimDir" is the aiming vector of the gun, we are looking for this):
Vector3 targetSpeed = currentTarget.GetComponent<Unit>().speed;
float angle;
float effProjectileSpeed = projectileSpeed;
for (int i = 0; i < 3; i++)
{
float calcHitDistance = Vector3.Magnitude((currentTargetPos + (targetSpeed) * (Vector3.Distance(currentTargetPos, projectileSpawn.transform.position) / effProjectileSpeed) - projectileSpawn.transform.position));
for (int j = 0; j < 3; j++)
{
calcHitDistance = Vector3.Magnitude((currentTargetPos + (targetSpeed) * (calcHitDistance / effProjectileSpeed) - projectileSpawn.transform.position));
}
aimDir = Vector3.Normalize(currentTargetPos + (targetSpeed) * (calcHitDistance / effProjectileSpeed) - projectileSpawn.transform.position + 5 * Mathf.Pow(calcHitDistance / effProjectileSpeed, 2) * Vector3.up);
angle = Vector3.Angle(Vector3.up, aimDir);
effProjectileSpeed = projectileSpeed * Mathf.Sin((Mathf.PI / 180) * angle);
}