使用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

