So I'm creating a breakout game and everything's going well, except certain collisions with bricks. I don't know how to go about detecting if the ball hits the brick from the left or the right. I'm trying to create an if statement that reads "if the ball hits the brick from the left or the right, xSpeed is multiplied by -1, else if the ball hits the brick from the top or bottom, y is multiplied by -1." This is easy to do, but detecting where the brick is hit has me stumped. I'm using javafx. I'm sure it's easy, I'd just like to figure out how I should start.
Make a collision box around the brick and the ball and compare the position values, something like this:
Rectangle brickCollision = new Rectangle(x, y, w, h);
Rectangle playerCollision = new Rectangle(x, y, w, h);
//example (collision for one side)
if(playerCollision .x + playerCollision.w > brickCollision.x && playerCollision.x < brickCollision.x + brickCollision.w){
System.out.println("Collision");
}
This is not java fx specific:
You have probably a setup for the grid with grid_x and grid_y coordinates to indicated a brick or empty spot.
When collision occurs, you know the grid position of the ball Bl_xy (which has to be an empty spot) and the grid position of the brick Br_xy which was hit.
Now, if the empty spot Bl_xy is above or below of the brick Br_xy, you need to change the vertical speed component for the ball.
If the empty spot Bl_xy is left or right from Br_xy, change the horizontal speed.
I hope this helps on your super secret school project.
Related
if(rect3.a+30>rect2.a){
ballright=false;
bounce++;
}
if(rect3.a-30<rect1.a){
ballright=true;
bounce++;
}
public void paint(Graphics g){
super.paint(g);
Graphics2D g2d=(Graphics2D) g;
g.setColor(Color.BLACK);
g.fillRect(0, 0, 1080, 760);
g2d.setColor(Color.WHITE);
// g.fillOval(x, y, 30, 30);
g2d.fillRect(rect3.a, rect3.b, 30, 30);
g2d.setColor(Color.WHITE);
g2d.fillRect(rect1.a, rect1.b, 30, 200);
g2d.setColor(Color.WHITE);
g2d.fillRect(rect2.a, rect3.b-50, 30, 200);
g2d.fillRect(520, 0, 10, 760);
So I have these rectnagles to be the objects for a pong game. Except I cant seem to figure out how to make the ball (rect3) bounce of the paddles (rect1,2) and if the paddles is not there go to the edge of the window to score a point. The way I have it now it simply bounces of the whole y axis at 0 and 1080. Is there a way to make it bounce only when the paddle is there and if it is not score a point ?
Let's start with the basic objects here:
Left Paddle (left:top:height:width = x1, y1, h, w)
Right Paddle (left:top:height:width = x2, y2, h, w)
Ball (centreXpos:centreYpos:radius = bx, by, r)
Board (left:top:height:width = boardX, boardY, boardH, boardW)
Note:
- width here refers to the short side
- height refers to the length of the long side of rectangle
Now that we have the objects out the way let's identify edges against which you want to detect collision:
1. Left Paddle
- Collision surface would be the long right side
(i.e All pixels from x=(left + width) y=top to x=(left + width) y = (top + height))
2. Right Paddle
- Collision surface would be the long left side
(i.e All pixels from x=(left) y=top to x=(left) y = (top + height))
3. Ball
- Collision surface is all pixels on the edge of the circle
- so bx +/- radius and by +/- radius
4. Board
- Collision surface are the edges of the board
- Long edges should cause you to bounce
- short edges should end the rally
Collision detection
- if collision surface of the ball is >= collision surface of right side paddle
or
- if collision surface of the ball is <= collision surface of left side paddle
What to do when collision is detected?
- if collision is detected with left paddle change direction to move to right (change increments to bx and by to make it take a different direction)
- if collision is detected with left end but not the paddle stop animation
- if collision is detected with right paddle change direction to move to left
- if collision is detected with right end but not the paddle stop animation
Suggestion around Objects
It would help if you modelled your objects and then in your animation loop called collision detector with each surface that you care about
The collision detector could then respond with an enumeration. You can have another function act based on the enumeration
From what I see so far you would need
GameObjects
|-- Ball
|-- Paddle
|-- Left
|-- Right
|-- Board
you can choose to use inheritance.
CollisionDetector
iterate through all Game objects and Detect if ball has collided with any surfaces that interest you
Here you could get both object say, Ball and Left-Paddle and have specific collision routines written for each
(BTW.... with this you can even write test cases around Collision detection and ball movement rather than trying to play it everytime to see if it works as expected)
I believe this will make it simpler. If you have a running copy somewhere on github I am happy to help you with it.
Reading back on the above seems like a long-winded explanation for what you were looking for and something more.
The above does not really depend on Java.. you could do that in almost any language.
Hope this helps :)
you are only checking if ball.x + ball.width > paddle.x
you should also account for the case that ball.y is between paddle.y and paddle.y + paddle.height
I am trying to recreate the snake game, right now i am stuck on adding a new section to the snake everything it collides with an apple
I tried to tell the program to draw a new rectangle but with the x axis slightly shifted and this "shifting" would slowly increase every time my snake eats an apple. My snake starts off with 2 sections, and when i move the snake towards the green apple, it only moves the second section instead of drawing a new one, I know i didn't tell my draw method to draw a new rectangle every time my snake eats an apple since i have no clue on how to do that
public void draw(PApplet p) {
// clear area
p.fill(255, 0, 0);
// draw snake head
p.rect(x, y, length, width);
// draw snake body ?
if (upIsDown == true ) {
p.rect(x, y - section, length, width);
} else if(downIsDown == true) {
p.rect(x, y + section, length, width);
} else if(RightIsDown == true) {
p.rect(x - section, y, length, width);
} else if(LeftIsDown == true) {
RightIsDown = false;
p.rect(x + section, y, length, width);
}
}
the section variable increases by 10 in my checkCollision method
The snake gets longer, but my program only draws the head and the tail of the snake
I added comments to your code.
You code in draw clears the whole game area, then draws 2 rectangles. If you want to see 3 rectanges, you will need to call p.rect() 3 times in that method. If you want to see 10 rectanges, you will need to call it 10 times (so maybe a loop would be good). Else reconsider the call to p.fill() in the beginning.
For a loop, you would need to store the coordinates for each body segment in a structure like a LinkedList, with the coordinates for each segment, and in the draw method draw all segments.
I'm having some difficulty implementing very basic collision within libGDX. The update code for the "player" is as so:
private void updatePosition(float _dt)
{
Vector2 _oldPos = _pos;
_pos.add(_mov.scl(_dt*50));
_bounds.setPosition(_pos.x-8, _pos.y-12);
if(_game.getMap().checkCollision(_bounds))
{
System.out.println("Collision Detected");
_pos = _oldPos;
_bounds.setPosition(_pos.x-8, _pos.y-12);
}
}
So, initially _oldPos is stored as the values of _pos position vector before applying any movement.
Movement is then performed by adding the movement vector _mov (multiplied by the delta-time * 50) to the position vector, the player's bounding rectange _bounds is then updated to this new position.
After this, the player's new bounding Rectangle is checked for intersections against every tile in the game's "map", if an intersection is detected, the player cannot move in that direction, so their position is set to the previous position _oldPos and the bounding rectangle's position is also set to the previous position.
Unfortunately, this doesn't work, the player is able to travel straight through tiles, as seen in this image:
So what is happening here? Am I correct in thinking this code should resolve the detected collision?
What is strange, is that replacing
_pos = _oldPos;
with (making the same move just made in reverse)
_pos.sub(_mov.scl(_dt*50));
Yields very different results, where the player still can travel through solid blocks, but encounters resistance.
This is very confusing, as the following statement should be true:
_oldPos == _pos.sub(_mov.scl(_dt*50));
A better solution for collision detection would be to have a Vector2 velocity, and every frame add velocity to position. You can have a method that tests if Up arrow key is pressed, add 1 (or whatever speed you would like) to velocity. And if down is pressed, subtract 1 (or whatever speed). Then you can have 4 collision rectangles on player, 1 on top of player, bottom, left, and right. You can say
if(top collides with bounds){
if(velocity.y > 0){
set velocity.y = 0.
}
}
And do the same for down, left and right (eg... for bottom, make sure to test if(velocity.y < 0) instead of if(velocity.y > 0).
EDIT: You code is not working because you set oldPos = pos without instantiating a new Vector2. Which means when you add onto pos, it also changes oldPos. So say oldPos = new Vector2(pos);
try to test future position before move. If collision, don't move.
I've got a ball that I can move around on a map consisting of equally sized tiles. The player should not be able to walk over the tiles that are darker and have a black border. I've got a multidimensional array of the tiles that I use to check which tiles are solid.
I would like the player to slide against the wall if he is moving both horizontally and vertically into it. The problem is that if he does that he sticks to the wall. I managed to get it working perfectly on each axis, but separately. Here is my code for the horizontal collision checking:
if (vx < 0) {
// checks for solid tiles left of the player
if (level.isBlocked(i, j) || level.isBlocked(i, jj)) {
x = side * (i + 1); // moves player to left side of tile
vx = 0;
}
} else if (vx > 0) {
// checks for solid tiles right of the player
if (level.isBlocked(ii, j) || level.isBlocked(ii, jj)) {
x = (ii * side) - getWidth(); // moves player to right side of tile
vx = 0;
}
}
The level.isBlocked() method checks if that index of the array is occupied by a solid tile. The i and j variables is which index in the array the player's top right corner is located on. The ii and jj variables is which index in the array the player's bottom right corner is located on.
This works fine, but then if I add the same chunk of code beneath but replacing x with y, vx with vy and so on the problem occurs. So I can add either the horizontal or vertical collision handling and it works, but not at the same time. I've seen a few articles explaining I have to separate them or something, but I didn't understand much of them. How can I check collision on both axes and keep the sliding effect?
I finally got it to work. Angelatlarge's answer was helpful in understanding the problem, but I decided to start from scratch. I ended up first calculating the new x and y position and storing them in separate variables. Then I checked the tile under the middle left of the player and the same with the middle right. I then set a boolean to true if the player was standing on a tile because of his horizontal speed. If there was no collision I set the real x variable to the new one I calculated earlier. I then repeated the same thing for the vertical collision.
This is for the horizontal checking:
float newX = x + vx * delta;
boolean xCollision = false;
if (vx < 0) {
int i = level.toIndex(x);
int j = level.toIndex(y + getHeight() / 2);
xCollision = level.isBlocked(i, j);
} else if (vx > 0) {
int i = level.toIndex(x + getWidth());
int j = level.toIndex(y + getHeight() / 2);
xCollision = level.isBlocked(i, j);
}
if (!xCollision) x = newX;
The problem is that with the setup you have, given a block and the player position, and also given the fact that they overlap, you don't know whether the player collided with a vertical or a horizontal wall of the block. So see this more clearly consider the following block and two collision paths
The top path will collide with the left wall, and requires a vx=0; (cessation of horizontal movement), while the bottom path collides with the bottom wall and will require vy=0;, or stopping of the vertical movement.
I think in order to do the kind of collision detection you want, you will want to compute intersections of the player path and the walls of the blocks, not just checking whether the player overlaps a block. You could hack the desired behavior by computing the overlapping rectange of the player rectangle and the block rectangle. Consider the following situation:
where the red seqare represents your player. The fact that the overlap rectangle (the small rectangle occupied where the player is on top of the block) is more wide than it is tall suggests that it was the vertical collision that happened, not a horizontal. This is not foolproof, however. And it still requires you to be able to access the shape of the block, rather than just stesting if a part of the player rectangle overlaps a block.
So i've made my own FPS, graphics and guns and all of that cool stuff; When we fire, the bullet should take a random direction inside the crosshair, as defined by:
float randomX=(float)Math.random()*(0.08f*guns[currentWeapon].currAcc)-(0.04f*guns[currentWeapon].currAcc);
float randomY=(float)Math.random()*(0.08f*guns[currentWeapon].currAcc)-(0.04f*guns[currentWeapon].currAcc);
bulletList.add(new Bullet(new float[]{playerXpos, playerYpos, playerZpos}, new float[]{playerXrot+randomX, playerYrot+randomY}, (float) 0.5));
We calculate the randomness in X and Y (say you had a crosshair size (guns[currentWeapon].currAcc) of 10, then the bullet could go 0.4 to any side and it would remain inside the crosshair.
After that is calculated, we send the player position as the starting position of the bullet, along with the direction it's meant to take (its the player's direction with that extra randomness), and finally it's speed (not important atm, tho).
Now, each frame, the bullets have to move, so for each bullet we call:
position[0] -= (float)Math.sin(direction[1]*piover180) * (float)Math.cos(direction[0]*piover180) * speed;
position[2] -= (float)Math.cos(direction[1]*piover180) * (float)Math.cos(direction[0]*piover180) * speed;
position[1] += (float)Math.sin(direction[0]*piover180) * speed;
So, for X and Z positions, the bullet moves according to the player's rotation on the Y and X axis (say you were looking horizontally into Z, 0 degrees on X and 0 on Y; X would move 0*1*speed and Z would move 1*1*speed).
For Y position, the bullet moves according to the rotation on X axis (varies between -90 and 90), meaning it stays at the same height if the player's looking horizontally or moves completely up if the player is looking vertically.
Now, the problem stands as follows:
If i shoot horizontally, everything works beautifully. Bullets spread around the cross hair, as seen in https://dl.dropbox.com/u/16387578/horiz.jpg
The thing is, if i start looking up, the bullets start concentrating around the center, and make this vertical line the further into the sky i look.
https://dl.dropbox.com/u/16387578/verti.jpg
The 1st image is around 40ยบ in the X axis, the 2nd is a little higher and the last is when i look vertically.
What am i doing wrong here? I designed this solution myself can im pretty sure im messing up somewhere, but i cant figure it out :/
Basicly the vertical offset calculation (float)Math.cos(direction[0]*piover180) was messing up the X and Z movement because they'd both get reduced to 0. The bullets would make a vertical line because they'd rotate on the X axis with the randomness. My solution was to add the randomness after that vertical offset calculation, so they still go left and right and up and down after you fire them.
I also had to add an extra random value otherwise you'd just draw a diagonal line or a cross.
float randomX=(float)Math.random()*(0.08f*guns[currentWeapon].currAcc)-(0.04f*guns[currentWeapon].currAcc);
float randomY=(float)Math.random()*(0.08f*guns[currentWeapon].currAcc)-(0.04f*guns[currentWeapon].currAcc);
float randomZ=(float)Math.random()*(0.08f*guns[currentWeapon].currAcc)-(0.04f*guns[currentWeapon].currAcc);
bulletList.add(new Bullet(new float[]{playerXpos, playerYpos, playerZpos}, new float[]{playerXrot, playerYrot}, new float[]{randomX,randomY, randomZ},(float) 0.5));
And the moving code...
vector[0]= -((float)Math.sin(dir[1]*piover180) * (float)Math.cos(dir[0]*piover180)+(float)Math.sin(random[1]*piover180)) * speed;
vector[1]= ((float)Math.sin(dir[0]*piover180)+(float)Math.sin(random[0]*piover180)) * speed;
vector[2]= -((float)Math.cos(dir[1]*piover180) * (float)Math.cos(dir[0]*piover180)+(float)Math.sin(random[2]*piover180)) * speed;
You didn't need to bust out any complex math, your problem was that when you were rotating the bullet around the y axis for gun spread, if you were looking directly up (that is, through the y axis, the bullet is being rotated around the path which its going, which means no rotation whatsoever (imagine the difference between sticking your arm out forwards towards a wall and spinning in a circle, and sticking you arm out towards the sky and spinning in a circle. Notice that your hand doesn't move at all when pointed towards the sky (the y-axis)) and so you get those "diagonal" bullet spreads.
The trick is to do the bullet spread before rotating by the direction the player is looking in, because that way you know that when you are rotating for spread, that the vector is guaranteed to be perpendicular to the x and y axes.
this.vel = new THREE.Vector3(0,0,-1);
var angle = Math.random() * Math.PI * 2;
var mag = Math.random() * this.gun.accuracy;
this.spread = new THREE.Vector2(Math.cos(angle) * mag,Math.sin(angle) * mag);
this.vel.applyAxisAngle(new THREE.Vector3(0,1,0),this.spread.y / 100); //rotate first when angle gaurenteed to be perpendicular to x and y axes
this.vel.applyAxisAngle(new THREE.Vector3(1,0,0),this.spread.x / 100);
this.vel.applyAxisAngle(new THREE.Vector3(1,0,0),player.looking.x); //then add player looking direction
this.vel.applyAxisAngle(new THREE.Vector3(0,1,0),player.looking.y);
this.offset = this.vel.clone()
I don't use java but I hope you get the main idea of what im doing by this javascript. I am rotating a vector going in the negative z direction (default direction of camera) by the spread.y, and spread.x, and then I am rotating by the pitch and yaw of the angle at which the player is facing.