You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Vanilla JavaScript实现多层天体轨道运动的问题排查

问题根源与解决思路

核心问题分析

  1. 轨道点重复生成与索引不匹配:每次调用setOrbit都会重新计算整个轨道的点集合,父天体位置变化后,新的点集合长度和顺序都可能改变,但orbit.current仍沿用之前的索引,直接导致位置跳变、轨道混乱。
  2. 角度计算逻辑错误:Math.atan2的参数顺序搞反了,正确用法是Math.atan2(纵坐标差, 横坐标差),你当前代码把x和y的差搞反,导致轨道点的顺时针排序完全错误。
  3. 更新顺序隐患:当前用world.objects.forEach遍历更新,虽然现在顺序是sun→earth→moon,但如果后续添加天体顺序变化,子天体可能先于父天体更新,导致用旧位置计算轨道。

最优解决方案:放弃预生成轨道点,用实时三角函数计算位置

预生成轨道点的方式在网格环境下虽直观,但处理动态父天体时会有大量索引同步问题,直接用三角函数计算当前位置更简洁准确,步骤如下:

  • 给每个天体添加orbit.angle属性,记录当前轨道角度,每次更新时递增角度
  • 每次更新时,基于父天体的实时位置,用cos和sin计算自身的x、y坐标
  • 确保更新顺序是从父到子(或按层级排序)

修改后的完整代码

//OBJECTS
let world = {
  size: 0,
  map: [],
  objects: [],
  init: function(size) {
    this.size = size;
    this.map = Array.from(Array(this.size), () => new Array(this.size));
  },
  update: function() {
    this.map = Array.from(Array(this.size), () => new Array(this.size));
    this.objects.forEach(e => {
      // 确保坐标在世界范围内(可选,防止超出网格)
      const clampedX = Math.max(0, Math.min(this.size - 1, e.x));
      const clampedY = Math.max(0, Math.min(this.size - 1, e.y));
      this.map[clampedY][clampedX] = e.logo;
    });
  }
}

//CLASSES
class CelestialObject {
  constructor(logo, x, y, orbitalparent, orbitalradius, orbitSpeed = 0.1) {
    this.logo = ` ${logo} `;
    this.x = x;
    this.y = y;
    this.orbit = {
      parent: orbitalparent,
      radius: orbitalradius,
      angle: 0, // 当前轨道角度
      speed: orbitSpeed // 角速度,控制公转快慢
    }

    world.objects.push(this);
  }

  updateOrbit() {
    if (this.orbit.parent != null) {
      // 更新轨道角度
      this.orbit.angle += this.orbit.speed;
      // 确保角度在0-2π范围内
      if (this.orbit.angle > Math.PI * 2) {
        this.orbit.angle -= Math.PI * 2;
      }
      // 基于父天体实时位置计算自身坐标
      const parentX = this.orbit.parent.x;
      const parentY = this.orbit.parent.y;
      this.x = Math.round(parentX + this.orbit.radius * Math.cos(this.orbit.angle));
      this.y = Math.round(parentY + this.orbit.radius * Math.sin(this.orbit.angle));
    }
  }
}

//FUNCTIONS
function draw() {
  let map = document.getElementById('map');

  map.innerHTML = '';
  world.update();

  for (let i = 0; i < world.size; i++) {
    for (let j = 0; j < world.size; j++) {
      if (world.map[j][i] != undefined) map.innerHTML += world.map[j][i];
      else map.innerHTML += ' . ';
    }
    map.innerHTML += '<br>';
  }
}

//RUNTIME
world.init(15);

let sun = new CelestialObject('s', 7, 7, null, null);
// 地球公转速度慢一点
let earth = new CelestialObject('e', 0, 0, sun, 4, 0.1);
// 月球公转速度快一点
let moon = new CelestialObject('m', 0, 0, earth, 2, 0.3);

setInterval(() => {
  // 按父→子顺序更新,确保父先移动,子用新位置计算
  sun.updateOrbit();
  earth.updateOrbit();
  moon.updateOrbit();
  draw();
}, 100);
body {
  background-color: black;
  color: white;
  font-family: monospace;
}
<div id="map"></div>

关键修改说明

  • 用orbit.angle和三角函数实时计算位置,彻底解决预生成轨道点的索引同步问题
  • 修正坐标计算逻辑,确保子天体始终基于父天体的最新位置公转
  • 添加orbit.speed参数,可独立控制每个天体的公转速度
  • 明确按父→子顺序更新,避免依赖objects数组的添加顺序
  • 增加坐标范围限制,防止天体超出网格边界

内容的提问来源于stack exchange,提问作者damiengurzynski

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 17:08:11