动手做一个银河系游戏,从一行代码到旋转的星尘
说来好笑,我最初想做一个银河系游戏,只是因为晚饭吃撑了,窝在椅子上刷手机,看到一个天文望远镜拍下的仙女座照片,忽然就冒出个念头:我能不能自己在屏幕上造一个会转的银河系?不是那种3A大作级别的,就是一个小小的、有星团旋臂的、拿鼠标能拖来拖去的玩意儿,第二天我就打开了编辑器,然后对着空白的屏幕发了十分钟呆。
一切的起点:你不需要是天文物理学家
费曼说过一句我特别受用的话:“我不能创造的,我就无法理解。”这话反过来也成立——你想理解银河系为什么长成旋涡状,最好的办法就是自己造一个,但别被吓到,我们不是要解纳维-斯托克斯方程,也不用搞暗物质模拟,我们要做的,是用最简单的规则,让几千个点自己组织出旋臂来。
核心原理一句话就能讲清楚:恒星绕着中心转,但转的速度和它离中心的距离不是简单的线性关系。这叫做“较差自转”——内侧转得快,外侧转得慢,如果一开始这些恒星是均匀散布在一个圆盘里,转着转着,它们自然就会被拉成旋臂的形状,这不是你画出来的图案,是转出来的结构。
第一步:在屏幕上撒一把星星
打开你手头的任何能画点的工具——Python加Pygame、JavaScript加Canvas、甚至Unity都行,我选的是P5.js,因为打开浏览器就能跑,先别想旋臂,先把星星撒上去。
怎么撒?不是完全随机的。银河系的恒星分布大致是一个圆盘,中心密、边缘稀。你可以在极坐标下生成位置:半径r用指数分布或者简单的随机加上一点点向心的权重,角度θ就均匀随机,初始速度呢?大致垂直于半径方向,速度大小取决于r,这里有个经验公式,文献里常管它叫“银河旋转曲线”——但咱先简化,让角速度差不多是r的某个幂次函数就行。
我第一版代码大概长这样(伪代码的感觉):
- 创建5000个粒子,每个有x, y, vx, vy四个属性
- 初始位置在半径2到15之间随机分布,用指数衰减让外围稀疏
- 初始速度设为垂直于半径的切线方向,大小按r的-0.5次方来调
- 每帧更新位置,加上一个简单的欧拉积分
跑起来之后,屏幕上是一团乱转的点,像个搅拌机里的芝麻糊,我盯着看了两分钟,差点关了电脑去睡觉。
第二步:旋臂是“转”出来的,不是“画”出来的
关键的一步来了,我上面说的初始条件其实藏着一个问题:如果你让恒星严格按圆形轨道转,它们转一百圈还是均匀分布,不会出现旋臂,旋臂本质上是一种密度波——恒星走进去、又走出来,但波的结构留住了。
我们不用模拟完整的密度波理论,那太复杂,一个取巧的办法是:初始时给恒星的位置加一点“扰动”,让它们不是完美的圆形分布,而是略微聚拢在几个角度附近,你可以把初始角度设成几个正态分布的叠加,每个分布的中心角度相差固定值,这样一转起来,这些聚集的区域就会被较差自转“拉伸”成旋臂。
我试了三种初始角度设置,效果对比如下:
| 初始角度分布 | 200帧后形态 | 是否像旋臂 |
| 完全均匀随机 | 均匀盘面,无明显结构 | 不像 |
| 两个高斯峰,相隔180° | 两条模糊的棒状结构 | 有点像棒旋星系 |
| 四个高斯峰,相隔90° | 四条清晰旋臂,逐渐缠绕 | 很像! |
当我第一次看到屏幕上四条旋臂慢慢缠紧、像咖啡拉花一样散开的时候,后背是真的有点发麻。就几百行代码,一个缩微版的星系在你眼前活过来了。
第三步:加点颜色和交互,让它变成“游戏”
这时候它还只是个模拟器,不是游戏,游戏得能玩,我想了想,最低成本的互动方案是:让玩家控制星系中心的超大质量黑洞的引力强度,鼠标滚轮上调引力,所有恒星被拉得更紧,旋臂收缩;下调引力,恒星向外飘散,旋臂解体,你还可以加一个“吞噬”机制——某些倒霉的恒星飘太近就被吸进去,同时黑洞质量变大。
色彩上我做了个简单的映射:
- 靠近中心的恒星偏蓝白色,代表年轻炽热的O型B型星
- 旋臂中段的偏暖黄,像我们的太阳
- 外围稀疏的偏暗红,像低温的红矮星
再加一个深空背景,撒上几百颗不动的背景星点,整个氛围一下子就出来了,鼠标拖动可以旋转视角,滚轮缩放,就这样,一个能看、能转、能拖、能调引力参数的银河系小玩具就算做完了。
那些卡住我的坑,你也会遇到
不说虚的,做这个过程中我踩了好几个坑,第一是积分精度——用简单的欧拉方法,恒星跑着跑着就飞出去了,后来换成半隐式欧拉才好一些,第二是性能,粒子数超过3000的时候我的老笔记本风扇狂转,后来发现是每帧都在重新计算所有粒子的距离矩阵,改成了空间哈希网格之后帧率从12蹦到了55,第三最蠢:旋臂缠太紧之后所有恒星挤成一团,是因为我忘了给半径方向加一个微小的随机速度弥散——真实的银河系里恒星轨道不是完美的圆,它们有一定的径向运动。
这些坑一个一个填过去,填到半夜两点,咖啡都凉透了,但星系在屏幕上安静地转着,几万光年的尺度被我缩在13寸屏幕里。那种感觉很难形容,就好像你真的造了个东西,哪怕它只是一堆像素。
做完之后我给一个学天体物理的朋友看,他拖了几下鼠标说:“旋臂缠绕角度偏大了,真实的银河系没那么紧。”我笑了笑没说话,心里想的是——这是我的银河系,旋臂缠成什么样我说了算。你的那个,也可以长成你喜欢的任何样子。