Trying to make objects move away from cursor

I have a class called ball and I’m creating 100 ellipses using an array and for loop in main. My goal is to make them all move away from the cursor and so far this is what I have:

void cursor()
  {
    if(dist(x,y,mouseX,mouseY) <= 100 && mousePressed)
    {
      xSpeed = -xSpeed;
      ySpeed = -ySpeed;
    }
  }

However, this is freezing any balls that the cursor passes over, as they are being told to constantly go back and forth. Is there any way to make the xSpeed/ySpeed change only happen once, or should I approach this in an entirely different way? Ie. maybe drawing an invisible circle around the cursor and making the balls bounce off of it and never being able to go inside.

Hi,

Welcome to the forum! :wink:

If you want to move the balls away from the mouse, think of it as a magnet. A magnet is applying a magnetic field on a piece of metal that may repulse it :

Since we are doing some geometry here, you need to think with vectors. If you don’t really know how they work, you can check this tutorial on the Processing website :

https://processing.org/tutorials/pvector/

The idea is to apply a force on the Ball that is in the opposite direction compared to the mouse cursor. This force depends on the distance d from the ball to the mouse since we want them to be more pushed away as soon as they are close to the mouse

A force can be represented as a vector, here represented at the origin of the ball.

In Processing, you would do :

// A position is also a vector
PVector mouseLocation = new PVector(mouseX, mouseY);

// The force is the difference between the two locations
PVector repulsionForce = PVector.sub(ballLocation, mouseLocation);

// Compute the distance between the ball and the cursor
// This is the length of the vector
float distanceFromMouse = repulsionForce.mag();

// If it's close enough to the mouse
if (distanceFromMouse < 100) {
  // We then normalize the vector so it's length is 1
  repulsionForce.normalize();

  // We multiply it by a certain amount (the power of the repulsion) that depends on the distance from the mouse
  repulsionForce.mult(map(distanceFromMouse, 0, 100, 2, 0));

  // We add the force to the velocity of the ball
  ballVelocity.add(repulsionForce);
}

// Update the ball location
ballLocation.add(velocity);

If you don’t know what some functions do, check the documentation :

So this is the basic idea, it will be in an update method for each ball so they have their own location and velocity as a vector rather than separate variables for xSpeed and ySpeed.

This may be confusing at first but vectors gives you some nice features : you can add other points that repulse the balls, others that attract them, add acceleration to the balls, collisions…

Hope it helps! :wink:

2 Likes

Josephh’s reply would work better than this, but I would get the distance from the ball and the mouse and store that in a variable. Then, I would make a PVector that transforms the value into a mapped value of how much the ball moves away, and turn the velocity of the ball into that PVector.

2 Likes

This does help a lot, thanks. Just to confirm, these vectors would be called and used in my ball object class and after using them I could get rid of anything like x = x + xSpeed?

You are welcome!

Yes basically you can get rid of your x, y, xSpeed and ySpeed variables and replace them with vectors and vectors operations.

As mentioned by @SNICKRS, there’s multiple ways to do this : in my case I add the force to the velocity but it can get quite fast if you don’t limit it’s strength or you can set the velocity to be the force.

1 Like

Yes, I agree with josephh, you should probably add some limits.

2 Likes

Got it. Haven’t added limits yet, but after fully implementing vectors, all 100 ellipses stay frozen and don’t move. I’m using ballLocation.add(ballVel); in my Ball.move function. Code is:

class Ball
{ 
  PVector ballLocation;
  PVector ballVel;
  PVector mouseLocation;
  PVector repulsionForce;
  float r1 = random(0,255);
  float b1 = random(0,255);
  float g1 = random(0,255);
  float d = random(10,30);
  float distanceFromMouse;
  Ball(float _x,float _y)
  {
    x = _x;
    y = _y;
    ballLocation = new PVector(x,y);
    ballVel = new PVector(1,1);
    mouseLocation = new PVector(mouseX,mouseY);
    repulsionForce = PVector.sub(ballLocation,mouseLocation);
    distanceFromMouse = repulsionForce.mag();
  }
  void render()
  {
    fill(r1,g1,b1);
    ellipse(x,y,d,d);
  }
  void move()
  { 
    ballLocation.add(ballVel);
  }

This is because you are drawing your ellipse with :

ellipse(x,y,d,d);

But the x and y member variables of the Ball class are never updated, you rather update the ballLocation vector.

So remove the x and y variables and use the vector representing the location instead :

ellipse(ballLocation.x, ballLocation.y, d, d);

Note that you can use the function circle() if you are drawing a circle :wink:

Also I named the position of the ball ballLocation but inside a class it makes more sense to name it location because it’s already implied that it belongs to the Ball

This helped a lot, it works now, thank you. 2 more things, first, how can I limit the velocity as if statements don’t work with pvectors. Also, my distanceFromMouse does not seem to work

PVector location;
  PVector velocity;
  PVector mouseLocation;
  PVector repulsionForce;
float distanceFromMouse = repulsionForce.mag();
 Ball(float _x,float _y)
  {
    x = _x;
    y = _y;
    location = new PVector(x,y);
    velocity = new PVector(random(-4,4),random(-4,4));
    mouseLocation = new PVector(mouseX,mouseY);
    repulsionForce = PVector.sub(location,mouseLocation);
    distanceFromMouse = repulsionForce.mag();
  }
void move()
  { 
    if(distanceFromMouse < 100)
    {
      repulsionForce.normalize();
      repulsionForce.mult(map(distanceFromMouse, 0, 100, 2, 0));
      velocity.add(repulsionForce);
      
    }
     location.add(velocity);
   }

When I change it to dist(mouseX,mouseY,location.x,location.y), the ellipses will move away from the mouse but it’s more like they lag away and will still go over the cursor, and then I run into the issue of them getting infinitely fast as they go near the cursor.

You are storing mouseLocation and repulsionForce as member variables in your class so it means that each instance of the class Ball is going to have it’s own mouse location vector…

This is not what you want because the mouseLocation can be retrieved using the global mouseX and mouseY variables and are therefore not specific for each ball. In your previous code, the mouseLocation vector was only computed once in the constructor so it’s not updating at each frame :wink:

Moreover the repulsionForce variable must be local to your move() function because it’s only needed here and it’s calculated at that specific step. This is the same for the distanceFromMouse variable.

I would rather do :

class Ball {
  PVector location;
  PVector velocity;

  Ball(float x, float y) {
    location = new PVector(x, y);
    velocity = new PVector(random(-4, 4), random(-4, 4));
  }

  void move() { 
    PVector mouseLocation = new PVector(mouseX, mouseY);
    PVector repulsionForce = PVector.sub(location, mouseLocation);

    float distanceFromMouse = repulsionForce.mag();

    if (distanceFromMouse < 100) {
      repulsionForce.normalize();
      repulsionForce.mult(map(distanceFromMouse, 0, 100, 2, 0));
      
      velocity.add(repulsionForce);
    }
    
    location.add(velocity);
  }
}

To limit the velocity strength, you can check the following PVector function :