Java / LWJGL Pong AI issue - java

I am struggling with what must be a basic concept, but can you have a look at my issue?
I have the code where: ai moves the player bat, HEIGHT = total height of Display, and batHeight is the size of the pong paddle/bat:
public void ai(int bally, int HEIGHT, int batHeight) {
if (bally < this.y + ySize / 2) {
if (this.y <= 0) {
System.out.println("Upper Bound");
y = 0;
} else {
y -= 2;
}
}
if (bally > this.y + ySize / 2) {
if (this.y >= HEIGHT - batHeight) {
System.out.println("Lower Bounds");
y = HEIGHT - batHeight;
} else {
y += 2;
}
}
}
The above does exactly what I want it to do. Pong Bat moves up, and when it hits the top of the screen, it prints the console line, and stops the Bat. Exactly the same happens at the bottom of the screen. It prints the console, and stops the bat. It does this every time with no issues.
Now, if I modify the code slightly:
public void ai(int bally, int HEIGHT, int batHeight) {
if (bally < this.y + ySize / 2) {
if (this.y <= 0) {
System.out.println("Upper Bound");
y = 0;
} else {
if(rand.nextInt(2)+1 == 1){
y -= 2;
}else{
y -=3;
}
}
}
if (bally > this.y + ySize / 2) {
if (this.y >= HEIGHT - batHeight) {
System.out.println("Lower Bounds");
y = HEIGHT - batHeight;
} else {
y += 2;
}
}
}
It iterates once, stopping at the top bound, but then it loses itself, and forgets the bounds and the bat moves off the screen. I have Console printing the Bat y position, and it tracks with no issue, accurately displaying its y co-ord, but after the first iteration, it goes to negative y and greater that screen height.
I did have the theory that you cannot nest a IF inside an ELSE statement, so i tried moving it around so that it read:
if(this.y != 0){
if(rand.nextInt(2) + 1 == 1){
//move the paddle at speed 1
} else {
//move paddle at speed 2
}
}else{
//do not move the paddle
}
But that made no difference.
The idea behind the code was to add some chance for the AI bat. Sometimes its fast, and other times it is slower.
Thanks in advance,

Your code from far away looks like this:
for a given time:
if the ball is below the paddle {
if the paddle is below the screen, put it back
else move it down 2 or 3 units
}
if the ball is above the paddle {
if the paddle is above the screen, put it back
else move it up 2 units
}
Imagine the case where the ball is at y = 1 and the paddle is at y = 2. The first if statement will be triggered (1 < 2), the paddle is not outside (2 > 0), so it moves down 2 or 3 units. Let's say 3, for argument's sake. Now, paddle is at y = -1, the ball is still at y = 1. Now, the condition for the second big if is true! So we enter it: the paddle's not above, and we move it up two units. Now, the paddle is at y = 1...
It is clear that it should not have entered the second loop. So, stick an else in front of it, because it should only ever enter one :)

Related

Change Ball Direction after collision with another ball

I need to change Ball Direction after collision with another ball or with a edge of the window.
I managed to do something like that:
y += yMove;
x += xMove;
//if the ball moves to the right edge of the window, turn around.
if(x > width - size)
{
x = width - size;
xMove *= -1;
if (xMove > 0) {
xSpeed = xMove + (Math.random() * (1));
}
if (xMove <= 0) {
xSpeed = xMove - (Math.random() * (1));
}
if (yMove > 0) {
ySpeed = yMove + (Math.random() * (1));
}
if (yMove <= 0) {
ySpeed = yMove - (Math.random() * (1));
}
}
And same for another edges.
I'm trying to use same method for changing direction of balls after they collide with each other, but it's just not working / it's weird. Can anyone help me?
When balls collide, make vector connecting ball centers (N) and normalize it (uN)
Components of velocities parallel to N (normal) are exchanged (due to impulse law)
Components of velocities perpendicular to N (tangential) remain the same
To get components in given local system, use scalar and cross product:
V1t = dot(V1, uN)
V2t = dot(V2, uN)
V1n = cross(V1, uN)
V2n = cross(V2, uN)
after collision
V1t' = V2t
V2t' = V1t
V1n' = V1n
V2n' = V2n
To return into global system (I did not checked signs thoroughly):
V1x = V1t` * uN.X + V2n` * uN.Y
V1y = -V1t` * uN.Y + V2n` * uN.X
(This is essentially dot and cross products again, but I expanded expressions to show different bases)
Note that this approach is like to ball-edge collision, when N is normal to the border and you reverse only one component of velocity vector.
For your BouncingBall class, you can have a method like flipDirection(), but you can have a finer directional control by splitting it into 2 methods which filps the direction of the ball vertically and horizontally.
class BouncingBall{
public void horizontalFlip(){
moveX *= -1;
}
public void verticalFlip(){
moveY *= -1;
}
//To have move control over each direction, you can have a method for each direction.
public void moveNorth(){
moveY = Math.abs(moveY) * -1;
}
public void moveSouth(){
moveY = Math.abs(moveY);
}
public void moveWest(){
moveX = Math.abs(moveX) * -1;
}
public void mpveEast(){
moveX = Math.abs(moveX);
}
}
Depending on how you want the ball to bounce off. In a simple bounce off, the balls can bounce towards 4 possible directions:
North West
North East
South West
South East
The direction of the ball to bounce off will be relative to the position of the ball it is colliding with and you do not want 2 collided balls which move in the same direction to switch direction just because they collided. Hence you need to check the positions of the 2 balls, and flipDirection() becomes insufficinet to achieve that.
if(b1.intersects(b2)){
if(b1.getX() < b2.getX()){ // b1 above b2
b1.moveNorth();
b2.moveSouth();
}
else{
b1.moveSouth();
b2.moveNorth();
}
if(b1.getY() < b2.getY()){ // b1 at left side of b2
b1.moveWest();
b2.moveEast();
}
else{
b1.moveEast();
b2.moveWest();
}
}
For example, to change direction when hitting the walls on the left and right:
if(ball.getPosX() <= 0 || ball.getPosX() >= PNL_WIDTH-Ball.SIZE)
ball.horizontalReverse();
Same logic for verticalReverse.

