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

p5.js随机生成圆形偶尔超出画布问题求助

p5.js 目标圆形偶尔超出画布边界的问题解决

问题描述

开发时设置随机圆形的x坐标范围为0 + circle radius至width - circle radius,y坐标范围为0 + circle radius至height - circle radius,但偶尔目标圆形(circles[0])会超出画布边界。添加判断语句尝试重选合规坐标后,问题仍未解决。

相关代码

const circlesToMake = 50;
let circleLayer, circles, r, g, b;
let lives = 3;
let round = -1;
let dead = false;

function setup() {
  createCanvas(600, 450);
  noStroke();
  textFont('JetBrains Mono');
  setupCircles();
  fill(0);
  noLoop();
  circleLayer = createGraphics(width, height);
}

function draw() {
  image(circleLayer, 0, 0);
  fill(0);
  drawCircles(circleLayer);
  dispInfo();
}

function setupCircles() {
  circles = []
  r = random(20, 235)
  g = random(20, 235)
  b = random(20, 235)
  round++;

  while (circles.length < circlesToMake) {
    const circle = {
      x: random(width),
      y: random(height),
      r: 20,
    };

    let overlapping = false;
    for (let j = 0; j < circles.length; j++) {
      const other = circles[j];
      const d = dist(circle.x, circle.y, other.x, other.y);

      if (d < circle.r + other.r) {
        overlapping = true;
      } if (circle.x < 0 + circle.r || circle.x > width - circle.r) {
        overlapping = true;
      } if (circle.y < 0 + circle.r || circle.y > height - circle.r) {
        overlapping = true;
      }
    }

    if (!overlapping) {
      circles.push(circle);
    }
  }
}

function drawCircles(circleLayer) {
  clear();
  stroke(0);
  fill(255);
  rect(0, 0, width-1, height-1);
  noStroke();
  circles.forEach(c => {
    fill(r, g, b, 200); ellipse(c.x, c.y, c.r * 2, c.r * 2);
  });
  
  // fill(r, g, b, 220); // normal
  fill(32, 32, 32, 255); // dev mode
  
  const target = circles[0];
  ellipse(target.x, target.y, target.r * 2, target.r * 2);
}

function dispInfo() {
  fill(0, 0, 0, 100);
  textAlign(LEFT, TOP);
  text(`Lives: ${lives}`, 10, 10);
  text(`Round: ${round + 1}`, 10, 25);
}

function youDied() {
  clear();
  dead = true;
  textAlign(CENTER, CENTER);
  textSize(20);
  fill (0, 0, 0);
  text(`You survived ${round} rounds.`, width/2, height/2);
  fill(r, g, b, 255);
  ellipse(circles[0].x, circles[0].y, circles[0].r * 2, circles[0].r * 2);
}

function mousePressed() {
  if (!dead) {
    if (dist(mouseX, mouseY, circles[0].x, circles[0].y) < circles[0].r) {
      setupCircles();
      drawCircles();
      redraw();
    } else {
      const wrongClick = circles.some(c => dist(mouseX, mouseY, c.x, c.y) < c.r);
      if (wrongClick) {
        lives--;
        redraw();
      } if (lives <= 0) {
        youDied();
      }
    }
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.0/p5.js"></script>

问题原因

setupCircles函数中,边界检查逻辑被嵌套在遍历已有圆形的for循环内部。当生成第一个圆形(即目标圆circles[0])时,circles.length为0,循环不会执行,导致第一个圆形直接跳过边界判断。如果此时随机生成的坐标刚好超出画布范围,就会出现目标圆出界的问题。

修复方案

方案1:抽离边界检查逻辑

将边界判断从循环中移出,确保每个生成的圆形(包括第一个)都先经过边界检查,再判断是否与已有圆形重叠:

function setupCircles() {
  circles = []
  r = random(20, 235)
  g = random(20, 235)
  b = random(20, 235)
  round++;

  while (circles.length < circlesToMake) {
    const circle = {
      x: random(width),
      y: random(height),
      r: 20,
    };

    let overlapping = false;
    // 先检查边界是否合规
    if (circle.x < circle.r || circle.x > width - circle.r) {
      overlapping = true;
    }
    if (circle.y < circle.r || circle.y > height - circle.r) {
      overlapping = true;
    }
    // 再检查与已有圆形是否重叠
    for (let j = 0; j < circles.length; j++) {
      const other = circles[j];
      const d = dist(circle.x, circle.y, other.x, other.y);

      if (d < circle.r + other.r) {
        overlapping = true;
      }
    }

    if (!overlapping) {
      circles.push(circle);
    }
  }
}

方案2:直接限定随机坐标范围(更高效)

在生成随机坐标时,直接将范围限定在合法区间内,从源头避免出界,再配合边界检查双重保险:

function setupCircles() {
  circles = []
  r = random(20, 235)
  g = random(20, 235)
  b = random(20, 235)
  round++;

  while (circles.length < circlesToMake) {
    const circle = {
      // 直接生成合法范围内的坐标
      x: random(circle.r, width - circle.r),
      y: random(circle.r, height - circle.r),
      r: 20,
    };

    let overlapping = false;
    // 可选:保留边界检查作为双重保险
    if (circle.x < circle.r || circle.x > width - circle.r) {
      overlapping = true;
    }
    if (circle.y < circle.r || circle.y > height - circle.r) {
      overlapping = true;
    }
    // 检查与已有圆形是否重叠
    for (let j = 0; j < circles.length; j++) {
      const other = circles[j];
      const d = dist(circle.x, circle.y, other.x, other.y);

      if (d < circle.r + other.r) {
        overlapping = true;
      }
    }

    if (!overlapping) {
      circles.push(circle);
    }
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 03:44:58