I have a program in Processing for a bouncing ball and a rectangle. I can get the collision for the sides of the rectangle correct, but I have no idea how to get the corners. This is what I have so far:
int radius = 20;
float circleX, circleY; //positions
float vx = 3, vy = 3; //velocities float boxW, boxH; //bat dimensions
void setup(){
size(400,400);
ellipseMode(RADIUS);
rectMode(RADIUS);
circleX = width/4;
circleY = height/4;
boxW = 50;
boxH = 20;
}
void draw(){
background(0);
circleX += vx;
circleY += vy;
//bouncing off sides
if (circleX + radius > width || circleX < radius){ vx *= -1; } //left and right
if (circleY + radius > height || circleY < radius){ vy *= -1; } //top and bottom
if (circleY + radius > height){
circleY = (height-radius)-(circleY-(height-radius)); } //bottom correction
//bouncing off bat
if (circleY + radius > mouseY - boxH && circleX > mouseX - boxW && circleX < mouseX + boxW){
vy *= -1; //top
}
if (circleX - radius < mouseX + boxW && circleY > mouseY - boxH && circleY < mouseY + boxH){
vx *= -1; //right
}
if (circleY - radius > mouseY + boxH && circleX > mouseX - boxW && circleX < mouseX + boxW){
vy *= -1; //bottom
}
if (circleX + radius < mouseX - boxW && circleY > mouseY - boxH && circleY < mouseY + boxH){
vx *= -1; //left
}
if ([CORNER DETECTION???]){
vx *= -1;
vy *= -1;
}
ellipse(circleX,circleY,radius,radius);
rect(mouseX,mouseY,boxW,boxH);
}
I don't know what to put in the if statement to detect the corner collisions.
The problem isn't that you need to detect the corner collision. The problem is that you current collision detection doesn't move the ball to a side when a collision is detected.
Call frameRate(5) in your setup() function to better see what's going on:
Notice that the ball intersects the top of the box, so you multiply the vy variable by -1. That causes the circle to start moving up. But the next frame, the circle is still colliding with the rectangle, because it hasn't moved up enough yet. So your code detects that collision, multiples vy by -1 again, and the ball moves back down. Next frame the same thing happens, until the ball eventually stop colliding with the rectangle.
To fix this problem, when you detect a collision, you need to move the ball so that it's no longer colliding with the rectangle in the next frame.
Here is an example of how to do that for the top side:
if (circleY + radius > mouseY - boxH && circleX > mouseX - boxW && circleX < mouseX + boxW) {
vy *= -1; //top
circleY = mouseY-boxH-radius;
}
You'll have to add similar logic for the other sides, but the general idea is the same: make sure that the ball will not be colliding in the next frame, otherwise it'll keep bouncing on the edge like that.
Edit: Taking a closer look at your collision logic, something is still off: you're only ever checking three sides, when you really should be checking all four sides.
Let's take this one for example:
if (circleY + radius > mouseY - boxH && circleX > mouseX - boxW && circleX < mouseX + boxW){
println("top");
vy *= -1; //top
}
You're checking that the ball is below the top of the rectangle, to the right of the left of the rectangle, and to the left of the right of the rectangle. That's going to be true for this case:
Add a println() statement to each of your if statements (see the example in the if statement above) and notice what happens when the ball is below the paddle, or to the right of the paddle.
You need to refactor your logic so that you're checking all four sides, not just three. If I were you, I'd write a function that takes the next position of the ball and returns a boolean value of true if that position collides with the rectangle. Then you can check before moving the ball on the X and Y axis, which tells you how to bounce. Something like this:
if (collides(circleX + vx, circleY)) {
vx*=-1;
}
else {
circleX += vx;
}
if (collides(circleX, circleY + vy)) {
vy*=-1;
}
else {
circleY += vy;
}
This takes the place of your four separate if statements, and it solves your above problem as well.
Related
How does one constrain interactively drawing a line to 45degrees?
Imagine there in an underlining grid that's at 45 degees which the mouse draw is magnetized too. Perhaps on mousedown decides which your starting position is and after that your Mouse.X and Mouse.Y position only update in 45 degrees from that starting click?
float dif = 10;
float easing = 0.05;
boolean current = false;
boolean started = false;
float rainbow, x, y;
float colbase;
PVector pos, prev_pos, update_pos;
float step = 25;
void setup(){
size(400, 400);
background(100);
colorMode(HSB);
}
void draw(){
if (rainbow >= 255) rainbow=0; else rainbow+=5;
if (frameCount % dif == 0) {
colbase = rainbow;
}
if(mousePressed){
update_pos();
if(current){
started = true;//start drawing
pos = update_pos;
}else{
prev_pos = update_pos;
}
current = !current;
}else{
update_pos();
started = false;
pos = update_pos;
prev_pos = update_pos;
}
if(started){
//style for lines
strokeWeight(step);
stroke(colbase,255,255);
noFill();
line(prev_pos.x, prev_pos.y, pos.x, pos.y);
}
}
PVector update_pos(){
x = lerp(x, mouseX, easing);
y = lerp(y, mouseY, easing);
update_pos = new PVector(x, y);
return update_pos;
}
I would like to say that I like this. Also, that this question was ultimately way harder to answer than I though it would be.
Here's the result:
First, I took the liberty to reorganize your example code. I don't know how experienced you are, and maybe some of the things I changed were on purpose, but it seemed to me that your example had a lot of weird code bits dangling. Here's the example code with my changes (check the next code block for the answer, this one is more like a code review to help you in general):
float dif = 10;
float easing = 0.05;
boolean current, started; // automatically initializing as false unless stated otherwise
float rainbow;
float colbase;
PVector pos, prev_pos;
float step = 25;
void setup() {
size(400, 400);
background(100);
colorMode(HSB); // clever!
pos = prev_pos = new PVector(); // instanciating some non-null PVectors
}
void draw() {
pos = prev_pos = update_pos(); // cascade attribution: it starts by the last one and goes back toward 'pos'
if (mousePressed) {
if (!started) {
// initializing variables needed for drawing
started = true;
pos = prev_pos = new PVector(mouseX, mouseY);
}
} else {
started = false;
}
if (started) {
updateColor();
strokeWeight(step);
stroke(colbase, 255, 255);
noFill();
line(prev_pos.x, prev_pos.y, pos.x, pos.y);
}
}
void updateColor() {
if (rainbow >= 255) {
rainbow=0;
} else {
rainbow+=5;
}
if (frameCount % dif == 0) {
colbase = rainbow;
}
}
PVector update_pos() {
float x = lerp(pos.x, mouseX, easing);
float y = lerp(pos.y, mouseY, easing);
return new PVector(x, y);
}
Notice how I changed a couple variables names. As a general rule, you should always name your variables as if the guy maintaining your code was an angry biker who knows where you live.
Now to solve your actual question:
boolean isDrawing;
float rainbow, colbase, dif, easing, step, centerZone;
PVector pos, prev_pos, origin, quadrant;
void setup() {
size(400, 400);
background(100);
colorMode(HSB); // clever!
centerZone = 5; // determine how close to the original click you must be to change quadrant
dif = 10;
easing = 0.05;
step = 25;
origin = pos = prev_pos = new PVector();
}
void draw() {
updatePosition();
if (mousePressed) {
if (!isDrawing) {
// setting variables needed for drawing
isDrawing = true;
origin = pos = prev_pos = new PVector(mouseX, mouseY); // drawing should always start where the mouse currently is
}
} else {
isDrawing = false;
}
if (isDrawing) {
updateColor();
strokeWeight(step);
stroke(colbase, 255, 255);
noFill();
line(prev_pos.x, prev_pos.y, pos.x, pos.y);
}
}
void updateColor() {
if (rainbow >= 255) {
rainbow=0;
} else {
rainbow+=5;
}
if (frameCount % dif == 0) {
colbase = rainbow;
}
}
void updatePosition() {
float relativeX = pos.x - origin.x;
float relativeY = pos.y - origin.y;
float diffX = mouseX - origin.x;
float diffY = mouseY - origin.y;
float distance = abs(diffX) > abs(diffY) ? abs(diffX) : abs(diffY); // this is just inline if, the syntax being " condition ? return if true : return if false; "
prev_pos = pos;
// validating if the mouse is in the same quadrant as the pencil
PVector mouseQuadrant = new PVector(diffX > 0 ? 1 : -1, diffY > 0 ? 1 : -1);
// we can only change quadrant when near the center
float distanceFromTheCenter = abs(relativeX) + abs(relativeY);
if (quadrant == null || distanceFromTheCenter < centerZone) {
quadrant = new PVector(diffX > 0 ? 1 : -1, diffY > 0 ? 1 : -1);
}
// if the mouse left it's quadrant, then draw toward the center until close enough to change direction
// ^ is the XOR operator, which returns true only when one of the sides is different than the other (one true, one false)
// if the quadrant info is positive and the diff coordinate is negative (or the other way around) we know the mouse has changed quadrant
if (distanceFromTheCenter > centerZone && (relativeX > 0 ^ mouseQuadrant.x > 0 || relativeY > 0 ^ mouseQuadrant.y > 0)) {
// going toward origin
pos = new PVector(lerp(prev_pos.x, origin.x, easing), lerp(prev_pos.y, origin.y, easing));
} else {
// drawing normally
pos = new PVector(lerp(prev_pos.x, origin.x + (distance * quadrant.x), easing), lerp(prev_pos.y, origin.y + (distance * quadrant.y), easing));
}
}
I'll answer your questions on this code if you have any. Have fun!
EDIT:
This part could use more explainations, so let's elaborate a little bit:
if (distanceFromTheCenter > centerZone && (relativeX > 0 ^ mouseQuadrant.x > 0 || relativeY > 0 ^ mouseQuadrant.y > 0)) {
// going toward origin
} else {
// drawing normally
}
The point of this check is to know if the mouse is still in the same quadrant than the "pencil", by which I mean the point where we're drawing.
But what are the "quadrants"? Remember, the user can only draw in 45 degrees lines. Theses lines are defines in relation to the point where the user clicked to draw, which I call "origin":
Which means that the screen is always divided into 4 different "quadrants". Why? Because there is 4 different directions where we can draw. But we don't want the user to have to stick to these exact pixels to be able to draw, do we? We could, but that's not how the algo is working: it poses a pencil on the page and then follows the mouse. So here if the mouse goes up-left from origin, the pencil will draw on the up-left branch of the 45 degrees X lines. Each of these imaginary lines has it's own "part of the screen" where they control where the pencil has the right to draw, which I call "quadrants":
Now with this algorithm we can force the pencil over the 45 degrees lines, but what happens if the mouse goes from one quadrant to another one? I figured out that the pencil should follow, but without breaking the "only drawing in 45 degrees lines" rule, so it has to go through the center before changing quadrant:
There is no big secret here. It's kind of easy to figure the business rules we want to apply:
While the mouse and the pencil are in the same quadrant, follow the mouse's position to draw (but only on the line).
If the mouse is in a different quadrant from the pencil, draw toward the center, then toward the mouse normally.
Which is exactly what we're doing here:
if (mouse and pencil are in different quadrants) {
// draw toward origin
} else {
// draw toward the mouse
}
Then... if it's so simple, why this convoluted code?
(distanceFromTheCenter > centerZone && (relativeX > 0 ^ mouseQuadrant.x > 0 || relativeY > 0 ^ mouseQuadrant.y > 0))
Well, really, it could be written in a waaay easier to read way, but it would be longer. Let's decompose it and see why it's written this way:
My operational logic here go as follow:
As you probably know, point (0,0) is on the upper-left side of the sketch. Most drawing in computer science calculate coordinates this way.
Quadrants exist in relation to the origin point (where the user clicked to start drawing).
Relative coordinates in each quadrant will be either positive or negative. X will be positive if they are to the right of origin. Y will be positive if they are lower in the screen than origin:
If you follow my logic, when relative coordinate of the mouse and pencil aren't both positive or both negative in the same way, it would mean that they are not located in the same quadrant.
So:
distanceFromTheCenter > centerZone => when we're close enough to the center, we can let the pencil switch direction. No need to calculate the rest if the pencil isn't near the center.
relativeX > 0 ^ mouseQuadrant.x > 0 => relativeX is really just pos.x - origin.x. It's where the pencil is in relation to origin. mouseQuadrant "knows" where the mouse is in relation to the origin (it's just diffX and diffY as interpreted for later use, in fact we totally could have used diffX and diffY instead as we just compare if they are positive or negative numbers)
Why the XOR operator if it's so simple? Because of this:
relativeX > 0 ^ mouseQuadrant.x > 0
// is the same thing than this pseudocode:
if (relativeX sign is different than mousequadrant.x's sign)
// and the same thing than this more elaborated code:
!(relativeX > 0 && mouseQuadrant.x > 0) || !(relativeX < 0 && mouseQuadrant.x < 0)
// or, in a better writing:
(relativeX > 0 && mouseQuadrant.x < 0) || (relativeX < 0 && mouseQuadrant.x > 0)
...which is also convoluted and also ugly. So, really, (relativeX > 0 ^ mouseQuadrant.x > 0 || relativeY > 0 ^ mouseQuadrant.y > 0) is just a short hand for:
(!(relativeX > 0 && mouseQuadrant.x > 0) || !(relativeX < 0 && mouseQuadrant.x < 0) || !(relativeY > 0 && mouseQuadrant.y > 0) || !(relativeY < 0 && mouseQuadrant.y < 0))
I hope this just made sense! Have fun!
I have a program in Processing for a bouncing ball and a rectangle. I can get the collision for the sides of the rectangle correct, but I have no idea how to get the corners. This is what I have so far:
int radius = 20;
float circleX, circleY; //positions
float vx = 3, vy = 3; //velocities float boxW, boxH; //bat dimensions
void setup(){
size(400,400);
ellipseMode(RADIUS);
rectMode(RADIUS);
circleX = width/4;
circleY = height/4;
boxW = 50;
boxH = 20;
}
void draw(){
background(0);
circleX += vx;
circleY += vy;
//bouncing off sides
if (circleX + radius > width || circleX < radius){ vx *= -1; } //left and right
if (circleY + radius > height || circleY < radius){ vy *= -1; } //top and bottom
if (circleY + radius > height){
circleY = (height-radius)-(circleY-(height-radius)); } //bottom correction
//bouncing off bat
if (circleY + radius > mouseY - boxH && circleX > mouseX - boxW && circleX < mouseX + boxW){
vy *= -1; //top
}
if (circleX - radius < mouseX + boxW && circleY > mouseY - boxH && circleY < mouseY + boxH){
vx *= -1; //right
}
if (circleY - radius > mouseY + boxH && circleX > mouseX - boxW && circleX < mouseX + boxW){
vy *= -1; //bottom
}
if (circleX + radius < mouseX - boxW && circleY > mouseY - boxH && circleY < mouseY + boxH){
vx *= -1; //left
}
if ([CORNER DETECTION???]){
vx *= -1;
vy *= -1;
}
ellipse(circleX,circleY,radius,radius);
rect(mouseX,mouseY,boxW,boxH);
}
I don't know what to put in the if statement to detect the corner collisions.
The problem isn't that you need to detect the corner collision. The problem is that you current collision detection doesn't move the ball to a side when a collision is detected.
Call frameRate(5) in your setup() function to better see what's going on:
Notice that the ball intersects the top of the box, so you multiply the vy variable by -1. That causes the circle to start moving up. But the next frame, the circle is still colliding with the rectangle, because it hasn't moved up enough yet. So your code detects that collision, multiples vy by -1 again, and the ball moves back down. Next frame the same thing happens, until the ball eventually stop colliding with the rectangle.
To fix this problem, when you detect a collision, you need to move the ball so that it's no longer colliding with the rectangle in the next frame.
Here is an example of how to do that for the top side:
if (circleY + radius > mouseY - boxH && circleX > mouseX - boxW && circleX < mouseX + boxW) {
vy *= -1; //top
circleY = mouseY-boxH-radius;
}
You'll have to add similar logic for the other sides, but the general idea is the same: make sure that the ball will not be colliding in the next frame, otherwise it'll keep bouncing on the edge like that.
Edit: Taking a closer look at your collision logic, something is still off: you're only ever checking three sides, when you really should be checking all four sides.
Let's take this one for example:
if (circleY + radius > mouseY - boxH && circleX > mouseX - boxW && circleX < mouseX + boxW){
println("top");
vy *= -1; //top
}
You're checking that the ball is below the top of the rectangle, to the right of the left of the rectangle, and to the left of the right of the rectangle. That's going to be true for this case:
Add a println() statement to each of your if statements (see the example in the if statement above) and notice what happens when the ball is below the paddle, or to the right of the paddle.
You need to refactor your logic so that you're checking all four sides, not just three. If I were you, I'd write a function that takes the next position of the ball and returns a boolean value of true if that position collides with the rectangle. Then you can check before moving the ball on the X and Y axis, which tells you how to bounce. Something like this:
if (collides(circleX + vx, circleY)) {
vx*=-1;
}
else {
circleX += vx;
}
if (collides(circleX, circleY + vy)) {
vy*=-1;
}
else {
circleY += vy;
}
This takes the place of your four separate if statements, and it solves your above problem as well.
I'm trying for the first time to make Pong. I don't always want the ball to go to the bottom right by adding 3 every single time. How would I make it so it will either do 3, or -3, but no number in between? I know that "||" doesn't work for integers, and "random(-3,3) has the chance of giving me numbers like "0.1" which wouldn't really function in here.
Code:
float circleX = 640/2;
float circleY = 360/2;
float xSpeed = 3;
float ySpeed = 3;
float Color = (255);
float circleHeight = 32;
float circleWidth = 32;
float xAcceleration = -1.0;
float yAcceleration = -1.0;
float paddleColor = 255;
float MyPaddleX = 630;
float OpPaddleX = 10;
float MyPaddleWidth = 10;
float OpPaddleWidth = -10;
void setup() {
size(640, 360);
frameRate(60);
}
void draw() {
background(0);
//Ball
fill(Color);
ellipse(circleX, circleY, circleWidth, circleHeight);
xSpeed = //(WHAT TO PUT HERE?)
circleX = circleX + xSpeed;
circleY = circleY + ySpeed;
//My Paddle
fill(paddleColor);
rect(MyPaddleX,mouseY,MyPaddleWidth,100);
//Bouncing
if (circleX >= OpPaddleX && OpPaddleX + OpPaddleWidth >= circleX) {
xSpeed = xSpeed * xAcceleration;
}
// Top/Bottom Bouncing
if (circleY > height || circleY < 0) {
ySpeed = ySpeed * yAcceleration;
}
//My Paddle Bounceback
if (circleY >= mouseY && circleY <= mouseY + 100) {
if (circleX >= MyPaddleX && circleX <= MyPaddleX + 3)
xSpeed = xSpeed * xAcceleration;
}
//Opponent Paddle
fill(paddleColor);
rect(OpPaddleX,circleY - 50,OpPaddleWidth,100);
//if (circleX < OpPaddleX || circleX > MyPaddleX) {
// circleX = width/2;
// circleY = height/2;
// xSpeed = 0;
// ySpeed = 0;
//}
}
You can generate a number between 0 and 1 and then compare that generated number to 0.5 to "flip a coin" in your code.
Think about it this way: when you call random(1), you'll get a value between 0 and 1. Half of those values will be less than 0.5, the other half will be greater than (or equal to) 0.5.
So you can do something like this:
float x;
if(random(1) < .5){
x = -3;
}
else{
x = 3;
}
You could expand this to choose from more numbers using else if statements, or you could shorten it into a single line of code using the ternary operator:
float x = random(1) < .5 ? 3 : -3;
How can I make the mouse move like a joystick. Here is a example of what I mean except this only moves to the right and when the mouse is all the way to the right it stops. I want the circle to stop if the mouse is at the center and move where ever the mouse is.
float ballX = 0; // need to keep track of the ball's current position
float ballY = 150;
void setup() {
size(300,300); // standard size
}
void draw() {
background(200); // dull background
float speed = 2.0 * ( mouseX / (width*1.0) );
println("speed is " + speed); // print just to check
ballX = ballX + speed; // adjust position for current movement
fill(255,0,0);
ellipse(ballX, ballY, 20,20);
}
You want to check the mouseX position against the center of the window, which is width/2.
To find the relative position of the mouse, simply subtract that center position from your mouse position. That way when the mouse is to the left of the center, that value is negative, and your ball will move to the left.
You might also want to keep your ball on the screen.
float ballX = 0; // need to keep track of the ball's current position
float ballY = 150;
void setup() {
size(300,300); // standard size
}
void draw() {
float centerX = width/2;
float maxDeltaMouseX = width/2;
float deltaMouseX = mouseX-centerX;
float speedX = deltaMouseX/maxDeltaMouseX;
background(200); // dull background
println("speed is " + speedX); // print just to check
ballX = ballX + speedX; // adjust position for current movement
//keep ball on screen
if(ballX < 0){
ballX = 0;
}
else if(ballX > width){
ballX = width;
}
fill(255,0,0);
ellipse(ballX, ballY, 20,20);
}
I am making a 2d plat former esc game and I have my game translate my characters x and y cords so my character is always in the middle of the screen, it seems however that mouseX and mouseY do not translate... how would I convert the mouseX and mouseY cords?
here is my translation code
void draw() {
background(100);
if (updateBlocks == true) {
updateBlocks();
}
pushMatrix();
translate(-player.location.x + 320, -player.location.y + 320);
mx = mouseX -player.location.x + 320;
my = mouseY -player.location.y + 320;
for(int a = 0; a < mapWidth; a ++) {
for(int b = 0; b < mapHeight; b ++) {
if(mx >= 16 * a && mx <= 16 * a + 16 && my >= 16 * b && my <= 16 * b + 16) {
map[a][b] = 1;
updateBlocks();
break;
}
}
}
for (int a = validBlocks.size()-1; a >= 0; a --) {
PVector validBlock = validBlocks.get(a);
rect(validBlock.x, validBlock.y, 16, 16);
}
player.update();
player.display();
popMatrix();
}
Yes, mouseX and mouseY are in terms of your window, regardless of your transformation matrix (translate, rotate, etc). (0, 0) is at the top-left corner no matter what's going on in your screen.
You have to translate that point yourself. In your case, some basic subtraction will do.