目标加速度和子弹减速度随角度变化的弹丸目标预测

2024-04-24

我之前问过一个关于这个话题的问题here https://gamedev.stackexchange.com/questions/200260/intercept-an-accelerating-object-with-a-constant-velocity-projectile,现在我想审查所提供的解决方案是否有效,然后根据所产生的困难和问题进行新的调查。

所以我之前的文章简要总结了一下,我试图做的是根据子弹的位置、目标位置、子弹速度、目标速度和目标加速度计算 3D 空间中子弹和移动目标的撞击点。

我缺乏估计冲击点的唯一变量是子弹到达目标所需的时间,我可以使用下面的公式来解决这个问题,这要归功于this https://gamedev.stackexchange.com/a/200266/161543 post:

现在我正在使用this https://github.com/sasamil/QuarticC++ 四次求解器,它根据上面的公式给出时间,如下所示:

    double t4calc = (TargetAcceleration.Dot(TargetAcceleration)) / 4;
    double t3calc = (TargetAceleration.Dot(TargetVelocity));
    double t2calc = TargetAcceleration.Dot(relativepos) + velocity.Dot(TargetVelocity) - bulletspeed * bulletspeed;
    double t1calc = 2.0 * destination.Dot(TargetVelocity);
    double tcalc = (relativepos.Dot(relativepos));

    std::complex<double>* solutions = solve_quartic(t3calc / t4calc, t2calc / t4calc, t1calc / t4calc, tcalc / t4calc);

using solutions->real()它返回击中目标所需的时间(以秒为单位)。

问题:

由于我假设子弹的速度是恒定的,因此不包含加速度,因此上面的函数仅包括目标的加速度,但忽略子弹的加速度。

现在我发现子弹也有加速度,并且随着时间的推移它会减慢。

不幸的是,我不能只使用像 -10M/s 这样的恒定加速度,因为子弹的速度和加速度也会根据玩家的俯仰视角而波动。

因为使用俯仰视角进行 f(x,y) 计算会很乏味,所以我决定从视角切换到目标的相对高度和距离,因为我的俯仰视角会根据目标距我的高度和距离而变化。

因此,通过收集一些数据并进行测试,以及使用曲线拟合,我能够获得以下函数,根据目标的相对高度和距离,该函数给出了子弹到达目标所需的时间(以秒为单位) :

double a0 = 0.28891;
double a1 = 0.00147988;
double a2 = 0.0000116694;

double x1 = DistanceToTarget;
double x2 = Targetheight - Playerheight; //relative height of Target

double TimeToTarget = a0 + (a1 * (x1^1.2)) + (a2 * (x2^1.75)) //Returns Time in seconds it needs to reach the Target (If Target is stationary)

好的,使用上面的函数我可以获得子弹击中目标所需的时间(如果目标没有移动并且站在某一点)。

但当然,如果目标正在移动并加速,我想要击中目标所需的时间。

所以现在我可以通过将到目标的当前距离除以从上面的函数获得的时间来计算子弹速度(以 M/s 为单位)并将其插入四次求解器函数中以获得子弹击中移动和加速目标所需的时间基于预测位置正确...

错误..这对于上面的四次求解器函数来说是不准确的,因为它将使用针对目标当前距离计算的子弹速度,而它应该使用针对目标撞击距离计算的子弹速度。我希望你能在这里关注我..如果没有请随时询问。

最后一个问题:

因此,为了解决这个问题,我想知道是否可以以某种方式将此 TimeToTarget 函数包含到前面的四次函数中。否则速度会计算错误,结果也会错误。


