[Solved] Predictive Aiming System

Edit: its working well now

Hi !
I have made a predictive aiming system for my game, I took an iterative approach to solve the predicted position.
it works nicely most of the time, but there seems to be a problem with calculating the offset angle of the spaceship weapon.
I’m assuming the problem is with the calculation of the projectile travel velocity and direction, but i can’t pinpoint the problem.
In the video you can see how it miss the target when the spaceship is moving fast, or orbiting a stationary object.

could any one help me out here, it seems like I’m missing something :face_with_spiral_eyes:

The way the system works:

  • calculating the predicted position of the target
  • getting the direction from weapon to prediction
  • getting the projectile travel direction, including inherited velocity from spaceship
  • getting the angles of the weapon relative to the ship
  • getting the angles of the projectile relative to the weapon
  • rotating the weapon to a new angle equals to: projectile angle + weapon angle

Yellow line - direction from weapon to prediction
Red line - projectile direction of travel
Green line - the final weapon direction

0:00 to 0:15 - working nicely in low speed when target is relatively in front and spaceship travels straight
0:15 to 0:45 - orbiting the target and missing it
0:45 to end - shooting on a moving target

Calculate Target Predicted Position

public static class PredictiveAiming
    {
        public static Vector3 CalculateTargetPredictedPosition
            (Transform target, Rigidbody targetRb, Transform shooter, Rigidbody shooterRb, float projectileStartSpeed, int iterations)
        {
            Vector3 targetVelocity = targetRb.velocity;
            Vector3 targetPosition = target.transform.position;

            Vector3 shooterPosition = shooter.transform.position;

            float projectileSpeed = GetProjectileSpeedInDirection(shooterRb, projectileStartSpeed, targetPosition, shooterPosition);

            float timeToTarget;
            Vector3 targetFuturePosition = targetPosition;

            for (int i = 0; i < iterations; i++)
            {
                timeToTarget = Vector3.Distance(targetFuturePosition, shooterPosition) / projectileSpeed;
                targetFuturePosition = (timeToTarget * targetVelocity) + targetPosition;

                projectileSpeed = GetProjectileSpeedInDirection(shooterRb, projectileStartSpeed, targetFuturePosition, shooterPosition);
            }

            return targetFuturePosition;
        }

        private static float GetProjectileSpeedInDirection(Rigidbody shooterRb, float projectileStartSpeed, Vector3 targetPosition, Vector3 shooterPosition)
        {
            float shooterSpeedInDirection = Vector3.Project(shooterRb.velocity, (targetPosition - shooterPosition).normalized).magnitude;
            float projectileSpeed = projectileStartSpeed + shooterSpeedInDirection;
            return projectileSpeed;
        }
    }

the part that sets the new weapon angle

public class Class1
{
    Vector3 futurePos = PredictiveAiming.CalculateTargetPredictedPosition
                        (AimAssistTarget.transform, AimAssistTarget.Rigidbody, weapon.transform, ShipConfig.Rigidbody, weapon.StartingSpeed, 64);

    Vector3 dirFromWeaponToPrediction = (futurePos - weapon.transform.position).normalized;

    Vector3 shipVelocity = ShipConfig.Rigidbody.velocity;
    _projectileTravelDirection = ((dirFromWeaponToPrediction* weapon.StartingSpeed) + shipVelocity).normalized;

    Vector3 shipForwardDir = weapon.transform.parent.transform.forward;
    _weaponAngleToTarget = Vector3.SignedAngle(shipForwardDir, dirFromWeaponToPrediction, weapon.transform.parent.transform.up);
    _projectileAngleToTarget = Vector3.SignedAngle(_projectileTravelDirection, dirFromWeaponToPrediction, weapon.transform.parent.transform.up);

    Vector3 newWeaponAngle = new Vector3(0f, _weaponAngleToTarget + _projectileAngleToTarget, 0f);
    weapon.transform.localRotation = Quaternion.Euler(newWeaponAngle);
}

Without looking too carefully, here are a couple ideas based on your symptoms.

  1. You may be using your ships “heading” rather than “track” when calculating your weapon to target angle. The difference becomes more important in tight loops when you still have some lateral movement, so that could explain the missing while “orbiting”

  2. You might be calculating your targets predicted position without the correct lead time. Do you calculate how long the projectile will be in flight? Do your projectiles slow while in flight? Does the movement of your gun angle change the origin for the projectile?

Thank you!

  1. in line 13 I’m using the (heading)ship’s forward direction when calculating the weapon’s angle, what do you mean by “track”? do you mean the ship actual direction of travel ie Velocity.normalized?

  2. I calculate the travel time, the projectiles are not slowing down, and the rotation of the gun does not change the origin of the projectile

