处理从屏幕上拾取椭圆

Processing Picking up ellipse from screen

我有一个创建球(椭圆)数组列表的项目。当我按下鼠标(左键)并按住它时,一个椭圆跟随我的鼠标。当我松开时,椭圆会出现在我鼠标所在的屏幕上。

我希望能够右键单击并按住一个椭圆(任何随机椭圆)并让它像以前一样再次跟随我的鼠标。如果我再次松开鼠标按钮,它应该放回我鼠标当前所在的屏幕上。

我很难理解如何找到屏幕上已经存在的椭圆的 x y 位置并将椭圆从列表中删除并让它再次跟随我的鼠标。

任何建议让我知道- 这是我的主要 class

ArrayList<Ball> ballList = new ArrayList<Ball>();boolean touching;
void setup() {
  size(600, 600);

}

void draw() {  
  background(150);

  // if the mouse button is held down, set the ball's coordinates to the mouse coordinates
  if (ballList.size() > 0 && mousePressed && mouseButton==LEFT) {
    ballList.get(ballList.size() - 1).xPos = mouseX;  // 'ballList.get(ballList.size() - 1)' is the java way to get the last item added to an arrayList
    ballList.get(ballList.size() - 1).yPos = mouseY;
  }
 for (Ball b : ballList) {
    b.drawBall();
 }

}

// this method will trigger once every time the user press a mouse button
void mousePressed() {
  if (mouseButton==LEFT) {
    ballList.add(new Ball(mouseX, mouseY));
  }
}

这是我的球class

class Ball {
  float xPos, yPos; 


  Ball(float xPos, float yPos) {
    this.xPos= xPos;
    this. yPos= yPos;
  }
  void drawBall() {
    ellipse(xPos, yPos, 50, 50);
    println("X" + xPos + " Y:"+ yPos);
  }

 
  void moveBall(){
    
    
  }
}

您可以通过检查小球位置和鼠标位置之间的距离(dist())来检查小球是否在光标下:

if(dist(ball.x, ball.y, mouseX, mouseY) < ball.radius){
  println("mouse over ball");
}

目前您正在硬编码球直径 (50),但您可以轻松添加 radius 属性:

class Ball {
  
  float xPos, yPos; 
  float diameter = 50;
  float radius = diameter * 0.5;

  Ball(float xPos, float yPos) {
    this.xPos= xPos;
    this. yPos= yPos;
  }
  void drawBall() {
    ellipse(xPos, yPos, diameter, diameter);
  }

}

条件可以包含在一个 for 循环中以对每个球进行相同的检查并进入一个函数,该函数 return 是第一个匹配条件的球或 null(如果有光标下没有球):

Ball getBall(float x, float y){
  // for each ball
  for(Ball ball : ballList){
    // check if the x,y coordinates are inside any of the existing balls
    if(dist(ball.xPos, ball.yPos, x, y) < ball.radius){
      // return the 1st match
      return ball;
    }
  }
  // return null if nothing was found
  return null;
}

您已经在使用 类 和函数,但以防万一上面的语法看起来不熟悉:

  • 函数开头的 Ball 类型替换了 void,这意味着函数必须 return 类型为 Ball 的对象(相对于 void)
  • return 关键字既退出函数又 return 对球的引用(如果找到,null 否则)

要辨别是否有选定的球(在左键和右键单击之间切换时),您可以使用 Ball 变量,该变量最初是 null,但在左键单击时被分配pressed or right click is pressed 并且鼠标坐标落在球的 position/radius.

这是使用上述代码修改后的版本:

ArrayList<Ball> ballList = new ArrayList<Ball>();
// reference to selection
Ball selectedBall;

void setup() {
  size(600, 600);
}

void draw() {  
  background(150);
  // render all balls
  for (Ball ball : ballList) {
    ball.drawBall();
  }
}

// this method will trigger once every time the user press a mouse button
void mousePressed() {

  if (mouseButton == LEFT) {
    // reset the selection to the newest ball
    selectedBall = new Ball(mouseX, mouseY);
    // append it to the list
    ballList.add(selectedBall);
    println("added new ball and updated selection", selectedBall);
  }

  if (mouseButton == RIGHT) {
    // check if a ball is under the cursor, if so select it
    selectedBall = getBall(mouseX, mouseY);
    println("right click selection", selectedBall);
  }
}

void mouseDragged() {
  // update dragged ball coordinates if there is a previous selection
  if (selectedBall != null) {
    selectedBall.xPos = mouseX;
    selectedBall.yPos = mouseY;
    println("dagging selected ball", selectedBall);
  }
}

void mouseReleased() {
  // clear selection
  selectedBall = null;
  println("selection cleared");
}