Reflection circle / rectangle problems

I am trying to recreate breakout but the interaction betweeen the corner of the blocks and the ball is giving me a massive headache. Bouncing off the edges of the window and the blocks works just fine and bouncing off the windows' corners works perfectly too. For simplicity, all blocks are axis-aligned. Swapping x and y movement only works as intended for 2 out of 8 cases. For the other 6, one or two of the vectors have to be inverted for the collision to be correct, but I can't figure out a way to do this in a simple way. Writing 8 if-statements can't be correct and is prone to cause bugs.
The Ball class is an extension of the Ellipse2D.Double class.
The Block class is an extension of the Rectangle class.
Every question / answer / tutorial I took a look at would either be overcomplicated (not AA rects, rect not on a fixed position), simplified (handle as if two rects collided) or completely off-topic (detect collision / overlap, ball to ball handling).
Here's what I got:
public void move(Paddle p, List<Block> b) {
//called every frame
this.x += dx;
this.y += dy;
checkCollisionsWalls();
// checkCollisionPaddle(p);
checkCollisionBlocks(b);
if (this.dir == 90 || this.dir == 270)
this.dir++;
// make sure ball wont get stuck between two walls of window
}
private void checkCollisionBlocks(List<Block> b) {
for (Block block : b) {
if (this.intersects(block)) {
if (this.x + this.width / 2 >= block.x && this.x + this.width / 2 <= block.x + block.width) {
System.out.println("Top / Bot reflect");
this.dy *= -1;
break;
}
if (this.y + this.height / 2 >= block.y && this.y + this.height / 2 <= block.y + block.height) {
System.out.println("Left / Right reflect");
this.dx *= -1;
break;
}
// if execution got here we must have hit a corner
reflectCorner();
}
}
}
private void reflectCorner() {
if (dx > dy) {
this.dy *= -1;
} else {
this.dx *= -1;
}
if (Math.abs(dx) == Math.abs(dy)) {
this.dy *= -1;
this.dx *= -1;
}
double temp = this.dx;
this.dx = this.dy;
this.dy = temp;
temp = 0;
}
dx and dy are global vars in the Ball class, in fact all of the code is (still) in the Ball class.
EDIT:
Sorry for looking over the part of asking a clear question, I'm a little worked up about it...
What is happening with this current code:
Reflection works perfectly when hitting the bottom-right corner from any direction.
Hitting the top left corner results in the ball getting stuck inside the rect. It then wanders along the edges to the bottom-right corner
When touching the top-right corner while moving parallel to the x-axis and when touching the bottom-left corner while moving parallel to the y-axis, the ball reflects twice, inching in either positive y or positive x direction one pixel per bounce (the axis orthogonally it is moving to).
All other reflections work fine.
What should happen:
The reflectCorner() method should work for all corners like it does for the bottom-right one and not produce results as decribed above.
Question:
How do I do that without overcomplicated if-structures?
If necessary, i can provide the testing setup I used.
I finally managed to make it work on all sides. First I calculated which corner was hit by checking if the distance between the ball's center and any corner was smaller / equal to the ball's radius. Then I'd swap the x and y movement. For two corners that's all that was needed, for the second two I had to invert one of them, depending on corner and direction of the ball.
Finally:
private void reflectCorner(Rectangle rect) {
double temp = this.dx;
this.dx = this.dy;
this.dy = temp;
temp = 0;
if (Math.abs(dx) == Math.abs(dy)) {
this.dy *= -1;
this.dx *= -1;
return;
}
if (Math.sqrt(Math.pow(Math.abs(this.getCenterX() - rect.x), 2)
+ Math.pow(Math.abs(this.getCenterY() - rect.y), 2)) <= this.width / 2) {
System.out.println("topL");
if (dx < dy) {
this.dy *= -1;
} else {
this.dx *= -1;
}
return;
}
if (Math.sqrt(Math.pow(Math.abs(this.getCenterX() - (rect.x + rect.width)), 2)
+ Math.pow(Math.abs(this.getCenterY() - (rect.y + rect.height)), 2)) <= this.width / 2) {
System.out.println("botR");
if (dx > dy) {
this.dy *= -1;
} else {
this.dx *= -1;
}
return;
}
}
More i not necissary, and it's relatively compact. Still, if someone has an even simpler idea, you can still answer and I'll test it.