如果减慢你的意思只是重力而不是空气摩擦力kv^2 or kv^3那么你可以使用 2 遍方法非常简单地计算:

  1. 定义

    为了简单起见,我们假设子弹是p0,v0球是p1,v1...意味着位置、速度和全局加速度a适用于两者......所以牛顿达朗贝尔物理学规定:

    v0+=a*dt; p0+=v0*dt;
    v1+=a*dt; p1+=v1*dt;
    

    更新时间为dt ...

  2. 估计子弹速度

    简单的直接视线就足够了,所以:

    v0=normalize(p1-p0)*v;
    

    其中 v 是子弹的启动速度。

  3. 计算碰撞时间

    简单地通过解决t in:

    (p0+(v0*t)+(0.5*a*t*t)) = (p1+(v1*t)+(0.5*a*t*t));
    t = (p1-p0)/(v0-v1);
    

    因为这是每个轴的一个解决方案,请使用 3 个视图中大于零的最小结果get_solution下面例子中的函数

  4. 计算两者之间的位置差p0,p1 after t

    简单地通过:

    dp=(p1-p0)+((v1-v0)*t);
    

    并纠正初始v0估计之后t秒差将为零:

    v0+=dp/t;
    
  5. 实时计算

    再次使用:

    t = (p1-p0)/(v0-v1);
    

    或者如果您需要考虑两个对象的半径,则:

    t = ((r0+r1-p0+p1)/(v0-v1));
    

但请注意,这种方法将改变|v0|“稍微”,所以如果有问题,你应该使用球坐标来拟合 v0...

