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

Processing(Java)游戏开发:线条绘制与点放置的高效实现问询

Processing像素级曲线绘制游戏解决方案

核心问题解答

a) 避免线条自交叉/与已有线条交叉

你之前用brightness(pixels[i])检查单个像素的方式不可靠——因为设置了strokeWeight(3),line()函数会覆盖多个像素,但你只校验了其中一个,导致交叉处漏判。

高效解决方式:

  • 用二维布尔数组记录所有已占用像素(屏幕尺寸固定,数组索引访问是O(1),比ArrayList/HashSet性能更优)。
  • 绘制曲线时,先通过算法生成当前鼠标拖动路径上的所有像素点,逐个检查这些点是否未被占用,只有全部点都可用时才绘制并标记为已占用。

b) 仅允许在最后一条线上放置点位

必须为每条线条单独维护像素集合。可以用ArrayList<ArrayList<PVector>>存储所有已完成的线条,每次新线条绘制完成后,将其像素集合添加到这个全局列表中。放置点位时,只需检查当前鼠标坐标是否存在于全局列表的最后一个元素(即最后一条线的像素集合)中即可。

高效实现方案

数据结构选择

  1. 全局占用标记:boolean[][] occupied,直接通过occupied[y][x]判断坐标(x,y)是否已被线条占用,性能最优。
  2. 线条像素存储:ArrayList<PVector>存储单条线的所有像素,ArrayList<ArrayList<PVector>>存储所有线条,便于后续点位校验和管理。

关键算法:Bresenham直线生成算法

替代Processing自带的line()函数,手动生成两点之间的所有像素点,精确控制每个像素的检查与绘制,彻底解决交叉漏洞问题。

修复后的代码示例

boolean[][] occupied;
ArrayList<PVector> currentLine;
ArrayList<ArrayList<PVector>> allLines;
boolean isDrawing = false;

void setup() {
  size(600, 400);
  background(0);
  stroke(255);
  strokeWeight(1); // 改为1,如需粗线可扩展Bresenham算法支持
  
  // 初始化数据结构
  occupied = new boolean[height][width];
  allLines = new ArrayList<>();
  currentLine = new ArrayList<>();
}

void draw() {
  // 重绘所有线条
  background(0);
  // 绘制已完成的线条
  for (ArrayList<PVector> line : allLines) {
    for (PVector p : line) {
      point(p.x, p.y);
    }
  }
  // 绘制当前正在绘制的线条
  for (PVector p : currentLine) {
    point(p.x, p.y);
  }
}

void mousePressed() {
  // 检查点击位置是否未被占用,开启新线条
  if (mouseX >= 0 && mouseX < width && mouseY >=0 && mouseY < height) {
    if (!occupied[mouseY][mouseX]) {
      isDrawing = true;
      PVector start = new PVector(mouseX, mouseY);
      currentLine.add(start);
      occupied[mouseY][mouseX] = true;
    }
  }
}

void mouseDragged() {
  if (!isDrawing) return;
  
  // 获取上一个点和当前点
  PVector lastPoint = currentLine.get(currentLine.size() - 1);
  ArrayList<PVector> pathPixels = getLinePixels(lastPoint.x, lastPoint.y, mouseX, mouseY);
  
  // 校验路径上所有像素是否未被占用
  boolean canDraw = true;
  for (PVector p : pathPixels) {
    if (p.x < 0 || p.x >= width || p.y <0 || p.y >= height || occupied[(int)p.y][(int)p.x]) {
      canDraw = false;
      break;
    }
  }
  
  // 允许绘制则添加像素并标记占用
  if (canDraw) {
    currentLine.addAll(pathPixels);
    for (PVector p : pathPixels) {
      occupied[(int)p.y][(int)p.x] = true;
    }
  }
}

void mouseReleased() {
  if (isDrawing && currentLine.size() > 1) {
    allLines.add(currentLine);
  }
  // 重置当前线条状态
  currentLine = new ArrayList<>();
  isDrawing = false;
}

// 点击放置点位(仅在最后一条线上有效)
void mouseClicked() {
  if (allLines.isEmpty()) return;
  
  ArrayList<PVector> lastLine = allLines.get(allLines.size() - 1);
  PVector clickPos = new PVector(mouseX, mouseY);
  
  // 允许1像素误差,提升操作容错性
  for (PVector p : lastLine) {
    if (dist(p.x, p.y, clickPos.x, clickPos.y) <= 1) {
      fill(255, 0, 0);
      noStroke();
      ellipse(p.x, p.y, 5, 5);
      stroke(255);
      fill(0);
      break;
    }
  }
}

// Bresenham算法生成两点之间的所有像素
ArrayList<PVector> getLinePixels(int x1, int y1, int x2, int y2) {
  ArrayList<PVector> pixels = new ArrayList<>();
  
  int dx = abs(x2 - x1);
  int dy = abs(y2 - y1);
  
  int sx = (x1 < x2) ? 1 : -1;
  int sy = (y1 < y2) ? 1 : -1;
  
  int err = dx - dy;
  
  int x = x1;
  int y = y1;
  
  while (true) {
    // 跳过起点(已添加到currentLine)
    if (!(x == x1 && y == y1)) {
      pixels.add(new PVector(x, y));
    }
    
    if (x == x2 && y == y2) break;
    
    int e2 = 2 * err;
    if (e2 > -dy) {
      err -= dy;
      x += sx;
    }
    if (e2 < dx) {
      err += dx;
      y += sy;
    }
  }
  
  return pixels;
}

代码说明

  • 用Bresenham算法精确生成鼠标拖动路径上的所有像素,确保每个像素都经过占用检查,彻底避免交叉漏洞。
  • 分currentLine(当前绘制中)和allLines(已完成线条)两个集合,清晰管理最后一条线的点位放置逻辑。
  • 点击放置点位时加入1像素误差判断,提升用户操作的容错性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 21:45:00