Java - Pixel-Perfect Collision - gap between player and wall

I'm currently working on a Top-Down-Shooter and having some issues with collision.
My world is made of tiles (64x64). The tiles and the entities are rectangles. The player moves with a speed of e.g 2.74 (and not in pixels for smoother movement). But when it comes to the collision between the player (an entity) and a wall i have some issues. To check if there is a collision i take the current position of my player and his movement speed to calculate where his next position would be and if there is any collision. But i check every pixel on the way, so i cant skip an obstacle even if the movement speed is very high. Let's just say the players current position is X:200 Y:200 and he moves 2.74 Pixels a tick in the x direction. My game now checks if there is any collision at X:201 Y:200, X:202 Y:200 or X:202.74 Y:200 and if not moves the player to that position. If I now try to move the player further in the x direction and there is a wall 0.26 Pixels away the player wont move and leave a tiny gap. I tried to calculate the distance between player and wall and add this amount to the players position but for that I need to know which side of the wall the player hits. Also I want the player to be able to move up and down when the wall he hits is in front of him and the other way around.
Here is my collision method (in Java):
public static boolean collision(float ex, float ey, int width, int height) { // ex, ey would be the next position of the player
if (ex < 0 || ex + width > worldWidth || ey < 0 || ey + height > worldHeight) return true; // checks if this position is in the world
int firstTileX = (int) (ex / Tile.TILE_SIZE); // calculates tiles he could possible collide width
int firstTileY = (int) (ey / Tile.TILE_SIZE);
int lastTileX = (int) ((ex + width - 1) / Tile.TILE_SIZE);
int lastTileY = (int) ((ey + height - 1) / Tile.TILE_SIZE);
for (int y = firstTileY; y <= lastTileY; y++) {
if (y < 0) continue; // checks for out of bounds
if (y >= worldTileHeight) break;
for (int x = firstTileX; x <= lastTileX; x++) {
if (x < 0) continue;
if (x >= worldTileWidth) break;
if (tiles[y][x].solid) return true; // if the tile is solid -> collision found
}
}
return false; // no collision found
}
And my movement method:
public void move(float xa, float ya) {
float nx, ny;
while (xa != 0 || ya != 0) {
nx = x;
ny = y;
if (xa != 0) {
if (Math.abs(xa) > 1) { // if the x-speed is greater than 1
nx = x + MathUtil.abs(xa); // returns -1 for negative numbers and 1 for positiv
xa -= MathUtil.abs(xa);
} else { // less than 1
nx = x + xa;
xa = 0;
}
}
if (ya != 0) { // same here
if (Math.abs(ya) > 1) {
ny = y + MathUtil.abs(ya);
ya -= MathUtil.abs(ya);
} else {
ny = y + ya;
ya = 0;
}
}
if (!Level.collision(nx, ny, width, height)) setPosition(nx, ny); // checks if there is an collision and sets the new position if not
else if (!Level.collision(nx, y, width, height)) x = nx; // if there was a collision check if the player can walk in x direction
else if (!Level.collision(x, ny, width, height)) y = ny; // or in y direction
}
}
My problem is the pretty much the same as CoderMusgrove's problem in his post (Pixel-perfect collision and doubles):
Summary & Question
I have a problem where if the speed of an entity isgreater thanthe distance from the tile it is going into, it will leave at least a pixel in between itself and the tile, and I really don't like this. What kind of algorithm could I use that will find the tiniest difference between the entity and the tile?
If you need any additional information, I will be glad to add it.
Thanks for your help!
Easily resolvable by changing your interpretation.
You are retaining a fractional position for the purpose of fine grained speed. Ignore the fraction for the purpose of collision detection and display (if you were to do sub-pixel rendering, do the collision on the subpixel rendering accurarcy level).
int screenX = (int) Math.round(objX);
int screenY = (int) Math.round(objY);
// rendering and collision detection based on rounded position

Collision Detection between player and tiles almost works