I have tried this

I have tried adding the offset between the heading and the tracking to the weapon’s new angle and also tried replacing “shipForwardDir” in line 13, both of them break the aiming of the system completely:(

Still looking for a solution…

Since the missing is occurring when there is significant lateral movement involved, I thought the problem must be with the calculation of the final impact position as a function of the ship movement. But maybe, the problem is with the time…

Are you able to confirm the tracking calculation is done at the same time the projectile is fired? If the projectile spawns even one “fixed update” later, the ships movement would change the projectiles origin.

I did read through the code this time, but these kinds of algorithms are hard to trace in your head… its tough to anticipate how changes will affect things without a working test environment. Unfortunately, I didn’t see you do anything obviously wrong, but maybe someone with a sharper eye than me will try yet.

One thing I did notice is that your iterative loop is calculating based on two moving targets. I think if you changed the target velocity to be relative to the shooter and then treat the shooter as stationary, you can simplify the loop. It won’t change the aim, but would be slightly less work for the computer.

If you’re struggling with ideas to troubleshoot, you could try changing your code enough to inject the projectiles expected origin and impact points. You can use the same strategy as your weapon aim visual; have each projectile draw a green dot at it’s expected origin and a red dot at its expected collision point.

I also thought it has something to do with time, also, the further the projectile travels, the longer it will take for it to reach target, also if the projectile is travelling in a curve, the distance travelled is larger than the shortest distance between where you fired the shot from and the destination.

thus the time taken to reach the target is greater.

btw, unless this is multiplayer, why not just make the projectiles heat-seeking-missiles if you dont want them to miss that badly?

Thank you guys, I also suspect the problem got to do with time,

That might actually be the problem, my weapons are fiering with a coroutine, I will try getting them out of the coroutine to check if it works.

Mortalanimal, I think that the projectile is flying straight and the curvature is just an optical illusion :slight_smile:
and for the “heat seeking” I prefer to do this correctly, to avoid other unpredicted problems, and mainly for the learning and challenge :slight_smile:

I do like the suggestion of making the algorithm calculate as a relative velocity, perhaps later on when I will figure this out first

I changed the weapon firing to be in update same as the predictive aiming and it didn’t help :
also tried putting one in late update and left the other in update and vise versa and it didn’t help :
also tried FixedUpdate for both, same results

I did notice something, when the ship move towards the target with a side movement as well it tends to miss, and when the ship moves away from the target with a side movement it hit perfectly… :eyes: something with the velocity perhaps…

In addition, you could change your projectiles to not disappear until they reach their expected destination, then pause the game so you can see where the target is in relation

I have managed to get it to work! :smile:
while trying to do this

It came to me that I need to iterate over the projectile direction of travel, and adjust for the new\future weapon position.
sometimes it still misses the shoot, but I think that’s because of another issue with the targeting system fov cone.

https://www.youtube.com/watch?v=d-rn8KX7NNY

Thank you for the help!
here is the new modified code if anyone wants to use this (the “CalculateTargetPredictedPosition()” is in the thread opening)

public class Class1
{
    Vector3 futurePos = PredictiveAiming.CalculateTargetPredictedPosition
                        (AimAssistTarget.transform, AimAssistTarget.Rigidbody, weapon.transform, ShipConfig.Rigidbody, weapon.StartingSpeed, 64);

    Vector3 dirFromWeaponToPrediction = (futurePos - weapon.transform.position).normalized;

    Vector3 shipVelocity = ShipConfig.Rigidbody.velocity;
    Vector3 weaponParentForwardDir = weapon.transform.parent.transform.forward;
    Vector3 weaponUp = weapon.transform.parent.transform.up;

    _weaponAngleToTarget = Vector3.SignedAngle(weaponParentForwardDir, dirFromWeaponToPrediction, weaponUp);

    Vector3 newDirection = dirFromWeaponToPrediction;

    for (int i = 0; i < 64; i++)   
    {
        _projectileTravelDirection = ((newDirection* weapon.StartingSpeed) + shipVelocity).normalized;

        _projectileAngleToTarget = Vector3.SignedAngle(_projectileTravelDirection, newDirection, weaponUp);

        newDirection = AngleToDirection(_weaponAngleToTarget + _projectileAngleToTarget, false);
    }

    Vector3 newWeaponAngle = new Vector3(0f, _weaponAngleToTarget + _projectileAngleToTarget, 0f);

    weapon.transform.localRotation  = Quaternion.Euler(newWeaponAngle);
}
1 Like