- Status
- Offline
- Joined
- Mar 3, 2026
- Messages
- 754
- Reaction score
- 457
Anyone still whiffing long-range Sentinel shots because their prediction logic is cooked? Found this solver that attempts to use Newton-Raphson for ballistic flight time convergence, but as the author noted, the accuracy is still hitting a wall.
Technical Breakdown
The implementation uses a numerical approach to find the flight time t where the projectile and the target finally meet. It switches between a simple fixed-point iteration for hitscan (or zero-gravity) and a hybrid Newton-Raphson/Bisection solver for projectile arcs.
The Logic Meat:
Native Troubleshooting & Common Pitfalls
If this is missing at range, check your units. The
factor suggests a conversion between source units and meters, but if the game's internal ballistics aren't perfectly aligned with your `BallisticFlightTime` function, you'll always be behind or above the target.
Also, consider that Apex uses a discrete tick-based simulation for projectiles. A continuous math solver like Newton-Raphson will always be a slight abstraction. If your weapon uses speed scrubbing (like the Charge Rifle or Bocek), the
must match the game's decay constants exactly. Any deviation in the gravity constant or your target's velocity smoothing will ruin the result.
Anyone tested this solver against the latest client update to see if the drag coefficients changed?
Technical Breakdown
The implementation uses a numerical approach to find the flight time t where the projectile and the target finally meet. It switches between a simple fixed-point iteration for hitscan (or zero-gravity) and a hybrid Newton-Raphson/Bisection solver for projectile arcs.
The Logic Meat:
- Target Prediction: Linear velocity extrapolation.
- Newton-Raphson: 6-iteration budget to solve for the root of the flight time equation.
- Numerical Derivative: Uses an epsilon of
for the derivative of the flight time function.Code:
0.01f - Bisection Fallback: If NR fails to converge within the budget or the derivative collapses, it falls back to a 12-iteration bisection search over a 5-second window.
- Drag Handling: Attempts to factor in speed decay via
Code:
EffectiveSpeed(b, tGuess)
Code:
Vector3 Aim::DoPrediction(const Cache::AimTarget& target, Vector3 aimPosition, Vector3 localPosition) {
Ballistics b = ResolveBallistics();
if (!b.ok) return aimPosition;
const Vector3& vel = target.velocity;
auto evalFlightTime = [&](float tGuess) -> float {
const Vector3 pred = aimPosition + vel * tGuess;
const Vector3 d = pred - localPosition;
const float h = sqrtf(d.x * d.x + d.y * d.y);
const float v = d.z;
const float es = EffectiveSpeed(b, tGuess);
if (es < 1.f) return -1.f;
return BallisticFlightTime(h, v, es, b.gravity);
};
const float dist0 = localPosition.ApexDistance(aimPosition) / 0.01905f;
float t = (b.speed > 1.f) ? (dist0 / b.speed) : 0.f;
if (b.gravity <= 0.f) {
for (int i = 0; i < 5; ++i) {
const Vector3 pred = aimPosition + vel * t;
const float distance = localPosition.ApexDistance(pred) / 0.01905f;
const float es = EffectiveSpeed(b, t);
if (!(es > 1.f) || t > 5.f) return aimPosition;
const float tNew = distance / es;
if (fabsf(tNew - t) < 0.001f) { t = tNew; break; }
t = tNew;
}
} else {
bool converged = false;
for (int i = 0; i < 6; ++i) {
const float ft = evalFlightTime(t);
if (ft < 0.f) break;
const float residual = ft - t;
if (fabsf(residual) < 0.005f) { t = ft; converged = true; break; }
constexpr float eps = 0.01f;
const float ft2 = evalFlightTime(t + eps);
if (ft2 < 0.f) { t = ft; converged = true; break; }
const float deriv = ((ft2 - (t + eps)) - residual) / eps;
if (fabsf(deriv) < 1e-6f) { t = ft; converged = true; break; }
t -= residual / deriv;
if (t < 0.f) t = 0.01f;
if (t > 5.f) t = 5.f;
}
if (!converged) {
float lo = 0.f, hi = 5.f;
for (int i = 0; i < 12; ++i) {
const float mid = (lo + hi) * 0.5f;
const float ft = evalFlightTime(mid);
if (ft < 0.f) { hi = mid; continue; }
if (ft < mid) hi = mid;
else lo = mid;
}
t = (lo + hi) * 0.5f;
}
if (!std::isfinite(t) || t > 5.f) return aimPosition;
}
Vector3 predicted = aimPosition + vel * t;
predicted.z += 0.5f * b.gravity * t * t;
if (!std::isfinite(predicted.x) || !std::isfinite(predicted.y) || !std::isfinite(predicted.z))
return aimPosition;
return predicted;
}
Native Troubleshooting & Common Pitfalls
If this is missing at range, check your units. The
Code:
0.01905f
Also, consider that Apex uses a discrete tick-based simulation for projectiles. A continuous math solver like Newton-Raphson will always be a slight abstraction. If your weapon uses speed scrubbing (like the Charge Rifle or Bocek), the
Code:
EffectiveSpeed
Anyone tested this solver against the latest client update to see if the drag coefficients changed?