I have been stuck on making some collision detection in my game (its kind of like Terraria) for a while but i made this code and... well, it kind of works. It works if the collision is above or on the left of the player, but if the collision is on the right, or below, instead of bouncing back, the player accelerates through the blocks until there is empty space. Here is the code that i made:
private void checkCollision() {
for(int x = (int) (xpos-1); x <= xpos+1; x++){
if(x < 0 || x > main.mw-1) continue;
for(int y = (int) (ypos-2); y <= ypos+1; y++){
if(y < 0 || y > main.mh-1) continue;
if(main.map[x][y] == null) continue;
if(!main.map[x][y].solid) continue;
if(main.map[x][y].blocktype == -1) continue;
double distance = Math.sqrt((xpos-x)*(xpos-x) + (ypos-y)*(ypos-y));
if(distance > 1.0){
continue;
}else{
double x_overlap = Math.max(0, Math.min(xpos + 16, x + 16) - Math.max(xpos, x));
double y_overlap = Math.max(0, Math.min(ypos + 32, y + 16) - Math.max(ypos, y));
double overlapArea = x_overlap * y_overlap;
if(overlapArea > 0){
if(x_overlap > y_overlap){
yblock += y_overlap/2;
}
if(x_overlap < y_overlap){
xblock += x_overlap/2;
}
//guessing i need to do something here to make player
go other way if block is on other side
}
}
}
}
}
So how would i make the player bounce back if the block that he is colliding with is on the right or below. Also is there any way i can make this smoother - right now the player be bouncing all over the place. Thanks! :)
What you want to do is keep track of the player's location, and if the location after moving is out of bounds you can reset the player's position to be right on the edge of the limit.
That's how I dealt with collision detection, I answered another question similar to this one though some folk decided to downvote the answer, go figure.

method is started when launched

So, I have a gameOver() method that should when oval goes out of bounds abort game but, as soon as I start the game it runs the gameOver method. I've been looking over it for a while trying different things. I think what stood out to me is removing the abort sequence the game runs mostly as it should after, popup is closed and that if I replace game.gameOver(); with ya = -1 the ball bounces off the wall.
gaveOver()
public void gameOver(){
JOptionPane.showMessageDialog(this, "Game Over", "Game Over", JOptionPane.YES_NO_OPTION);
System.exit(ABORT);
}
move()
package com.edu4java.minitennis1;
import java.awt.Graphics2D;
import java.awt.Rectangle;
public class Ball {
private static final int DIAMETER = 30;
int x = 0;
int y = 0;
int xa = 1; // Represent the speed in which the ball is moving
int ya = 1; // Represent the speed in which the ball is moving
private Game game;
public Ball(Game game){
this.game=game;
}
/*
* If xa=1, the ball moves to the right, one pixel every round of the Game Loop,
* if xa=-1, the ball moves to the left.
* If ya=1 moves the ball down and
* If ya=-1 moves the ball up.
*/
void move(){
if (x + xa < 0) // Makes left bounds
xa = 3; // Moves the ball right
if (x + xa > game.getWidth() - DIAMETER) // Makes right bounds
xa = -3; // Moves the ball left
if(y + ya < 0) // Makes top bounds
ya = 3; // Moves ball down
if(y + ya > game.getHeight() - DIAMETER) // Makes bottom bounds
game.gameOver();
if (collision()){ // Makes collision with Racquet
ya = -3;
y = game.racquet.getTopY() - DIAMETER;
}
x = x + xa;
y = y + ya;
}
private boolean collision(){
return game.racquet.getBounds().intersects(getBoundsBall());
}
public void paint(Graphics2D g){
g.fillOval(x, y, DIAMETER, DIAMETER);
}
public Rectangle getBoundsBall(){
return new Rectangle(x, y, DIAMETER, DIAMETER);
}
}
The problems has been resolved I just went back and rewrote the move() method and replaced it with
void move(){
if(x + xa < 0)// Makes left bounds
xa = 1;// Moves the ball right
if(x + xa == game.getWidth() - DIAMETER)// Makes right bounds
xa = -1;// Moves the ball left
if (y + ya < 0)// Makes top bounds
ya = 1;// Moves ball down
if(y + ya == game.getHeight() - DIAMETER){ // Makes bottom bounds
ya = 0;// Moves ball up
xa = 0;
game.gameOver();
}
Sometimes all you need to do is go back.
You have answered your question yourself I think. Assuming there are no further calls to your gameOver() method than the one that's in your move() method, than gameOver is called from this if block:
if(y + ya > game.getHeight() - DIAMETER)
game.gameOver();
Which means that (y + ya) is greater than (game.getHeight() - DIAMETER). Since I don't know what your variables are supposed to be I can only guess that there might be a problem with your initial values,etc.
Try stepping through your program with a debugger and it will be much easier to see the problem and understand what's going on.

Categories