Skip to content

Commit

Permalink
Missile interception prediction
Browse files Browse the repository at this point in the history
Turns out this doesn't need calculus; just painfully solving a quartic.
  • Loading branch information
MEEPofFaith committed Oct 25, 2023
1 parent a83c652 commit 6d466fe
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 19 deletions.
65 changes: 56 additions & 9 deletions src/progressed/util/Math3D.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,11 @@ public static float[] diskVertices(float x, float y, float z, float rotation, fl
public static float[] castVertices(float x, float y, float rotation, float startAngle, float tilt, float rad, int verts){
float[] castVerts = new float[verts * 2];
float space = 360f / (verts - 1f);
float scl = 1f + Mathf.sinDeg(tilt);
float scl = 1f + sinDeg(tilt);

for(int i = 0; i < verts; i++){
float angle = startAngle + space * i - rotation;
vec.trns(rotation, Mathf.cosDeg(angle) * rad * scl, Mathf.sinDeg(angle) * rad);
vec.trns(rotation, cosDeg(angle) * rad * scl, sinDeg(angle) * rad);
castVerts[i * 2] = x + vec.x;
castVerts[i * 2 + 1] = y + vec.y;
}
Expand All @@ -83,15 +83,40 @@ public static float[] castVertices(float x, float y, float rotation, float start
* @param dsty Y of target
* @param dstvx X velocity of target (subtract shooter X velocity if needed)
* @param dstvy Y velocity of target (subtract shooter Y velocity if needed)
* @param accel constant acceleration of bullet
* @param ba constant acceleration of the bullet
* @param bv initial velocity of the bullet
* @return the intercept location
*/
public static Vec2 intercept(float srcx, float srcy, float dstx, float dsty, float dstvx, float dstvy, float accel){
//TODO come back once I learn parametrics
return vresult.set(dstx, dsty);
public static Vec2 intercept(float srcx, float srcy, float dstx, float dsty, float dstvx, float dstvy, float ba, float bv){
dstvx /= Time.delta;
dstvy /= Time.delta;
float dx = dstx - srcx,
dy = dsty - srcy;
float uv = dstvx * dstvx + dstvy * dstvy,
ud = dx * dstvx + dy * dstvy;

// Get quartic components
float a = -(ba * ba) / 4;
float b = -ba * bv;
float c = uv - (bv * bv);
float d = 2 * ud;
float e = dx * dx + dy * dy;

// Solve
float[] ts = quartic(a, b, c, d, e);

// Find smallest positive solution
Vec2 sol = vresult.set(dstx, dsty);
float min = Float.MAX_VALUE;
for(float t : ts){
if(t >= 0 && t < min) min = t;
}
if(min < Float.MAX_VALUE) sol.set(dstx + dstvx * min, dsty + dstvy * min);

return sol;
}

public static Vec2 intercept(Position src, Position dst, float accel){
public static Vec2 intercept(Position src, Position dst, float ba, float bv){
float ddx = 0, ddy = 0;
if(dst instanceof Hitboxc h){
ddx += h.deltaX();
Expand All @@ -101,7 +126,8 @@ public static Vec2 intercept(Position src, Position dst, float accel){
ddx -= h.deltaX();
ddy -= h.deltaY();
}
return intercept(src.getX(), src.getY(), dst.getX(), dst.getY(), ddx, ddy, accel);
if(ddx == 0 && ddy == 0) return vresult.set(dst); //Don't bother performing unnecessary math if no prediction is needed.
return intercept(src.getX(), src.getY(), dst.getX(), dst.getY(), ddx, ddy, ba, bv);
}

public static Vec2 inaccuracy(float inaccuracy){
Expand All @@ -113,7 +139,7 @@ public static float dst(float x1, float y1, float z1, float x2, float y2, float
float xd = x2 - x1;
float yd = y2 - y1;
float zd = z2 - z1;
return Mathf.sqrt(xd * xd + yd * yd + zd * zd);
return sqrt(xd * xd + yd * yd + zd * zd);
}

/**
Expand All @@ -131,4 +157,25 @@ public static float tubeStartAngle(float x1, float y1, float x2, float y2, float

return Angles.angle(x2, y2, Tmp.v2.x, Tmp.v2.y);
}

// https://math.stackexchange.com/questions/785/is-there-a-general-formula-for-solving-quartic-degree-4-equations
private static float[] quartic(float a, float b, float c, float d, float e){
float p1 = 2*c*c*c - 9*b*c*d + 27*a*d*d + 27*b*b*e - 72*a*c*e;
float p2 = c*c - 3*b*d + 12*a*e;
float p3 = p1 + sqrt(-4*p2*p2*p2 + p1*p1);
float p4 = PMMathf.cbrt(p3/2);
float p5 = p2/(3*a*p4) + (p4/(3*a));

float p6 = sqrt((b*b)/(4*a*a) - (2*c)/(3*a) + p5);
float p7 = (b*b)/(2*a*a) - (4*c)/(3*a) - p5;
float p8 = (-(b*b*b)/(a*a*a) + (4*b*c)/(a*a) - (8*d)/a) / (4*p6);

float[] out = new float[4];
out[0] = -(b/(4*a)) - (p6/2) - (sqrt(p7-p8)/2);
out[1] = -(b/(4*a)) - (p6/2) + (sqrt(p7-p8)/2);
out[2] = -(b/(4*a)) - (p6/2) - (sqrt(p7+p8)/2);
out[3] = -(b/(4*a)) - (p6/2) + (sqrt(p7+p8)/2);

return out;
}
}
8 changes: 2 additions & 6 deletions src/progressed/util/PMMathf.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,8 @@ public static float cornerDst(float w, float h){
return (float)Math.sqrt(w * h * 2f);
}

public static float sqrt(float a){
return Mathf.sign(a) * Mathf.sqrt(Math.abs(a));
}

public static float log(float a, float value){
return Mathf.sign(value) * Mathf.log(a, Math.abs(value) + 1);
public static float cbrt(float x){
return (float)Math.cbrt(x);
}

/** Copied from {@link Predict#quad(float, float, float)} */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public void targetPosition(Posc pos){
ArcMissileBulletType bullet = (ArcMissileBulletType)peekAmmo();

if(predictTarget && pos instanceof Hitboxc h){
targetPos.set(Math3D.intercept(this, h, bullet.accel));
targetPos.set(Math3D.intercept(this, h, bullet.accel, bullet.speed));
}else{
targetPos.set(pos);
}
Expand Down Expand Up @@ -114,7 +114,7 @@ protected void bullet(BulletType type, float xOffset, float yOffset, float angle
if(!type.scaleLife) targetPos.sub(this).setLength(range()).add(this);
float dst = Math.max(Math.min(Mathf.dst(bulletX, bulletY, targetPos.x, targetPos.y), range()), minRange);
ArcMissileBulletType m = (ArcMissileBulletType)type;
float time = Mathf.sqrt((2 * dst) / m.accel);
float time = Mathf.sqrt((2 * dst) / m.accel); //TODO consider initial velocity
float zVel = -0.5f * -m.gravity * time;
handleBullet(m.create3DVel(this, team, bulletX, bulletY, 0f, shootAngle, zVel, m.accel * velScl, targetPos.x, targetPos.y), xOffset, yOffset, shootAngle - rotation);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public void targetPosition(Posc pos){
ArcMissileBulletType bullet = (ArcMissileBulletType)peekAmmo();

if(predictTarget && pos instanceof Hitboxc h){
targetPos.set(Math3D.intercept(this, h, bullet.accel));
targetPos.set(Math3D.intercept(this, h, bullet.accel, bullet.speed));
}else{
targetPos.set(pos);
}
Expand Down Expand Up @@ -111,7 +111,7 @@ protected void bullet(BulletType type, float xOffset, float yOffset, float angle

float dst = Math.max(Math.min(Mathf.dst(bulletX, bulletY, targetPos.x, targetPos.y), range()), minRange);
ArcMissileBulletType m = (ArcMissileBulletType)type;
float time = Mathf.sqrt((2 * dst) / m.accel);
float time = Mathf.sqrt((2 * dst) / m.accel); //TODO consider initial velocity
float zVel = -0.5f * -m.gravity * time;
handleBullet(m.create3DVel(this, team, bulletX, bulletY, 0f, shootAngle, zVel, m.accel * velScl, targetPos.x, targetPos.y), xOffset, yOffset, shootAngle - rotation);

Expand Down

0 comments on commit 6d466fe

Please sign in to comment.