太阳系模拟天体不运动问题排查(附love.update代码)
太阳系模拟程序天体无法运动问题排查
正在开发太阳系模拟程序,所有初始值设置完成且绘图显示正常,但天体完全无法运动,问题集中在love.update(dt)函数中。
错误的love.update(dt)代码
function love.update(dt) local G = 6.67430e-11 -- gravitational constant -- Force Between all the bodies for i, body in ipairs(bodies) do local fx = 0 local fy = 0 for j, body in ipairs(bodies) do if i ~= j then // skip calculation for the same body local dx = body.px - body.x local dy = body.py - body.y local dist = math.sqrt(dx^2 + dy^2) local f = G * body.mass * body.mass / dist^2 theta = math.atan2(dy, dx) fx = math.cos(theta) * f fy = math.sin(theta) * f return fx, fy end end body.ax = fx / body.mass body.ay = fy / body.mass --Update of Velocity and Position for _, body in ipairs(bodies) do body.x = body.x + body.vx * dt + 0.5 * body.ax * dt^2 body.y = body.y + body.vy * dt + 0.5 * body.ay * dt^2 body.vx = body.vx + 0.5 * (body.ax + body.a0x) * dt body.vy = body.vy + 0.5 * (body.ay + body.a0y) * dt body.a0x = body.ax body.a0y = body.ay end end
天体初始值示例
bodies[1] = {name = "sun", mass = 1.9891e30, radius = 696340000, x = width/2, y = height/2, vx = 0, vy = 0 , a0x= 0, a0y= 0, ax= 0, ay= 0, px = 0, py = 0} bodies[2]= {name = "mercury", mass = 0.3302e24, radius = 2440000, x = width/2 + 0.38*100, y = height/2, vx = 0, vy = 48.92 * 1000, a0x= 0, a0y= 0, ax= 0, ay= 0, px = 46.0e9, py = 0}
尝试过的dx写法
local dx = body.px - body.xlocal dx = body2.px - body1.pxlocal dx = body.px - body.px
核心问题分析
- 循环变量命名冲突:内外层循环都使用
body作为变量名,内层循环会覆盖外层的天体引用,无法正确获取两个不同天体的数据。 - 语法错误:Lua单行注释是
--,代码中使用的//会直接导致函数执行失败,这是天体不动的直接原因之一。 - 错误的return语句:内层循环中直接
return fx, fy会导致函数提前终止,根本无法完成所有天体间的引力计算。 - 引力计算逻辑错误:
- 引力公式应为
G * mass_i * mass_j / dist²,代码中错误使用了同一个天体的质量平方。 - dx/dy的计算逻辑错误,应该是两个天体真实位置的差值(
body_j.px - body_i.px),而非同一个天体真实位置与屏幕位置的差。
- 引力公式应为
- 位置更新逻辑嵌套错误:位置更新的循环被放在外层天体循环内部,导致每个天体计算完加速度后,所有天体都被重复更新多次,逻辑混乱。
- 真实位置未同步:仅更新了屏幕显示的
x/y,未同步真实位置px/py,导致后续引力计算始终使用初始位置。
修复后的代码
function love.update(dt) local G = 6.67430e-11 -- gravitational constant -- 第一步:计算所有天体的加速度 for i, body_i in ipairs(bodies) do local fx = 0 local fy = 0 for j, body_j in ipairs(bodies) do if i ~= j then local dx = body_j.px - body_i.px local dy = body_j.py - body_i.py local dist_sq = dx^2 + dy^2 local dist = math.sqrt(dist_sq) -- 避免除以0(天体碰撞时的保护) if dist < 1e9 then dist = 1e9 end -- 计算引力大小 local f = G * body_i.mass * body_j.mass / dist_sq -- 计算引力分量(用dx/dist、dy/dist代替三角函数,更高效) fx = fx + (dx / dist) * f fy = fy + (dy / dist) * f end end -- 更新当前天体的加速度 body_i.ax = fx / body_i.mass body_i.ay = fy / body_i.mass end -- 第二步:更新所有天体的速度和位置 for _, body in ipairs(bodies) do -- 同步更新屏幕位置和真实位置 body.x = body.x + body.vx * dt + 0.5 * body.ax * dt^2 body.y = body.y + body.vy * dt + 0.5 * body.ay * dt^2 body.px = body.px + body.vx * dt + 0.5 * body.ax * dt^2 body.py = body.py + body.vy * dt + 0.5 * body.ay * dt^2 -- 更新速度(使用平均加速度) body.vx = body.vx + 0.5 * (body.ax + body.a0x) * dt body.vy = body.vy + 0.5 * (body.ay + body.a0y) * dt -- 保存当前加速度作为下一次的初始加速度 body.a0x = body.ax body.a0y = body.ay end end
额外说明
- 用
dx/dist * f和dy/dist * f替代三角函数计算引力分量,减少性能开销且结果一致。 - 添加距离最小值保护,防止天体距离过近导致数值溢出。
- 分离加速度计算与位置更新步骤,逻辑更清晰,避免重复更新。
- 同步屏幕位置与真实位置,确保引力计算使用最新数据。
内容的提问来源于stack exchange,提问作者SomthingHasToWork
相关产品推荐
相关产品推荐

