Unity模拟⾏星轨道(⼀)
Unity模拟⾏星轨道
第⼀次在CSDN上写博客,有什么不⾜之处还请见谅或指出~
⼀、画出椭圆轨道
在这次模拟中,我们只考虑椭圆轨道的情形,双曲线和抛物线轨道等以后再进⾏完善。⾸先,我们需要画出⼀个椭圆轨道,这个⽤Unity 的LineRenderer功能就能实现。
private List<Vector3> points = new List<Vector3>();
public LineRenderer line;
public float w;
public float h;
public int num =100;
void AddPoints()
{
points.Clear();
for(int i =0; i <= num; i++)
{
float angle = i *2.0f* Mathf.PI / num;
float x = w * Mathf.Cos(angle);
float y = h * Mathf.Sin(angle);
Vector3 pt = new Vector3(x, y,0);
points.Add(pt);
}
line.positionCount = points.Count;
line.SetPositions(points.ToArray());
}
然后在Unity中建⼀个空物体Trajectory挂上LineRenderer,材质⾥挂上⾃⼰建的材质
接着另外建⼀个球体Planet挂上drawOval,然后LineRenderer选择Trajectory
当然把LineRenderer也挂在Planet下⾯也是可以的,但是后期要移动物体会很不⽅便,因为球体和轨迹的质⼼会合并。
现在,我们就能画出⼀个椭圆了!
⼆、由初始参数得到椭圆参数
由长短半轴画椭圆实在太low了,我们要模拟⾏星轨道,当然要仅仅依靠初始速度,位置,中⼼天体质量等参数来计算出椭圆的参数。
当然,这需要⼀点点的天⽂知识和物理学知识(⿁知道我被这⼀点点卡了多久 (╯‵□′)╯︵┻━┻)
unity 教程不过现在我可以直接给出我们所需的公式,即轨道速度公式和开普勒第⼆定律
OK,我知道你们肯定不关⼼怎么推导,那么我们直接上代码
r = ansform.position - center.position;
v = velocity.position - ansform.position;
//利⽤轨道线速度公式求出轨道半长轴
w =1/(2/ r.magnitude - Mathf.Pow(v.magnitude,2)/ GM );
//利⽤开普勒第⼆定律求出轨道半短轴
h = Vector3.Cross(v, r).magnitude * Mathf.Sqrt(w / GM);
//求出焦距
f = Mathf.Sqrt(w * w - h * h);
其中GM就是引⼒常数和中⼼天体质量的乘积。r是中⼼天体指向环绕天体的位置⽮量,v是环绕天体初始的⽅向⽮量,后⾯还有别的⽮量,千万别搞混了。
为了实现以中⼼天体为中⼼的效果我们还需要稍稍修改⼀下之前的代码,将中⼼调整到焦点上
Vector3 pt = new Vector3(x - f, y,0)+ ansform.position;
然后实时更新
void FixedUpdate()
{
Analyse();
AddPoints();
}
现在我们就能动态控制椭圆⼤⼩了!当然,在这之前Unity也要进⾏⼀些⼩设置。我们需要设置⼀个中⼼天体,挂在drawOval脚本的center 上,还需要设置⼀个Velocity物体,Planet与Velocity的连线代表初始速度。
但其实现在这个系统还⾮常菜,你们可能注意到了,Center,Planet和Velocity必须都在XoY平⾯上!⽽且r和v的⽅向也不能随意变更,接下来我们就要逐步解决这两个问题。
三、将椭圆轨道扩展到3D空间
仔细思考⼀下,我们所拥有的初始变量除了假设恒定的GM以外只有r和v。两个向量可以构造⼀个平⾯,
⽽我们所要构造的椭圆轨道其实完全处在这个平⾯之内。因此,先将三维坐标系转换成⼆维坐标系,求出椭圆轨道以后再还原到三维坐标系就可以了。当然,直接在三维空间中⽤向量和椭圆体公式莽应该也是可以的,但我不会_(:з)∠)_
构造平⾯坐标系
构造平⾯坐标系很简单,将r表⽰成(|r| , 0),将v表⽰成(|v|cosθ , |v|sinθ)即可
但其实由于在任何坐标系中r和v的夹⾓都是不变的,⽽我们只需要这个夹⾓,所以v就不⽤转化了。
rr = new Vector3(r.magnitude,0,0);
在平⾯坐标系中旋转椭圆
在上⾯其实我们已经可以⽤r和v获得⼀个正椭圆了,接下来我们要做的就是让这个正椭圆绕质⼼旋转⼀定⾓度,然后让我们的Planet刚好在这个轨道上,v也刚好相切。
利⽤我们已经知道的|r|和r和v的夹⾓,我们可以在正椭圆中确定唯⼀的r0
//利⽤准线求出x0的值
float x0 = w * w / f - w / f * r.magnitude;
//理论上r^2⼤于等于(x0-f)^2,但是由于精度问题可能略⼩于0,因此使⽤Abs函数
float y0 = Mathf.Sqrt(Mathf.Abs(r.magnitude * r.magnitude -(x0 - f)*(x0 - f)));
Vector3 r0 = new Vector3(x0 - f, y0,0);
//v0是在当前r0情况下应该具有的速度,⽤夹⾓来确定y0应该为正还是为负
Vector3 v0 = new Vector3(-y0 * w / h, x0 * h / w,0);
if(Mathf.Abs(Vector3.Angle(v, r)- Vector3.Angle(v0, r0))>0.01)
y0 =-y0;
//先求出在正坐标系中与质⼼距离为r的点的⽮量r0,然后测量需要倾斜alpha使⾏星可以位于轨道上
r0 = new Vector3(x0 - f, y0,0);
//如果r处在XOY平⾯中可以直接⽤r替换rr
alpha = Vector3.Angle(r0, rr);
if(Vector3.Cross(r0, rr).z <0) alpha =-alpha;
AddPoints()函数需要稍微修改⼀下,把所有点旋转⼀下
float angle = i *2.0f* Mathf.PI / num;
float x = w * Mathf.Cos(angle);
float y = h * Mathf.Sin(angle);
Vector3 pt = new Vector3(x - f, y,0)+ ansform.position;
Quaternion q = Quaternion.AngleAxis(alpha, new Vector3(0,0,1));
pt = q * pt;
points.Add(pt);
将Analyse末尾的rr替换成r就可以初步检验我们的成果了。然⽽如果不替换的话由于rr和r有个夹⾓,结果是不准确的。
还原坐标系
最后这⼀步其实⾮常简单:
根据r和v得到法向量,然后计算出单位向量i和j,最后将⼆维坐标转换成三维坐标就OK了。
Vector3 n = Vector3.Cross(v, r);
Yj = Vector3.Cross(r, n);
Yj = Yj / Yj.magnitude;
Xi = r / r.magnitude;
float angle = i *2.0f* Mathf.PI / num;
float x = w * Mathf.Cos(angle);
float y = h * Mathf.Sin(angle);
Vector3 pt = new Vector3(x - f, y,0);
//此时的点其实还都在平⾯坐标系上
Quaternion q = Quaternion.AngleAxis(alpha, new Vector3(0,0,1));
pt = q * pt;
//转换到三维坐标系
pt = pt.x * Xi + pt.y * Yj;
pt = pt + ansform.position;
points.Add(pt);
最后放上三维空间中的椭圆⾏星轨道
结语
第⼀次不看教程⾃⼰捣⿎,真的是累啊。不到100⾏代码⽤了三天时间才写完,中间还费了⽆数张草稿纸?
下⼀步是添加双曲线和抛物线轨道,以及让⾏星动起来。希望⾏星运动的时间函数能有⼀个⽐较好实现的解析解,不然只能靠积分解了。。。
1.
2.
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论