Shooting projectiles with trajectory destination preview

Shooting projectiles with trajectory destination preview

How to shoot a projectile using Unity Physics and calculating the target hit point.

This example’s all about shooting a projectile using the physics system, plus using the projectile motion formula to calculate the hit point.

This is the tank structure:

  • The Tank has a Turret (rotates horizontally);

  • The Turret has a Cannon (rotates vertically);

  • The Cannon shoots the projectile from the Origin and moves the Target to the projectile's destination

The projectile is a sphere with a Rigidbody component attached - necessary to use the physics system.

To shoot the sphere, the AddForce method is used, with the corresponding direction - cannon forward vector (transform.forward) - and the power - configurable value (ex. 20).

// Cannon/Projectile.cs 
public void Shoot(Vector3 direction, float power)
{
    _rigidbody.AddForce(direction * power, ForceMode.Impulse);
}

Then, I used the projectile motion formula to calculate the hit point. I’m not going to go over the formula - it’s way above my paygrade - but if you’re interested check the previous link. All that we need to know, is that to calculate the trajectory we need the projectile velocity, angle and initial height.

// TrajectoryHelper.cs
public static TrajectoryData GetTrajectory(float velocity, float angle, float initialHeight)
{
    angle *= Mathf.Deg2Rad;

    var gravity = Physics.gravity.magnitude;
    var vxo = velocity * Mathf.Cos(angle);
    var vyo = velocity * Mathf.Sin(angle);

    var t = vyo / gravity;

    var maxHeight = initialHeight + (vyo * t) - (0.5f * gravity * (t * t));
    var tfall = Mathf.Sqrt(2 * maxHeight / gravity);
    var totalTime = t + tfall;
    var range = vxo * totalTime;

    return new TrajectoryData(range, maxHeight, totalTime);
}

This returns a TrajectoryData object. It’s a simple class that holds the calculated trajectory data:

  • Distance;

  • Maximum Height;

  • Duration.

public class TrajectoryData
{
    #region Variables

    public float Distance { get; }
    public float Height { get; }
    public float Duration { get; }

    #endregion

    public TrajectoryData(float distance, float height, float duration)
    {
        Distance = distance;
        Height = height;
        Duration = duration;
    }
}

Then, with the trajectory data, it’s a simple matter of using the calculated distance to position the Target object. The object will be in front of the Turret at the calculated distance.

// Cannon/TankCannon.cs
void Update()
{
    _trajectoryData = TrajectoryHelper.GetTrajectory(_shootPower, -transform.rotation.eulerAngles.x, _shootOrigin.transform.position.y);

    Vector3 targetPosition = _shootOrigin.transform.position + (transform.parent.forward * _trajectoryData.Distance);
    _shootTarget.transform.position = new Vector3(targetPosition.x, 0, targetPosition.z);
}

Check the full example.