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

使用j索引Planets数组时X/Y值变为NaN/Infinity,i索引正常

问题分析与解决方案

我调用名为Planets的行星数组时,用j索引(如Planets[j])访问数组对象时,X和Y元素会随机变为NaN或Infinity;用i索引(如Planets[i])时完全正常。尝试过将嵌套for循环改为while循环、将var变量改为let、修改冲突变量名等操作,问题仍未解决。

核心错误点

1. 未声明的全局变量引发冲突与计算异常

代码中多处使用未加let/const声明的变量(如i、mass、x、y、bigX等),这些变量会成为全局变量,在不同函数的循环中互相干扰,导致索引混乱或计算值异常。比如generatePlanets函数中的全局i会和iterate函数的局部i隐式冲突,破坏循环逻辑。

2. 幂运算符号使用错误

calculateDistance函数中用^进行平方运算,但JavaScript里^是按位异或运算符,不是幂运算。这会导致距离计算结果完全错误,甚至出现NaN。正确的平方运算应使用**2或Math.pow()。

3. 除以零导致Infinity

iterate函数中addedDist = distX + distY,当两个行星的坐标差互为相反数(如坐标完全相同)时,addedDist会等于0,此时distX / addedDist会得到Infinity,最终导致行星的X/Y变为Infinity。

4. 行星位置更新时机错误

每次j循环一次就更新Planets[i].X/Y,而非等j遍历完所有行星、计算完总引力影响后再更新。这会导致后续j循环使用已修改的位置,计算结果偏差甚至异常。

5. 属性名大小写不匹配

drawPlanets函数中用planet.x和planet.y访问坐标,但Planet类的属性是大写的X和Y,会得到undefined,导致绘制失败。

修正后的代码

JavaScript 部分

let cv = document.getElementById("canvas");
let context = cv.getContext("2d");

let wh = window.innerHeight;
let ww = window.innerWidth;

cv.width = ww;
cv.height = wh;
cv.style.background = "#ffff12";

const numPlanets = 10;
const gridSize = 500;
const maxMass = 50;
const gFactor = 0.667;
const timeScale = 1;

let Planets = [];

class Planet {
  Mass = 0;
  X = 0;
  Y = 0;
  VelX = 0;
  VelY = 0;

  constructor(X, Y, Mass, VelX = 0, VelY = 0) {
    this.Mass = Mass;
    this.X = X;
    this.Y = Y;
    this.VelX = VelX;
    this.VelY = VelY;
  }

  getMass() { return this.Mass; }
  getX() { return this.X; }
  getY() { return this.Y; }
  getVelX() { return this.VelX; }
  getVelY() { return this.VelY; }
}

function generateMass(maxMass) {
  const mass = Math.round((Math.random() * (maxMass - 1)) + 1);
  return mass;
}

function generateCoordinate(gridSize) {
  const coordinate = Math.round(Math.random() * gridSize);
  return coordinate;
}

function generatePlanets(numPlanets, maxMass, gridSize) {
  for (let i = 0; i < numPlanets; i++) {
    const x = generateCoordinate(gridSize);
    const y = generateCoordinate(gridSize);
    const mass = generateMass(maxMass);
    const hempPlanet = new Planet(x, y, mass);
    Planets[i] = hempPlanet;
  }
}

function calculateDistance(x1, x2, y1, y2) {
  const dx = x1 - x2;
  const dy = y1 - y2;
  return Math.sqrt(dx ** 2 + dy ** 2);
}

function iterate(Planets) {
  let i = 0;
  while (i < numPlanets) {
    const planet = Planets[i];
    const planetMass = planet.getMass();
    let planetX = planet.getX();
    let planetY = planet.getY();

    let addedPosX = 0;
    let addedPosY = 0;

    for (let j = 0; j < numPlanets; j++) {
      const tempPlanet = Planets[j];
      if (tempPlanet !== planet) {
        const tempPlanetMass = tempPlanet.getMass();
        const tempPlanetX = tempPlanet.getX();
        const tempPlanetY = tempPlanet.getY();

        const distance = calculateDistance(tempPlanetX, planetX, tempPlanetY, planetY);
        const dx = planetX - tempPlanetX;
        const dy = planetY - tempPlanetY;
        
        const distSquared = distance ** 2 + 5;
        const gravity = (planetMass * tempPlanetMass * gFactor) / distSquared;
        const dirX = dx / distance;
        const dirY = dy / distance;

        addedPosX += gravity * dirX * timeScale;
        addedPosY += gravity * dirY * timeScale;
      }
    }

    Planets[i].X = planetX + addedPosX;
    Planets[i].Y = planetY + addedPosY;
    i++;
  }
}

function drawPlanets(Planets) {
  context.clearRect(0, 0, ww, wh);
  for (let i = 0; i < numPlanets; i++) {
    const planet = Planets[i];
    const planetX = planet.getX();
    const planetY = planet.getY();
    context.beginPath();
    context.arc(planetX, planetY, Math.min(planet.getMass() / 2, 15), 0, Math.PI * 2);
    context.fillStyle = "#2c3e50";
    context.fill();
  }
}

function main() {
  generatePlanets(numPlanets, maxMass, gridSize);
  function animate() {
    iterate(Planets);
    drawPlanets(Planets);
    requestAnimationFrame(animate);
  }
  animate();
}

main();

HTML 部分

<canvas id="canvas"></canvas>

额外优化说明

  • 修正Planet构造函数,正确初始化实例属性。
  • 简化calculateDistance函数,直接计算坐标差的平方和。
  • 改用单位向量计算引力方向,避免除以零的问题,更符合物理逻辑。
  • 调整iterate函数的循环结构,确保遍历完所有行星后再更新位置。
  • 优化drawPlanets函数,用arc绘制圆形行星,并添加动画循环。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 04:07:15