这里是一个小的 C++/VCL 示例(只是改编自我的之前的回答 https://stackoverflow.com/a/71808916/2521214例子)

//$$---- Form CPP ----
//---------------------------------------------------------------------------
#include <vcl.h>
#include "GLSL_math.h"
#pragma hdrstop
#include "win_main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TMain *Main;
//---------------------------------------------------------------------------
vec3 p0=vec3(  0,  0,  0),v0=vec3(  0,  0,0);   // bullet
vec3 p1=vec3(150,100,  0),v1=vec3(-50,-30,0);   // ball
vec3 a =vec3(0,-10,0);                          // global acceleration
//---------------------------------------------------------------------------
float t0=0.0;               // time to hit
const float r0= 2.0;        // bullet radius
const float r1=15.0;        // ball radius
float x0,y0,z0,x1,y1,z1;    // walls
//---------------------------------------------------------------------------
float get_solution(vec3 tt)
    {
    float t=0.0;
    // t = min(tt)
    if (t<tt.x) t=tt.x;
    if (t<tt.y) t=tt.y;
    if (t<tt.z) t=tt.z;
    return t;
    }
//---------------------------------------------------------------------------
void shoot()
    {
    const float v=250.0;            // bullet start speed
    int i;
    float t;
    vec3 dp;
    p0=vec3(0.5*(x0+x1),y0+r0,0);   // reset shot start position

    // solve t for colision:
    //  (p0+(v0*t)+(0.5*a*t*t)) - (p1+(v1*t)+(0.5*a*t*t)) = r0+r1;
    //  t = (r0+r1-p0+p1)/(v0-v1);

    v0=normalize(p1-p0)*v;          // v0 estimate
    t=get_solution((p1-p0)/(v0-v1));// time estimate
    dp=(p1-p0)+((v1-v0)*t);         // diference after time t
    v0+=dp/t;                       // correct v0 estimate
    t0=get_solution((r0+r1-p0+p1)/(v0-v1)); // real time to collision
    }
//---------------------------------------------------------------------------
void update(double dt)
    {
    int e=0;
    // Newton/d'Lambert simulation
    v0+=a*dt; p0+=v0*dt;
    v1+=a*dt; p1+=v1*dt;
    t0-=dt;
    // bullet timeout
    if (t0<=0.0) e=1;
    // bullet hit the target
    if (length(p1-p0)<=r1+r0) e=1;
    // bullet colision with wall
    if (p0.x<x0+r0) e=1;
    if (p0.x>x1-r0) e=1;
    if (p0.y<y0+r0) e=1;
    if (p0.y>y1-r0) e=1;
    if (p0.z<z0+r0) e=1;
    if (p0.z>z1-r0) e=1;
    // ball colision with wall
    if (p1.x<x0+r1){ p1.x=x0+r1; v1.x=-v1.x; }
    if (p1.x>x1-r1){ p1.x=x1-r1; v1.x=-v1.x; }
    if (p1.y<y0+r1){ p1.y=y0+r1; v1.y=-v1.y; }
    if (p1.y>y1-r1){ p1.y=y1-r1; v1.y=-v1.y; }
    if (p1.z<z0+r1){ p1.z=z0+r1; v1.z=-v1.z; }
    if (p1.z>z1-r1){ p1.z=z1-r1; v1.z=-v1.z; }
    // shoot again if needed
    if (e) shoot();
    }
//---------------------------------------------------------------------------
void TMain::draw()
    {
    if (!_redraw) return;
    float x,y,r;

    // clear buffer
    bmp->Canvas->Brush->Color=clBlack;
    bmp->Canvas->FillRect(TRect(0,0,xs,ys));

    // walls
    bmp->Canvas->Pen->Color=clWhite;
    bmp->Canvas->Brush->Color=clBlack;
    bmp->Canvas->Rectangle(x0,ys-y0,x1,ys-y1);

    // ball
    bmp->Canvas->Pen->Color=clAqua;
    bmp->Canvas->Brush->Color=clBlue;
    x=p1.x; y=ys-p1.y; r=r1;
    bmp->Canvas->Ellipse(x-r,y-r,x+r,y+r);
    // bullet
    x=p0.x; y=ys-p0.y; r=r0;
    bmp->Canvas->Ellipse(x-r,y-r,x+r,y+r);

    bmp->Canvas->Brush->Color=clBlack;
    bmp->Canvas->Font->Color=clYellow;
    bmp->Canvas->TextOutA(0,0,AnsiString().sprintf("time to hit: %.3fs",t0));

    // render backbuffer
    Main->Canvas->Draw(0,0,bmp);
    _redraw=false;
    }
//---------------------------------------------------------------------------
__fastcall TMain::TMain(TComponent* Owner) : TForm(Owner)
    {
    bmp=new Graphics::TBitmap;
    bmp->HandleType=bmDIB;
    bmp->PixelFormat=pf32bit;
    pyx=NULL;
    _redraw=true;
    }
//---------------------------------------------------------------------------
void __fastcall TMain::FormDestroy(TObject *Sender)
    {
    if (pyx) delete[] pyx;
    delete bmp;
    }
//---------------------------------------------------------------------------
void __fastcall TMain::FormResize(TObject *Sender)
    {
    xs=ClientWidth;  xs2=xs>>1;
    ys=ClientHeight; ys2=ys>>1;
    bmp->Width=xs;
    bmp->Height=ys;
    if (pyx) delete[] pyx;
    pyx=new int*[ys];
    for (int y=0;y<ys;y++) pyx[y]=(int*) bmp->ScanLine[y];
    _redraw=true;

    int w=32;
    // boundaries
    x0=w; x1=xs-w;
    y0=w; y1=ys-w;
    z0=-100; z1=+100;

    p1=vec3(x1-r1,y1-r1,0);
    shoot();

    update(0.0);
    }
//---------------------------------------------------------------------------
void __fastcall TMain::FormPaint(TObject *Sender)
    {
    _redraw=true;
    }
//---------------------------------------------------------------------------
void __fastcall TMain::tim_redrawTimer(TObject *Sender)
    {
    update(0.04);
    _redraw=true;
    draw();
    }
//---------------------------------------------------------------------------

并预览:

再次重要的是功能shoot()瞄准并发射子弹p0,v0所以它击中了p1,v1 after t0秒...功能update(dt)只是模拟时间并处理碰撞......

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

目标加速度和子弹减速度随角度变化的弹丸目标预测 的相关文章

随机推荐