I am trying to get a ball to bounce off a square, but I am having trouble detecting the collision and having the ball bounce properly.I tried making two rectangles and using intersect, but this doesn't work and I don't know why. Should I write the collision detection manually? If so, how? Or is the rectangle method not working because of some other issue in my project. Here is my current collision detection.
public boolean collidedWith(Ball ball){
int ballX = ball.getXPosition();
int ballY = ball.getYPosition();
int ballRadius = ball.getRadius();
int squareX = this.getXPosition();
int squareY = this.getYPosition();
Rectangle me = new Rectangle(squareX, squareY, size, size);
Rectangle other = new Rectangle(ballX, ballY, ballRadius,ballRadius);
if(me.intersects(other)|| other.intersects(me))
{
return true;
}
else
return false;
}
}
I find it very hard to understand your code. You didn't quite well.
But in order to detect a collision -
You check if the ball hit the bounds of the square.
if (Math.abs(ballX + ballVelocityX) + ballRadius > SquareX)
...;
if (Math.abs(ballY + ballVelocityY) + ballRadius > SquareY)
...;
Math abs is the absolute value.
Related
I have been coding a significantly simple game for my academic work in which the PNG bee sprite is meant to run away from the orange ball. If the bee collides with the orange ball, she dies. Apart from this, I intend to include a timer that keeps going onwards as the bee succeeds in running away from the ball. The ball moves automatically throughout the screen, whereas the bee bounces throughout the screen with the arrow keys and gravity.
I have come across some explanations towards collision detection upon the Processing forum, however I still don't understand how this event can occur in the cases of circles x circles collisions, rectangles x circles collisions, etc.
Please, excuse my messy code. Also, excuse my poor description of what I want to know.
This is what I see on the screen:
My code:
//background
PImage background;
// keys | keyboard
boolean upPressed = false;
boolean downPressed = false;
boolean leftPressed = false;
boolean rightPressed = false;
// bee player character
PImage charImage;
float charSpeed = 5.5;
float charX = 0;
float charY = 450;
float gravity = 1.5;
float vy = 0;
float bounce = -0.8;
// platforms
PImage bee;// bee
float beeX = 300;
float beeY = 490;
PImage hi; // hi
float hiX = 400;
float hiY = 300;
PImage ve; // ve
float veX = 420;
float veY = 440;
// more beehives
PImage beehive4;// beehive4
float bee4X = 120;
float bee4Y = 90;
PImage beehive5; // beehive5
float bee5X = 200;
float bee5Y = 300;
PImage beehive6; // beehive6
float bee6X = 30;
float bee6Y = 400;
PImage beehive7; // beehive7
float bee7X = 496;
float bee7Y = 90;
// enemy ball
float ballX = 100;
float ballY = 100;
float xspeed = 100;
float yspeed = 100;
//////
public void setup() {
size(600, 800);
noStroke();
smooth();
noFill(); // to adjust the image in the screen properly in fullscreen
//load beehives
bee = loadImage("beehive 1.png");
bee.resize(100, 100);
hi = loadImage("beehive 2.png");
hi.resize(100,100);
ve = loadImage("beehive 3.png");
ve.resize(100, 100);
// load more beehives
beehive4 = loadImage("beehive 4.png");
beehive4.resize(100, 100);
beehive5 = loadImage("beehive 5.png");
beehive5.resize(100, 100);
beehive6 = loadImage("beehive 6.png");
beehive6.resize(100, 100);
beehive7 = loadImage("beehive 7.png");
beehive7.resize(100, 100);
}
/*********** drawing section !***********/
public void draw() {
background(244, 240, 219);
noStroke();
// render beehives
image(bee, beeX, beeY);
image(hi, hiX, hiY);
image(ve, veX, veY);
// render more beehives
image(beehive4, bee4X, bee4Y);
image(beehive5, bee5X, bee5Y);
image(beehive6, bee6X, bee6Y);
image(beehive7, bee7X, bee7Y);
// render bee
charImage = loadImage("bee walk 3.png");
charImage.resize(200, 200);
vy += gravity; // it applies gravity to the bee sprite
charY += vy;
if(charY > height - 150 )
vy *= bounce; // bouncing bee
// Add the current speed to the location.
ballX = ballX + xspeed;
ballY = ballY + yspeed;
// Check for bouncing
if ((ballX > width) || (ballX < 0)) {
xspeed = xspeed * -1;
}
if ((ballY > height) || (ballY < 0)) {
yspeed = yspeed * -1;
}
// Display at x,y location
stroke(0);
fill(179, 98, 0);
ellipse(ballX, ballY,80,80);
// update keys
if (upPressed) {
charY--;
}
if (downPressed) {
charY++;
}
if (leftPressed) {
charX--;
}
if (rightPressed) {
charX++;
}
if(keyPressed){
if(keyCode == UP && charY > 0){
charY -= 30;
}
if(keyCode == DOWN && charY < height){
charY += 10;
}
if(keyCode == LEFT && charX > 0){
charX -= 30;
}
if(keyCode == RIGHT && charX < width){
charX += 10;
}
}
// render beecharacter on screen
image(charImage, charX, charY);
}
There mulitple ways to tackle the problem.
Collision detection can be coarse: less accurate but faster (and simpler) or detailed (e.g. pixel level precision) but slower (and more complex).
In terms of simple collision detection two options could rectangle or circle intersections.
Rectangle intersection can be implemented manually or using Rectangle's intersects() method.
Circle intersection is trivial: if the distance(dist()) between the 1st circle's center and 2nd circle's center is smaller than the two radii then they must intersect.
here's a basic example illustrating circle intersection:
// check collision: circle
if (dist(ballX, ballY, charX, charY) < charImage.width) {
// tint red to display collision: placeholder for subtracting bee health
tint(192, 0, 0);
}else{
noTint();
}
this condition can be added before this section in draw():
// render beecharacter on screen
image(charImage, charX, charY);
The collision detection is quite rough: not pixel perfect, but hopefully a good starting point. It should tint everything red if there's a collision.
I have two other small suggestions:
reducing xspeed, yspeed to 10 or smaller values will make the game more playable. 100px/frame is too fast for people and the bee will likely collide instantly before the users even has a chance to react.
intead of a boolean beeIsAlive sort of variable that immediately, with one ball hit switches to true also might be too harsh as a game rule. Consider something like int beeHealth = 100; and gradually reducing the health with each collision will make the game more playable.
im trying to make the bouncing ball bounce on the arrays of rectangles. I've looked at various other codes but cant seem to find a solution. Would appreciate any help!!!
Basically, i want the bouncing ball to recognise that theres the rectangles there and for it to be able to jump onto the rectangles.
PVector location; // Location of shape
PVector velocity; // Velocity of shape
PVector gravity; // Gravity acts at the shape's acceleration
PVector upwardForce;
PImage bg;
int radius = 10, directionX = 1, directionY = 0;
float x=20, y=20, speed=0.5;
int xarray[] = new int[20];
int yarray[] = new int[20];
// =========================================================
void setup() {
size(380,750);
location = new PVector(100,50);
velocity = new PVector(0.0,2.1);
upwardForce = new PVector(0.0,-10.0);
gravity = new PVector(0,0.4);
bg = loadImage("bg.png");
bg.resize(1600,1600);
background(0);
for(int i =0; i< 20;i++){
xarray[i]= i*100;
yarray[i] = 750-int(random(10))*50;
}
}
int xd =0, yd=0;
void draw() {
background(0);
noStroke();
xd--;
yd++;
// display image twice:
image(bg, y, 0);
image(bg, y+bg.height, 0);
// pos
y--;
if (y<-bg.height)
y=0;
for (int i = 0;i< 20;i++){
if (xarray[i] <100 && xarray[i]+100 >100){
fill(255,0,0);
}
else {
fill(255);
}
rect(xarray[i],yarray[i],100,1200);
fill(255);
xarray[i]=xarray[i]-4;
//yarray[i]=yarray[i]+1;
if (xarray[i] + 100 < 0){
xarray[i]+=2000;
// yarray[i]-=850;
}
}
// changing Position
x=x+speed*directionX;
y=y+speed*directionY;
// check boundaries
if ((x>width-radius) || (x<radius))
{
directionX=-directionX;
}
if ((y>height-radius) || (y<radius))
{
directionY=-directionY;
}
// draw
// if(direction==1)
// Add velocity to the location.
location.add(velocity);
// Add gravity to velocity
velocity.add(gravity);
// Bounce off edges
if ((location.x > width) || (location.x < 0)) {
velocity.x = velocity.x * -1;
}
if ((location.y > height) || (location.y < 0)){
// We're reducing velocity ever so slightly
// when it hits the bottom of the window
velocity.y = velocity.y * -0.95;
location.y = height;
}
// Display circle at location vector
stroke(255);
strokeWeight(0);
fill(255);
ellipse(location.x,location.y,30,30);
}
void keyPressed()
{
velocity.add(upwardForce);
}
The best advice we can give you is to break your problem down into smaller steps and to take those steps on one at a time.
For example, can you create a simple sketch that just shows a single hard-coded circle and a single hard-coded rectangle? Now add some code that prints a message to the console if they're colliding. You're going to have to do some research into collision detection, but here's a hint: a common technique is to treat the ball as a rectangle, so you can do rectangle-rectangle collision detection.
Get that working perfectly by itself, and then work your way forward in small steps. Can you add a second rectangle to your sketch? How about a third?
Then if you get stuck, you can post a MCVE (not your whole project, just a small example) along with a more specific question. Good luck.
Here's a few suggestions:
You're best off using a Rectangle class. That way, you don't have to store the locations in an array, and the collide function can be a method of the class. It's easier to just call the positions of the rectangles "x" and "y", but this would obviously conflict with the x and y global variables which you declared at the top of the code. Assuming that you would want to make the ball bounce if it collided, you would need to have a "ballLastx" and a "ballLasty" in order to keep track of which direction the ball came from. You would also need to store the Rectangles in an array or arrayList. It would be something like this:
PVector lastLocation;
Rectangle[] rects;
As for the rectangle class, here's how it would probably look like this:
class Rectangle {
float x, y;
Rectangle(float x_, float y_) {
x = x_;
y = y_;
}
void show() {
//Displays rectangle
if (x < 100 && x+100 > 100) fill(255,0,0);
else fill(255);
rect(x,y,100,1200);
fill(255);
x=x-4;
if (x + 100 < 0) x+=2000;
}
private boolean insideX(PVector pos) {
return (pos.x + 15 >= x && pos.x - 15 <= x+100);
}
private boolean insideY(PVector pos) {
return (pos.y + 15 >= y && pos.y - 15 <= x + 1200);
}
boolean collidedX() {
//Detects if the ball has collided along the x-axis
return ((insideX(location) && !insideX(lastLocation)) && insideY(location))
}
boolean collidedY() {
//Detects if the ball has collided along the y-axis
return ((insideY(location) && !insideY(lastLocation)) && insideX(location))
}
}
And then, in your setup function, you could declare the Rectangle classes in a for-loop:
//declare the rects array
rects = new Rectangle[20];
//declare each item of the rects array to be a Rectangle
for(int i = 0; i < rects.length; i++) {
rects[i] = new Rectangle(i*100, 750-int(random(0,10))*50;
}
In order to detect the collision and to bounce the ball, you would need to loop through all of the Rectangles and see if the ball should bounce off any of them:
boolean bouncex = false;
boolean bouncey = false;
//see if any of the rects are colliding with the ball
for(Rectangle r : rects) {
if(r.collidedX()) bouncex = true;
if(r.collidedY()) bouncey = true;
}
//if any are colliding, bounce the ball
if(bouncex) velocity.x = -velocity.x;
if(bouncey) velocity.y = -velocity.y;
Finally, don't forget to set the lastLocation PVector to the current location, just before moving the current location:
lastLocation = location.copy();
//move the ball...
Hope this was helpful!
I'm very new to libgdx and game dev. Ive been tasked with an assignment where I am given a very simple 2D game which has rectangular collision detection already implemented.
The game is just made up of a square which can be controlled by the player, which is inside of walls with other scattered squares within the walls. I now need to implement pixel perfect collision between the player square and the scattered squares/walls.
I originally tried using Pixmap and tried to check if the pixels of the current coordinate were transparent for the player rectangle and whichever rectangle it is colliding with but am having trouble implementing the code correctly.
I understand that I must just check the pixel colors for both objects at a certain coordinate and see if they both are not transparent for a collision to occur but I am having trouble. I have spent a very long time researching and looking online so any help or advice on how to execute this would be greatly appreciated!
public boolean entityCollision(Entity e1, Direction direction, float newX, float newY) {
boolean collision = false;
for(int i = 0; i < entities.size(); i++) {
Entity e2 = entities.get(i);
if (e1 != e2) {
if (newX < e2.x + e2.width && e2.x < newX + e1.width &&
newY < e2.y + e2.height && e2.y < newY + e1.height) {
collision = true;
int colorOne = player.getPixel((int)newX,(int)newY);
int colorTwo = enemy.getPixel((int)e2.x,(int)e2.y);
if (colorOne != 0 && colorTwo != 0) {
collision = true;
e1.entityCollision(e2, newX, newY, direction);
}
}
}
}
return collision;
}
Take a look at the Rectangle implementation of libgdx. It allows you to do something like this:
Rectangle e1Rec = new Rectangle(e1.x, e1.y, e1.width, e1.height);
Rectangle e2Rec = new Rectangle(e2.x, e2.y, e2.width, e2.height);
if (e1Rec.contains(e2Rec)) {
// Collision!
}
If you want collisions with a sprite, you get the sprite rectangle and check for collisions inside of that rectangle.
sprite.getboundingrectangle().contains(x, y) ; //returns a boolean
I am making a game where a ball goes and collects other balls.
I have written a collision detector, but I am finding that it is a little off kilter.
If the controlled ball is about 10 pixels to the right of the ball to be collected, it will still register a collision.
If the user's ball is on the left side of the ball to be collected, the collision works well.
Could someone please explain why this is happening and/or suggest a fix? Thanks!
Here is my collision detector:
private void checkForCollision(UserBall b) {
int ballX = b.getX();
int ballY = b.getY();
int ballR = b.getRadius();
int a = x - ballX;
int bb = y - ballY;
int collide =radius + ballR;
if (a*a + bb*bb <=collide*collide){
performAction(b);
createNew= true;
}
}
This is js code, nearest I found on my computer. It works fine.
Ball.prototype.intersects = function (p) {
'use strict';
switch (p.id) {
case IDS.Ball:
var dist = this.radius + p.radius,
dx = this.x - p.x, //x - is center of Ball
dy = this.y - p.y;
return dist * dist >= (dx * dx + dy * dy);
default:
throw "Ball does not provide intersects for " + p.id;
}
};
Also C++ code, I found on my computer. It also works fine
bool Ball::intersects(Ball &b) {
if (this != &b) {
int dx = b.x - this->x; //x is center of Ball
int dy = b.y - this->y;
int sqrdist = dx*dx + dy*dy;
int sumrad = b.radius + this->radius;
return sumrad*sumrad >= sqrdist;
}
return false;
}
It also works fine. So I suggest that you calculate position of balls before check for intersection.
P.S. And please, distinguish method to check intersection, which return bool. Do not mix checking of event with action on event.
I just inserted your code snippet on a similar collision detection method in one of my games and it worked as expected.
However, i remember i ran into a similar issue when i was testing mine, the issue was with the image displaying, not the collision detection. If you are using LWJGL or something like that for rendering, check that your textures/images are of a power of two size. Sometimes, they just add padding to the nearest power of two and it "seems" like they are not colliding when they actually are, just not visually.
I am making a java game using tiles. I am having trouble with the collision element. I am defining rectangles for each tile on the map, and another rectangle for the player. What I am having trouble with is knowing which side the player is coming from when it hits the rectangle, and then pushing the player off in the direction the player came from. I have already made a method that checks how much the character is inside the rectangle, so it can know how much to push it out, but I can't figure out how to tell which side the character is coming from.
here is my current collision methoe - note rect1 is the character and rect2 is the tile
public void collision(Rectangle rect1, Rectangle rect2) {
float xAdd;
float xAdd2;
float yAdd;
float yAdd2;
boolean hitRight = false;
boolean hitLeft = false;
boolean hitTop = false;
boolean hitBot = false;
Vector2f rect1Origin = new Vector2f(rect1.x, rect1.y);
Vector2f rect2Origin = new Vector2f(rect2.x, rect2.y);
Vector2f rect1Mid = new Vector2f((rect1.x + rect1.width) / 2,(rect1.y + rect1.height) / 2);
Vector2f rect2Mid = new Vector2f((rect2.x + rect2.width) / 2,(rect2.y + rect2.height) / 2);
Vector2f rect1A = new Vector2f(rect1Origin.x + rect1.width, rect1.y);
Vector2f rect1B = new Vector2f(rect1Origin.x, rect1Origin.y+ rect1.height);
Vector2f rect1C = new Vector2f(rect1Origin.x + rect1.width,rect1Origin.y + rect1.height);
Vector2f rect2A = new Vector2f(rect2Origin.x + rect2.width, rect2.y);
Vector2f rect2B = new Vector2f(rect2Origin.x, rect2Origin.y
+ rect2.height);
Vector2f rect2C = new Vector2f(rect2Origin.x + rect2.width,
rect2Origin.y + rect2.height);
xAdd = rect2C.x - rect1B.x;
xAdd2 = rect1C.x - rect2B.x;
yAdd = rect2A.y - rect1B.y;
yAdd2 = rect2C.y - rect1A.y;
if (rect1Mid.y < rect2Mid.y) {
if (rect1.intersects(rect2)) {
y_pos += yAdd;
}
}
if (rect1Mid.y > rect2Mid.y) {
if (rect1.intersects(rect2)) {
System.out.println(yAdd2);
y_pos += yAdd2;
}
}
if(rect1Mid.x > rect2Mid.x){
if(rect1.intersects(rect2)){
hitRight = true; x_pos += xAdd;
}
}
if(rect1Mid.x< rect2Mid.x){
if(rect1.intersects(rect2)) {
x_pos += -xAdd2;
}
}
}
Any help would be greatly appreciated
Thanks
Keep two positions for your character - where the it is (as of last frame, move, etc.), and where you want to move it. Then only move it if you don't detect collision - if you don't allow for corrupt state, you don't have to fix it.
Edit: The collision method should be boolean - it should be done before actually moving the character, like
if (!collision(character, tile))
{
doMove(character);
}
else
{
//custom handling if required
}
Edit2: The previous would work only for a small step, if you need a partial move, you really need to know the character's original position, like move(originalPosition, desiredPosition, tile), where you can deduce the direction from the originalPosition and the tile.
The main point is, you don't actually move the character, before you have a valid position for it.
First of all, any Sprite (the character and the tile here) should have four members: xPos, yPos, xVec, yVec. With that in mind, you know where the character was and will be in the next frame.
sprite.xPos += sprite.xVec;
sprite.yPos += sprite.yVec;
Also take a look at your code, you should improve the propositions (for instance, you check if(rect1.intersects(rect2)) in the four if statements). Instead, check only once for the intersects(), and inside check for each possible case (left, right, top, bottom hit).
Finally, the collision method should receive 2 sprites objects (for instance, sprite1 and sprite2), and then check for the intersects() method, taking into account the original positions of both sprites and its vectors. If the sprites intersect in the new position, then you invert the object's vector accordingly.
Assuming you can detect the collision with your code. Here is a way to determine the position of your character with respect to the rectangle object.
Find the middle point of the rectangle. ( Rx,Ry).
Find the middle point of your character sprite. ( Sx,Sy).
Now you can compare Rx,Ry , Sx , Sy to determine in which side is Sx and Sy of Rx and Ry
for ex:
if Sx < Rx then
Sx = left side of Rx
Was struggling with this myself, but after some thinking this check can be done by having all your entities in a List, in my code I had to block the player from moving through blocks. All blocks is in a List. Now, when checking players position I create a new Rectangle and put the players position one frame forward in the direction the update will happened next frame, if the the rectangle intersects that update for that direction will not happened. Heres my code for this:
if (isKeyPressed(KeyEvent.VK_LEFT)) {
if(!collisionWithBlocks(1)){
pl.x = pl.x - updatePlayerPosition;
}
}
if (isKeyPressed(KeyEvent.VK_RIGHT)) {
if(!collisionWithBlocks(0)){
pl.x = pl.x + updatePlayerPosition;
}
}
if (isKeyPressed(KeyEvent.VK_UP)) {
if(!collisionWithBlocks(3)){
pl.y = pl.y - updatePlayerPosition;
}
}
if (isKeyPressed(KeyEvent.VK_DOWN)) {
if(!collisionWithBlocks(2)){
pl.y = pl.y + updatePlayerPosition;
}
}
collisionWithBlocks():
public boolean collisionWithBlocks(int side){
for(Block b : main.blocks){
Rectangle block = b.getBounds();
Rectangle player = null;
if(side == 0){
player = new Rectangle(pl.x + updatePlayerPosition, pl.y, pl.getWidth(), pl.getHeight());
} else if(side == 1){
player = new Rectangle(pl.x - updatePlayerPosition, pl.y, pl.getWidth(), pl.getHeight());
} else if(side == 2){
player = new Rectangle(pl.x, pl.y + updatePlayerPosition, pl.getWidth(), pl.getHeight());
} else if(side == 3){
player = new Rectangle(pl.x, pl.y - updatePlayerPosition, pl.getWidth(), pl.getHeight());
}
if(player.intersects(block)){
return true;
}
}
return false;
}
updatePlayerPosition is 2, and changes in my code but enough about that.
In your case, I recommend putting the entities in a List and then checking like I did over.