计算给定速度的轨道截距。
Calculate an intercept to an orbit for a given speed.
背景:
尝试编写一个游戏,其中 'FTL' 旅行不受重力影响并且加速度是即时的。
在给定行星的开普勒轨道和船舶当前位置及其最大 FTL 速度的情况下,我如何计算行星的位置。 (在 m/s)
我可以在给定的日期时间获得行星的位置,但我正在努力弄清楚如何计算行星的位置,以及将飞船送往何处,而不需要围绕轨道追逐行星.
我会迭代...
计算行星与飞船当前位置之间的距离
如果目标是静止的(不移动),你可以从中计算出你的船需要多长时间才能到达目标。这次我们打电话 t
.
计算 actual_time+t
中的行星位置并计算 t
这个新位置
记住最后一个 t
让我们称它为 t0
。然后以与 #1 中相同的方式计算新的 t
,但对于行星在 t
.
之后的位置
循环#2
如果 fabs(t-t0)<accuracy
.
停止
这个迭代解决方案应该更接近每次迭代的发现 t
除非你的星球移动得太快 and/or 飞船真的太远或太慢(初始 t
很重要部分甚至大于行星回归年)。在这种情况下,您通常会先跳入恒星系统,然后再跳到行星(就像原始精英中一样)。
对于模糊的行星运动(比如太小的轨道周期)你需要不同的方法,但要意识到这种情况意味着行星非常靠近恒星或者非常重的系统中心质量如黑洞......
在具有恒定 FTL 速度的代码中,它看起来像这样:
vec3 pp,ps=vec3(?,?,?); // planet and ship positions
double t,t0,time;
time=actual_time(); t=0.0;
for (int i=0;i<100;i++) // just avoiding infinite loop in case t/planet_orbit_period>=~0.5
{
t0=t;
pp = planet_position(time+t);
t=Length(pp-ps)/ship_FTL_speed;
if (fabs(t-t0)*ship_FTL_speed<=ship_safe_FTL_distance) break;
}
这应该很快收敛(比如 5-10 次迭代就足够了)现在 t
应该保持旅行所需的时间,pp
应该保持你的船应该前往的位置。但是,如果 i>=100
没有找到解决方案,那么您首先需要靠近系统,或者使用更快的 FTL 或使用不同的方法,但这不应该是恒星系统 FTL 中常见的情况,因为旅行时间应该是远小于目标轨道周期...
顺便说一句。这可能会让您感兴趣:
[Edit1] 比 FTL 翻译驱动慢
我对它进行了一些教学并稍微更改了算法。首先,它会以一定的步长(每个周期 100 个点)检查整个行星周期内的所有位置,并记住最接近前往船的时间,而不管旅行期间经过的行星周期如何。然后简单地“递归地”检查最佳位置,角度步长越来越小。此处预览结果:
并更新了源代码(完整的 VCL 应用程序代码,因此只需 use/port 您需要的,忽略其余部分)
//$$---- Form CPP ----
//---------------------------------------------------------------------------
#include <vcl.h>
#include <math.h>
#pragma hdrstop
#include "win_main.h"
#include "GLSL_math.h" // just for vec3
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TMain *Main;
//---------------------------------------------------------------------------
// constants
const double deg=M_PI/180.0;
const double t_day=60.0*60.0*24.0;
// view
double view_x0=0.0;
double view_y0=0.0;
double zoom=1.0;
// simulation
double sim_t=0.0,sim_dt=0.01*t_day;
//---------------------------------------------------------------------------
void toscr(double &x,double &y)
{
x*=zoom; x+=view_x0;
y*=zoom; y+=view_y0;
}
//---------------------------------------------------------------------------
class planet // Kepler body simplified to 2D axis aligned. For fully 3D orbit add mising orbital parameters and equations
{
public:
// input parameters
double a,b,t0,T; // major axis,minor axis, time where M=E=0.0 deg, orbital period
// computet parameters
double c1,c2,e;
void ld(double _a,double _b,double _t0,double _T)
{
// copy input orbital parameters
a=_a;
b=_b;
t0=_t0;
T=_T;
// prepare orbital constants
e=1.0-((b*b)/(a*a)); // eccentricity
if (e>=1.0) e=0; // wrong e
c1=sqrt((1.0+e)/(1.0-e)); // some helper constants computation
c2=a*(1-e*e);
//b=a*sqrt(1.0-e);
}
vec3 position(double t) // actual position relative to center mass of the system
{
int q;
vec3 p;
double E,V,r,M;
// compute mean orbital position M [rad] from time t
M=(t-t0)/T;
M-=floor(M);
M*=2.0*M_PI;
// compute real orbital position E [rad] from M
for (E=M,q=0;q<20;q++) E=M+e*sin(E);// Kepler's equation
// heliocentric ellipse
V=2.0*atan(c1*tan(E/2.0));
r=c2/(1.0+e*cos(V));
p.x=r*cos(V);
p.y=r*sin(V);
p.z=0.0;
return p;
}
void draw_orbit(TCanvas *scr)
{
int i;
double ang,x,y,r,V,E;
x=a; y=0; toscr(x,y);
for (i=2,E=0.0;i;E+=3.6*deg)
{
if (E>=2.0*M_PI) { E=0.0; i=0; }
V=2.0*atan(c1*tan(E/2.0));
r=c2/(1.0+e*cos(V));
x=r*cos(V);
y=r*sin(V);
toscr(x,y);
if (i==2){ scr->MoveTo(x,y); i=1; }
else scr->LineTo(x,y);
}
}
};
//---------------------------------------------------------------------------
class ship // Space ship with translation propulsion
{
public:
vec3 pos,dir; // position and translation direction
double spd,tim; // translation speed and time to translate or 0.0 if no translation
ship() { pos=vec3(0.0,0.0,0.0); dir=pos; spd=0.0; tim=0.0; }
void update(double dt) // simulate dt time step has passed
{
if (tim<=0.0) return;
if (dt>tim) { dt=tim; tim=0.0; }
else tim-=dt;
pos+=spd*dt*dir;
}
void intercept(planet &pl) // set course for planet pl intercept
{
if (spd<=0.0) { tim=0.0; return; }
const double d=1000000.0; // safe distance to target
/*
// [Iteration]
int i;
vec3 p;
double t0;
for (tim=0.0,i=0;i<100;i++)
{
t0=tim;
p=pl.position(sim_t+tim);
tim=length(p-pos)/spd;
if (fabs(tim-t0)*spd<=d) break;
}
dir=normalize(p-pos);
*/
// [search]
vec3 p;
int i;
double tt,t,dt,a0,a1,T;
// find orbital position with min error (coarse)
for (a1=-1.0,t=0.0,dt=0.01*pl.T;t<pl.T;t+=dt)
{
p=pl.position(sim_t+t); // try time t
tt=length(p-pos)/spd;
a0=tt-t; if (a0<0.0) continue; // ignore overshoots
a0/=pl.T; // remove full periods from the difference
a0-=floor(a0);
a0*=pl.T;
if ((a0<a1)||(a1<0.0)) { a1=a0; tim=tt; } // remember best option
}
// find orbital position with min error (fine)
for (i=0;i<3;i++) // recursive increase of accuracy
for (a1=-1.0,t=tim-dt,T=tim+dt,dt*=0.1;t<T;t+=dt)
{
p=pl.position(sim_t+t); // try time t
tt=length(p-pos)/spd;
a0=tt-t; if (a0<0.0) continue; // ignore overshoots
a0/=pl.T; // remove full periods from the difference
a0-=floor(a0);
a0*=pl.T;
if ((a0<a1)||(a1<0.0)) { a1=a0; tim=tt; } // remember best option
}
// direction
p=pl.position(sim_t+tim);
dir=normalize(p-pos);
}
};
//---------------------------------------------------------------------------
planet pl;
ship sh;
//---------------------------------------------------------------------------
void TMain::draw()
{
if (!_redraw) return;
double x,y,r=3;
vec3 p;
// clear buffer
bmp->Canvas->Brush->Color=clBlack;
bmp->Canvas->FillRect(TRect(0,0,xs,ys));
// Star
bmp->Canvas->Pen->Color=clYellow;
bmp->Canvas->Brush->Color=clYellow;
x=0; y=0; toscr(x,y);
bmp->Canvas->Ellipse(x-r,y-r,x+r,y+r);
// planet
bmp->Canvas->Pen->Color=clDkGray;
pl.draw_orbit(bmp->Canvas);
bmp->Canvas->Pen->Color=clAqua;
bmp->Canvas->Brush->Color=clAqua;
p=pl.position(sim_t);
x=p.x; y=p.y; toscr(x,y);
bmp->Canvas->Ellipse(x-r,y-r,x+r,y+r);
// ship
bmp->Canvas->Pen->Color=clRed;
bmp->Canvas->Brush->Color=clRed;
p=sh.pos;
x=p.x; y=p.y; toscr(x,y);
bmp->Canvas->Ellipse(x-r,y-r,x+r,y+r);
// render backbuffer
Main->Canvas->Draw(0,0,bmp);
_redraw=false;
}
//---------------------------------------------------------------------------
__fastcall TMain::TMain(TComponent* Owner) : TForm(Owner)
{
pl.ld(1000000000.0,350000000.0,0.0,50.0*t_day);
sh.pos=vec3(-3500000000.0,-800000000.0,0.0);
sh.spd=500.0; // [m/s]
sh.intercept(pl);
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;
view_x0=xs-(xs>>3);
view_y0=ys2;
zoom=double(xs2)/(2.5*pl.a);
// draw(); Sleep(5000);
}
//---------------------------------------------------------------------------
void __fastcall TMain::FormPaint(TObject *Sender)
{
_redraw=true;
}
//---------------------------------------------------------------------------
void __fastcall TMain::tim_redrawTimer(TObject *Sender)
{
for (int i=0;i<10;i++)
{
sh.update(sim_dt);
sim_t+=sim_dt;
if (sh.tim<=0.0) sim_dt=0.0; // stop simulation when jump done
}
if (sim_dt>0.0) _redraw=true;
draw();
}
//---------------------------------------------------------------------------
重要的东西在两个 类 planet,ship
然后 sim_t
是实际模拟时间和 sim_dt
是模拟时间步长。在这种情况下,模拟在船舶到达目的地后停止。 ship::tim
是在 ship::intercept()
中计算的剩余行驶时间以及预设速度的方向。应该在每个模拟时间步调用更新...
背景: 尝试编写一个游戏,其中 'FTL' 旅行不受重力影响并且加速度是即时的。
在给定行星的开普勒轨道和船舶当前位置及其最大 FTL 速度的情况下,我如何计算行星的位置。 (在 m/s)
我可以在给定的日期时间获得行星的位置,但我正在努力弄清楚如何计算行星的位置,以及将飞船送往何处,而不需要围绕轨道追逐行星.
我会迭代...
计算行星与飞船当前位置之间的距离
如果目标是静止的(不移动),你可以从中计算出你的船需要多长时间才能到达目标。这次我们打电话
t
.计算
actual_time+t
中的行星位置并计算t
这个新位置记住最后一个
之后的位置t
让我们称它为t0
。然后以与 #1 中相同的方式计算新的t
,但对于行星在t
.循环#2
如果
停止fabs(t-t0)<accuracy
.
这个迭代解决方案应该更接近每次迭代的发现 t
除非你的星球移动得太快 and/or 飞船真的太远或太慢(初始 t
很重要部分甚至大于行星回归年)。在这种情况下,您通常会先跳入恒星系统,然后再跳到行星(就像原始精英中一样)。
对于模糊的行星运动(比如太小的轨道周期)你需要不同的方法,但要意识到这种情况意味着行星非常靠近恒星或者非常重的系统中心质量如黑洞......
在具有恒定 FTL 速度的代码中,它看起来像这样:
vec3 pp,ps=vec3(?,?,?); // planet and ship positions
double t,t0,time;
time=actual_time(); t=0.0;
for (int i=0;i<100;i++) // just avoiding infinite loop in case t/planet_orbit_period>=~0.5
{
t0=t;
pp = planet_position(time+t);
t=Length(pp-ps)/ship_FTL_speed;
if (fabs(t-t0)*ship_FTL_speed<=ship_safe_FTL_distance) break;
}
这应该很快收敛(比如 5-10 次迭代就足够了)现在 t
应该保持旅行所需的时间,pp
应该保持你的船应该前往的位置。但是,如果 i>=100
没有找到解决方案,那么您首先需要靠近系统,或者使用更快的 FTL 或使用不同的方法,但这不应该是恒星系统 FTL 中常见的情况,因为旅行时间应该是远小于目标轨道周期...
顺便说一句。这可能会让您感兴趣:
[Edit1] 比 FTL 翻译驱动慢
我对它进行了一些教学并稍微更改了算法。首先,它会以一定的步长(每个周期 100 个点)检查整个行星周期内的所有位置,并记住最接近前往船的时间,而不管旅行期间经过的行星周期如何。然后简单地“递归地”检查最佳位置,角度步长越来越小。此处预览结果:
并更新了源代码(完整的 VCL 应用程序代码,因此只需 use/port 您需要的,忽略其余部分)
//$$---- Form CPP ----
//---------------------------------------------------------------------------
#include <vcl.h>
#include <math.h>
#pragma hdrstop
#include "win_main.h"
#include "GLSL_math.h" // just for vec3
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TMain *Main;
//---------------------------------------------------------------------------
// constants
const double deg=M_PI/180.0;
const double t_day=60.0*60.0*24.0;
// view
double view_x0=0.0;
double view_y0=0.0;
double zoom=1.0;
// simulation
double sim_t=0.0,sim_dt=0.01*t_day;
//---------------------------------------------------------------------------
void toscr(double &x,double &y)
{
x*=zoom; x+=view_x0;
y*=zoom; y+=view_y0;
}
//---------------------------------------------------------------------------
class planet // Kepler body simplified to 2D axis aligned. For fully 3D orbit add mising orbital parameters and equations
{
public:
// input parameters
double a,b,t0,T; // major axis,minor axis, time where M=E=0.0 deg, orbital period
// computet parameters
double c1,c2,e;
void ld(double _a,double _b,double _t0,double _T)
{
// copy input orbital parameters
a=_a;
b=_b;
t0=_t0;
T=_T;
// prepare orbital constants
e=1.0-((b*b)/(a*a)); // eccentricity
if (e>=1.0) e=0; // wrong e
c1=sqrt((1.0+e)/(1.0-e)); // some helper constants computation
c2=a*(1-e*e);
//b=a*sqrt(1.0-e);
}
vec3 position(double t) // actual position relative to center mass of the system
{
int q;
vec3 p;
double E,V,r,M;
// compute mean orbital position M [rad] from time t
M=(t-t0)/T;
M-=floor(M);
M*=2.0*M_PI;
// compute real orbital position E [rad] from M
for (E=M,q=0;q<20;q++) E=M+e*sin(E);// Kepler's equation
// heliocentric ellipse
V=2.0*atan(c1*tan(E/2.0));
r=c2/(1.0+e*cos(V));
p.x=r*cos(V);
p.y=r*sin(V);
p.z=0.0;
return p;
}
void draw_orbit(TCanvas *scr)
{
int i;
double ang,x,y,r,V,E;
x=a; y=0; toscr(x,y);
for (i=2,E=0.0;i;E+=3.6*deg)
{
if (E>=2.0*M_PI) { E=0.0; i=0; }
V=2.0*atan(c1*tan(E/2.0));
r=c2/(1.0+e*cos(V));
x=r*cos(V);
y=r*sin(V);
toscr(x,y);
if (i==2){ scr->MoveTo(x,y); i=1; }
else scr->LineTo(x,y);
}
}
};
//---------------------------------------------------------------------------
class ship // Space ship with translation propulsion
{
public:
vec3 pos,dir; // position and translation direction
double spd,tim; // translation speed and time to translate or 0.0 if no translation
ship() { pos=vec3(0.0,0.0,0.0); dir=pos; spd=0.0; tim=0.0; }
void update(double dt) // simulate dt time step has passed
{
if (tim<=0.0) return;
if (dt>tim) { dt=tim; tim=0.0; }
else tim-=dt;
pos+=spd*dt*dir;
}
void intercept(planet &pl) // set course for planet pl intercept
{
if (spd<=0.0) { tim=0.0; return; }
const double d=1000000.0; // safe distance to target
/*
// [Iteration]
int i;
vec3 p;
double t0;
for (tim=0.0,i=0;i<100;i++)
{
t0=tim;
p=pl.position(sim_t+tim);
tim=length(p-pos)/spd;
if (fabs(tim-t0)*spd<=d) break;
}
dir=normalize(p-pos);
*/
// [search]
vec3 p;
int i;
double tt,t,dt,a0,a1,T;
// find orbital position with min error (coarse)
for (a1=-1.0,t=0.0,dt=0.01*pl.T;t<pl.T;t+=dt)
{
p=pl.position(sim_t+t); // try time t
tt=length(p-pos)/spd;
a0=tt-t; if (a0<0.0) continue; // ignore overshoots
a0/=pl.T; // remove full periods from the difference
a0-=floor(a0);
a0*=pl.T;
if ((a0<a1)||(a1<0.0)) { a1=a0; tim=tt; } // remember best option
}
// find orbital position with min error (fine)
for (i=0;i<3;i++) // recursive increase of accuracy
for (a1=-1.0,t=tim-dt,T=tim+dt,dt*=0.1;t<T;t+=dt)
{
p=pl.position(sim_t+t); // try time t
tt=length(p-pos)/spd;
a0=tt-t; if (a0<0.0) continue; // ignore overshoots
a0/=pl.T; // remove full periods from the difference
a0-=floor(a0);
a0*=pl.T;
if ((a0<a1)||(a1<0.0)) { a1=a0; tim=tt; } // remember best option
}
// direction
p=pl.position(sim_t+tim);
dir=normalize(p-pos);
}
};
//---------------------------------------------------------------------------
planet pl;
ship sh;
//---------------------------------------------------------------------------
void TMain::draw()
{
if (!_redraw) return;
double x,y,r=3;
vec3 p;
// clear buffer
bmp->Canvas->Brush->Color=clBlack;
bmp->Canvas->FillRect(TRect(0,0,xs,ys));
// Star
bmp->Canvas->Pen->Color=clYellow;
bmp->Canvas->Brush->Color=clYellow;
x=0; y=0; toscr(x,y);
bmp->Canvas->Ellipse(x-r,y-r,x+r,y+r);
// planet
bmp->Canvas->Pen->Color=clDkGray;
pl.draw_orbit(bmp->Canvas);
bmp->Canvas->Pen->Color=clAqua;
bmp->Canvas->Brush->Color=clAqua;
p=pl.position(sim_t);
x=p.x; y=p.y; toscr(x,y);
bmp->Canvas->Ellipse(x-r,y-r,x+r,y+r);
// ship
bmp->Canvas->Pen->Color=clRed;
bmp->Canvas->Brush->Color=clRed;
p=sh.pos;
x=p.x; y=p.y; toscr(x,y);
bmp->Canvas->Ellipse(x-r,y-r,x+r,y+r);
// render backbuffer
Main->Canvas->Draw(0,0,bmp);
_redraw=false;
}
//---------------------------------------------------------------------------
__fastcall TMain::TMain(TComponent* Owner) : TForm(Owner)
{
pl.ld(1000000000.0,350000000.0,0.0,50.0*t_day);
sh.pos=vec3(-3500000000.0,-800000000.0,0.0);
sh.spd=500.0; // [m/s]
sh.intercept(pl);
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;
view_x0=xs-(xs>>3);
view_y0=ys2;
zoom=double(xs2)/(2.5*pl.a);
// draw(); Sleep(5000);
}
//---------------------------------------------------------------------------
void __fastcall TMain::FormPaint(TObject *Sender)
{
_redraw=true;
}
//---------------------------------------------------------------------------
void __fastcall TMain::tim_redrawTimer(TObject *Sender)
{
for (int i=0;i<10;i++)
{
sh.update(sim_dt);
sim_t+=sim_dt;
if (sh.tim<=0.0) sim_dt=0.0; // stop simulation when jump done
}
if (sim_dt>0.0) _redraw=true;
draw();
}
//---------------------------------------------------------------------------
重要的东西在两个 类 planet,ship
然后 sim_t
是实际模拟时间和 sim_dt
是模拟时间步长。在这种情况下,模拟在船舶到达目的地后停止。 ship::tim
是在 ship::intercept()
中计算的剩余行驶时间以及预设速度的方向。应该在每个模拟时间步调用更新...