Ball getBall(float x, float y) {
  // for each ball
  for (Ball ball : ballList) {
    // check if the x,y coordinates are inside any of the existing balls
    if ( dist(ball.xPos, ball.yPos, x, y) < ball.radius ) {
      // return the 1st match (exits loop and function immediately)
      return ball;
    }
  }
  // return null if nothing was found
  return null;
}

class Ball {

  float xPos, yPos; 
  float diameter = 50;
  float radius   = diameter * 0.5;

  Ball(float xPos, float yPos) {
    this.xPos = xPos;
    this.yPos = yPos;
  }
  
  void drawBall() {
    ellipse(xPos, yPos, diameter, diameter);
  }
  // pretty print info
  String toString(){
    return "[Ball x=" + xPos + " y="+ yPos + "]";
  }
}

我删除了一些未使用的变量,添加了 toString()(因此它可以使用 println() 很好地显示信息)并添加了一些可选的 println() 语句,因此更容易看到是什么继续测试代码。

最后的笔记:

  • 目前,如果您左键单击多次,您可以添加多个重叠的球。您可以调整实现以更新检查是否首先有一个球,如果有,则仅在该位置没有任何现有球的情况下添加一个新球
  • 遍历每个球并检查距离(使用 sqrt())对于大量球来说计算量会很大。在这个阶段,代码的可读性更为重要,但如果你的代码发展成更复杂的东西,你可以使用平方距离而不是 dist() 并使用其他优化技术。

更新 这是上面草图的一个调整版本,如果鼠标位置上还没有球,它只会添加一个新球(只允许鼠标左键拖动):

ArrayList<Ball> ballList = new ArrayList<Ball>();
// reference to selection
Ball selectedBall;

void setup() {
  size(600, 600);
}

void draw() {  
  background(150);
  // render all balls
  for (Ball ball : ballList) {
    ball.drawBall();
  }
}

// this method will trigger once every time the user press a mouse button
void mousePressed() {
  // update selection
  selectedBall = getBall(mouseX, mouseY);
  
  // if there isn't a ball already, add one:
  if (selectedBall == null) {
    ballList.add(new Ball(mouseX, mouseY));
  }
}

void mouseDragged() {
  // update dragged ball coordinates if there is a previous selection
  if (selectedBall != null) {
    selectedBall.xPos = mouseX;
    selectedBall.yPos = mouseY;
  }
}

void mouseReleased() {
  // clear selection
  selectedBall = null;
}

Ball getBall(float x, float y) {
  // for each ball
  for (Ball ball : ballList) {
    // check if the x,y coordinates are inside any of the existing balls
    if ( dist(ball.xPos, ball.yPos, x, y) < ball.radius ) {
      // return the 1st match (exits loop and function immediately)
      return ball;
    }
  }
  // return null if nothing was found
  return null;
}

class Ball {

  float xPos, yPos; 
  float diameter = 50;
  float radius   = diameter * 0.5;

  Ball(float xPos, float yPos) {
    this.xPos = xPos;
    this.yPos = yPos;
  }
  
  void drawBall() {
    ellipse(xPos, yPos, diameter, diameter);
  }
  // pretty print info
  String toString(){
    return "[Ball x=" + xPos + " y="+ yPos + "]";
  }
}

如果您想立即选择新添加的球,您当然可以添加新球并更新选择:

ArrayList<Ball> ballList = new ArrayList<Ball>();
// reference to selection
Ball selectedBall;

void setup() {
  size(600, 600);
}

void draw() {  
  background(150);
  // render all balls
  for (Ball ball : ballList) {
    ball.drawBall();
  }
}

// this method will trigger once every time the user press a mouse button
void mousePressed() {
  // update selection
  selectedBall = getBall(mouseX, mouseY);
  
  // if there isn't a ball already, add one:
  if (selectedBall == null) {
    selectedBall = new Ball(mouseX, mouseY);
    ballList.add(selectedBall);
  }
}

void mouseDragged() {
  // update dragged ball coordinates if there is a previous selection
  if (selectedBall != null) {
    selectedBall.xPos = mouseX;
    selectedBall.yPos = mouseY;
  }
}

void mouseReleased() {
  // clear selection
  selectedBall = null;
}

Ball getBall(float x, float y) {
  // for each ball
  for (Ball ball : ballList) {
    // check if the x,y coordinates are inside any of the existing balls
    if ( dist(ball.xPos, ball.yPos, x, y) < ball.radius ) {
      // return the 1st match (exits loop and function immediately)
      return ball;
    }
  }
  // return null if nothing was found
  return null;
}

class Ball {

  float xPos, yPos; 
  float diameter = 50;
  float radius   = diameter * 0.5;

  Ball(float xPos, float yPos) {
    this.xPos = xPos;
    this.yPos = yPos;
  }
  
  void drawBall() {
    ellipse(xPos, yPos, diameter, diameter);
  }
  // pretty print info
  String toString(){
    return "[Ball x=" + xPos + " y="+ yPos + "]";
  }
}