Answers here well explained the concept, but
seems code answer was not posted yet:
// predicts collision time between two moving objects
// objects are spheres moving in 3D space with constant velocities
// returns PositiveInfinity if collision will never happen
float GetCollisionTime(WorldObject a, WorldObject b)
{
// relative velocity and position
var rel_v = a.Velocity - b.Velocity;
var rel_pos = a.Position - b.Position;
float start_dist_squared = rel_pos.LengthSquared();
double need_dist_squared = M.Pow(a.Radius + b.Radius, 2);
// skip if already collide
if (start_dist_squared <= need_dist_squared)
return 0;
float rel_dot = Vector3.Dot(rel_pos, rel_v);
float rel_v_squared = rel_v.LengthSquared();
// skip if they move away
if (rel_dot > 0 || rel_v_squared == 0)
return float.PositiveInfinity;
float min_dist_t = -rel_dot / rel_v_squared;
float min_dist_squared = (rel_pos + rel_v * min_dist_t).LengthSquared();
// skip if they never touch
if (min_dist_squared > need_dist_squared)
return float.PositiveInfinity;
double collide_dist_to_min_squared = need_dist_squared - min_dist_squared;
float collide_t_before_min = (float)M.Sqrt(collide_dist_to_min_squared / rel_v_squared);
return min_dist_t - collide_t_before_min;
}
Squared distances are used only to optimize out slow root calculations.