I have a project that creates an array list of balls (ellipses). When I press my mouse down (left button) and hold it an ellipse follows my mouse. When I let go the ellipse is placed on the screen where my mouse was.
I wanna be able to right click and hold over an ellipse (any random one) and have it following my mouse again like previous. and again if I let go of the mouse button, it should be placed back on the screen where my mouse is currently positioned.
I am struggling to understand how I can find the x y position of the ellipse that is already on the screen and remove the ellipse from the list and have it follow my mouse again.
Any suggestion let me know- Here is my main 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));
}
}
here is my ball 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(){
}
}
You can check if a ball is under the cursor by checking if the distance(dist()
) between the ball's position and the mouse's position:
if(dist(ball.x, ball.y, mouseX, mouseY) < ball.radius){
println("mouse over ball");
}
Currently you're hardcoding the ball diameter (50), but you could easily add a radius
property:
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);
}
}
The condition can be wrapped in a for loop to do the same check on every ball and into a function which either returns the first ball matching the condition or null
(in case there is no ball under the cursor):
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;
}
You're already using classes and functions, but just in case the above syntax looks unfamiliar:
Ball
type at the begining of the function replaces void
and it means the function must return an object of type Ball
(as opposed to void
)return
keyword both exits the function but also returns the reference to the ball (if found, null
otherwise)To discern whether there's a selected ball or not (when switching between left and right click) you could use a Ball
variable which initially is null
, but gets assigned either then left click is pressed or right click is pressed and there's a mouse coordinates fall within the ball's position/radius.
Here's a modified version of your code using the above:
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 + "]";
}
}
I've removed a few unused variables, added toString()
(so it displays info nicely using println()
) and sprinkled a few optional println()
statements so it's easier to see what's going as you test the code.
Final notes:
sqrt()
) can get computationally expensive for a large number of balls. At this stage code readability is more important, but in case you code develops into something a lot more complex you could used squared distance instead of dist()
and use other optimisation techniques.Update Here's a tweaked version of the above sketch which only adds a new ball if there isn't one already at the mouse location (allowing mouse left only dragging):
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 + "]";
}
}
If you want to pick the newly added ball immediately you can off course both add the new ball and update the selection:
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 + "]";
}
}