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);
}