I'm trying to create a Roomba program with a ball that bounces around the screen that cleans the tiles that it passes over. The program should start with all grey tiles and when the ball passes over them then the tiles turn white. Currently I have the ball that bounces around everywhere and a grid method which creates a 5x5 grid.
I have encountered two problems:
I cannot make the grid and the ball appear in the same simulation while running the program, it's either one or the other.
I'm having trouble with finding a way to analyze if the ball has passed over certain squares in the grid, perhaps I need to create an object for the grid/ball?
My code:
import edu.princeton.cs.introcs.StdDraw;
public class Roomba {
private static int windowWidth = 200;
private static int windowHeight = 200;
private static double x = 100;
private static double y = 100;
private static double vx = 2;
private static double vy = 4;
private static int radius = 5;
private static boolean inGame = true;
public static void updateLocations() {
x += vx;
y += vy;
}
public static void drawGrid() {
StdDraw.setScale(0, 5);
int[][] grid = new int[5][5];
for (int x = 0; x < grid.length; x++) {
for (int y = 0; y < grid.length; y++) {
grid[x][y] = 255;
}
}
for (int x = 0; x < grid.length; x++) {
for (int y = 0; y < grid.length; y++) {
StdDraw.square(x, y, 1);
}
}
}
public static void updateVelocities() {
if (y + radius >= windowHeight) {
vy = -vy;
} else if (y - radius <= 0) {
vy = -vy;
}
if (x >= 194 || x <= 6) {
vx = -vx;
}
}
public static void setBackground() {
StdDraw.clear(StdDraw.GRAY);
// drawGrid();
}
public static void drawBall() {
StdDraw.setPenColor(StdDraw.BLACK);
StdDraw.filledCircle(x, y, radius);
// StdDraw.setPenColor(StdDraw.RED);
// StdDraw.filledSquare(x + 3, y + 3, 1);
StdDraw.setPenColor(StdDraw.BLACK);
StdDraw.text(100, 70, "x is: " + x + " y is: " + y);
}
public static void draw() {
setBackground();
drawBall();
}
public static void main(String[] args) {
StdDraw.setCanvasSize(800, 800);
StdDraw.setXscale(0, windowWidth);
StdDraw.setYscale(0, windowHeight);
while (true) {
if (inGame) {
draw();
updateVelocities();
updateLocations();
} else {
StdDraw.text(100, 100, "Game Over");
}
// change to if all tiles have been cleaned
// if (x + radius > windowWidth || x - radius < 0) {
// inGame = false;
// }
StdDraw.show(20);
}
}
}
Maybe the background is being drawn over the ball or vice verse? Try to draw the ball first then the background? Make sure the ball is actually being rendered at the correct size and not as the entire screen. I'm not to familiar with 2D graphics but maybe there is some z fighting?
Related
I'm trying to make a minesweeper clone using Processing's java. I have managed to create a grid and each tile has a randomized value assigned to it, but for some reason, only the top left tile can be clicked, the rest of the tiles do not return a value. If anyone knows whats wrong with my code or what I could try that would be great, thanks
Here's my Java code:
int realWidth = 500;
int realHeight = 500;
int tilemount = 0;
boolean mousePress = false;
//calls class
Tile[] tile = new Tile[100];
float tileSize = realWidth/10;
//sets size of window
void settings() {
size(realWidth, realHeight);
}
//Draws 100 tiles on the grid and assignemts each with a value from -1 - 9
void setup() {
int x = 0;
int y = 0;
for (int i = 0; i < tile.length; i++) {
tile[i] = new Tile(x, y, int(random(-1,9)), tilemount);
x += tileSize;
if (x > realWidth-tileSize) {
x = 0;
y += tileSize;
}
tilemount ++;
}
print("done");
}
//updates each tile in the list
void draw() {
background(50);
for (int i = 0; i < tile.length; i++) {
tile[i].display();
tile[i].tileClicked();
}
//checks if tiles are clicked
clickCheck();
}
//sets up tile class
class Tile {
int x;
int y;
int value;
int tilemount;
Tile(int x, int y, int value, int tilemount) {
this.x = x;
this.y = y;
this.value = value;
this.tilemount = tilemount;
}
//positions the tile based off of display values
void display() {
rect(x, y, tileSize, tileSize);
}
//the problem:
/*if tile is clicked, it should run through all 100 tiles in the list,
detect if the mouse is inside the x value assinged to each tile, and produce the
value of the tile the mouse is currently inside */
void tileClicked() {
if (mousePressed && mousePress == false) {
mousePress = true;
for (int i = 0; i < tile.length; i++) {
//println(tile[i].x, tile[i].y);
//println(tile[2].x, tile[2].y, mouseX, mouseY);
if (mouseX > tile[i].x && mouseX < tileSize && mouseY > tile[i].y && mouseY < tileSize) {
println(i, tile[i].value);
break;
}
}
}
}
}
//Checks if mouse is clicked
void clickCheck() {
if (!mousePressed) {
mousePress = false;
}
}
There is a bit of confusion over classes/objects and global variables in your code.
I say that for a few reasons:
boolean mousePress = false; is a global variable, accessible throughout the sketch, however you are accessing it and changing it from each Tile instance, resetting it's value: you probably meant to use a mousePress property for Tile. This way, each Tile has it's mousePress without interfering with one another.
in tileClicked() you're itereratting through all the tiles, from Tile which means unecessarily doing the loop for each time: each tile can check it's own coordinates and position to know if it's being clicked or not (no need for the loop)
speaking of checking bounds, checking if mouseX < tileSize and mouseY < tileSize will only check for the top left tile: you probably meant mouseX < x + tileSize and mouseY < y + tileSize
Here's a version of your sketch with the above notes applied
(and optional debug text rendering to double check the printed value against the tile):
int realWidth = 500;
int realHeight = 500;
int tilemount = 0;
//calls class
Tile[] tile = new Tile[100];
float tileSize = realWidth/10;
//sets size of window
void settings() {
size(realWidth, realHeight);
}
//Draws 100 tiles on the grid and assignemts each with a value from -1 - 9
void setup() {
int x = 0;
int y = 0;
for (int i = 0; i < tile.length; i++) {
tile[i] = new Tile(x, y, int(random(-1,9)), tilemount);
x += tileSize;
if (x > realWidth-tileSize) {
x = 0;
y += tileSize;
}
tilemount ++;
}
println("done");
}
//updates each tile in the list
void draw() {
background(50);
for (int i = 0; i < tile.length; i++) {
tile[i].display();
tile[i].tileClicked();
//checks if tiles are clicked
tile[i].clickCheck();
}
}
//sets up tile class
class Tile {
int x;
int y;
int value;
int tilemount;
boolean mousePress = false;
Tile(int x, int y, int value, int tilemount) {
this.x = x;
this.y = y;
this.value = value;
this.tilemount = tilemount;
}
//positions the tile based off of display values
void display() {
fill(255);
rect(x, y, tileSize, tileSize);
fill(0);
text(value,x + tileSize * 0.5, y + tileSize * 0.5);
}
//the problem:
/*if tile is clicked, it should run through all 100 tiles in the list,
detect if the mouse is inside the x value assinged to each tile, and produce the
value of the tile the mouse is currently inside */
void tileClicked() {
if (mousePressed && mousePress == false) {
mousePress = true;
//println(tile[2].x, tile[2].y, mouseX, mouseY);
if (mouseX > x && mouseX < x + tileSize && mouseY > y && mouseY < y + tileSize) {
println(value);
}
}
}
//Checks if mouse is clicked
void clickCheck() {
if (!mousePressed) {
mousePress = false;
}
}
}
I understand the intent of the mousePress variable, but I'd like to mousePressed() function which you can use to avoid de-bouncing/resetting this value and simplify code a bit.
Here's a version of the above sketch using mousePressed() (and renaming variables a touch to be in line with Java naming conventions):
int realWidth = 500;
int realHeight = 500;
int tileCount = 0;
//calls class
Tile[] tiles = new Tile[100];
float tileSize = realWidth/10;
//sets size of window
void settings() {
size(realWidth, realHeight);
}
//Draws 100 tiles on the grid and assignemts each with a value from -1 - 9
void setup() {
int x = 0;
int y = 0;
for (int i = 0; i < tiles.length; i++) {
tiles[i] = new Tile(x, y, int(random(-1,9)), tileCount);
x += tileSize;
if (x > realWidth-tileSize) {
x = 0;
y += tileSize;
}
tileCount ++;
}
println("done");
}
//updates each tile in the list
void draw() {
background(50);
for (int i = 0; i < tiles.length; i++) {
tiles[i].display();
}
}
void mousePressed(){
for (int i = 0; i < tiles.length; i++) {
tiles[i].click();
}
}
//sets up tile class
class Tile {
int x;
int y;
int value;
int index;
Tile(int x, int y, int value, int tileCount) {
this.x = x;
this.y = y;
this.value = value;
this.index = tileCount;
}
//positions the tile based off of display values
void display() {
fill(255);
rect(x, y, tileSize, tileSize);
// optional: display the tile value for debugging purposes
fill(0);
text(value, x + tileSize * 0.5, y + tileSize * 0.5);
}
//the problem:
/*if tile is clicked, it should detect if the mouse is inside the
value assinged to each tile, and produce the
value of the tile the mouse is currently inside */
void click() {
if (mouseX > x && mouseX < x + tileSize && mouseY > y && mouseY < y + tileSize) {
println("index", index, "value", value);
}
}
}
Good start though: keep going!
The rest of the code is good, congrats on using a 1D array for a 2D grid layout (and the logic for resetting the x position on each row). Have fun learning !
I think you did a good job naming functions and classes and what not. From this point, I would recommend breaking your code down so that you can make sure that at least 3 tiles function fully before allowing the tilelength to be something like 100 (for 100 tiles).
so from what I can see:
+ You have 3 apostrophes at the end of your code that need to be removed.
+ you use tile.length but never defined a length variable in your class, so when your for loop runs it has no number to use to determine it's number of cycles.
+ Please double check your logic in the tileClicked function with the help of a truth/logic table: https://en.wikipedia.org/wiki/Truth_table . I didn't look too deeply into this but this looks like an area where one misunderstanding could throw ;
Try adding this.length = 3; in your tile class and then re-run your code to see if more tiles pop up.
And it might be good to use some system.out.println calls to print conditional values out so that you can see the intermeediate results and compare them to what you expect to happen. Just some basic debugging.
Good luck.
I'm working on a project where we are supposed to have rectangles of random sizes bounce off a wall and change color each time they bounce.
When you click on them, they are supposed to freeze in place and turn red. I'm just having trouble having them stop and for some reason they slow when one is clicked.
import java.util.Random;
public class Main {
public static void main(String[] args) {
MovingRectangles[] rectangles = new MovingRectangles[5];
Random rng = new Random();
for (int i = 0; i < rectangles.length; i++) {
rectangles[i] = new MovingRectangles(rng.nextDouble(), rng.nextDouble(), rng.nextDouble(), rng.nextDouble(), rng.nextDouble(), rng.nextDouble());
}
for (int g = 0; g < rectangles.length; g++) {
rectangles[g] = new MovingRectangles(
rng.nextDouble(),
rng.nextDouble(),
rng.nextDouble() / 50 - 0.01,
rng.nextDouble() / 50 - 0.01,
rng.nextDouble() * 0.04 + 0.03,
rng.nextDouble() * 0.04 + 0.03
);
}
while (true) {
StdDraw.clear();
for (int h = 0; h < rectangles.length; h++) {
rectangles[h].draw();
rectangles[h].update();
}
int count = 0;
for (int i =0; i < rectangles.length; i++) {
rectangles[i].draw();
if (StdDraw.mousePressed() && rectangles[i].containsPoint(StdDraw.mouseX(), StdDraw.mouseY())) {
rectangles[i].freeze();
}
if (rectangles[i].isFrozen()) {
count++;
StdDraw.show(25);
}
}
}
}}
This is the class for moving rectangles. Stackoverflow says I need to add context to explain what this code is.
import java.util.Random;
public class MovingRectangles {
private double x;
private double y;
private double vx;
private double vy;
private double hx;
private double hy;
private boolean isFrozen;
private int red;
private int green;
private int blue;
public MovingRectangles(double x, double y, double vx, double vy, double hx, double hy) {
this.x = x;
this.y = y;
this.vx = vx;
this.vy = vy;
isFrozen = false;
this.hx = hx;
this.hy = hy;
randomColor();
}
public void randomColor() {
Random rng = new Random();
red = rng.nextInt(256);
blue = rng.nextInt(256);
green = rng.nextInt(256);
}
public void draw() {
if (isFrozen) {
StdDraw.setPenColor(StdDraw.RED);
} else {
StdDraw.setPenColor(red, green, blue);
}
StdDraw.filledRectangle(x, y, hx, hy);
}
public void update() {
x += vx;
y += vy;
if (x - hx < 0) {
vx *= -1;
x = 0 + hx;
randomColor();
}
if (x + hx > 1) {
vx *= -1;
x = 1 - hx;
randomColor();
}
if (y - hy < 0) {
vy *= -1;
y = 0 + hy;
randomColor();
}
if (y + hy > 1) {
vy *= -1;
y = 1 - hy;
randomColor();
}
}
public void freeze() {
isFrozen = true;
}
public boolean isFrozen() {
return isFrozen;
}
public boolean containsPoint(double a, double b) {
return
a > x - hx &&
a < x + hx &&
b > y - hy &&
b < y + hy;
}
}
The only other thing I need to add is for it to print "You Win" when all five of the boxes have been clicked. thanks for any help.
My thought is that you're not stopping the actual update of the rectangle.
In your MovingRectangles class...
public void update() {
if(!this.isFrozen) {
{...your code...}
}
}
add at the very beginning of update method line like
if(isFrozen) return;
it should stop your rectangle.
the another way (if you don't want to touch rectangle class).
after
for (int h = 0; h < rectangles.length; h++) {
rectangles[h].draw();
add
if(!rectangles[h].isFrozen()) rectangles[h].update();
This update to draw fixed the problem I was having.
public void draw() {
if (isFrozen) {
StdDraw.setPenColor(StdDraw.RED);
StdDraw.filledRectangle(x, y, hx, hy);
vx = 0;
vy = 0;
} else {
StdDraw.setPenColor(red, green, blue);
}
StdDraw.filledRectangle(x, y, hx, hy);
}
anything i do seems to break down my code into unusable rubbish. Note that it is also a mess... but at least it does work almost properly.
Here are some details:
it's sort of a particle system but with images. koffer stands for bag and it has to be a sort of eplosion of rotating bags that get slightly bigger or smaller for the depth effect and they fade away in time and after leaving the screen it should be deleted.
For now i got half of the bags that explode to turn random ways and half of the bags don't do that. They all fade but don't get deleted after leaving the screen. there is no slight resize yet. But most of all i have to use an arraylist() and don't know how to implement that.
here is my code:
Main Class BagEplode:
PImage coffre;
float life = 255;
int i = 0;
Ball [] balls = new Ball [200];
void setup(){
size (600,600,P3D);
noStroke();
smooth();
coffre = loadImage("coffre.png");
for (i = 0; i < balls.length; i ++)
{
balls[i] = new Ball();
}
}
void draw()
{
clear();
life -= 2.5;
for (int i = 0; i < balls.length; i++)
{
if(i <= 4)
{
balls[i].kleur();
image(coffre, balls[i].x, balls[i].y);
tint(255, life);
balls[i].update();
}
//else
//{
//}
}
}
and i got the Class Ball:
class Ball
{
public void kleur(){
}
float x = width/2;
float y = 601;
float vX = random(1,4);
float vY = random(-9,-5);
float sizeX = 0 ;
float sizeY = 0 ;
float draaiHoek = 0;
float angle = random(-3,3);
public void update(){
x += vX ;
y += vY ;
vY += 0.125;
sizeX = 64 ;
sizeY = 64 ;
draaiHoek -=angle;
imageMode(CENTER);
pushMatrix();
translate(x,y);
rotate((radians(draaiHoek)));
image(coffre, 0, 0);
imageMode(CORNER);
popMatrix();
//if (x > width || x < 0 || y > height || y < 0)
//{
//}
}
}
Thanx for helping me in advance. I hope i really can get some pointers.
From my understanding, you need to replace balls array of type- Ball with an ArrayList of type Ball
import java.util.ArrayList;
public class Ball {
PImage coffre;
float life = 255;
int i = 0;
//Ball [] balls = new Ball [200];
ArrayList<Ball> balls= new ArrayList<Ball>();
void setup(){
size (600,600,P3D);
noStroke();
smooth();
coffre = loadImage("coffre.png");
/* for (i = 0; i < balls.length; i ++)
{
balls[i] = new Ball();
}
*/
for(int i=0;i<balls.size();i++)
{
balls.add(new Ball());
}
}
void draw()
{
clear();
life -= 2.5;
/* for (int i = 0; i < balls.length; i++)
{
if(i <= 4)
{
balls[i].kleur();
image(coffre, balls[i].x, balls[i].y);
tint(255, life);
balls[i].update();
}
*/
for (int i = 0; i < balls.size(); i++)
{
if(i <= 4)
{
balls.get(i).kleur();
image(coffre, balls.get(i).x, balls.get(i).y);
tint(255, life);
balls.get(i).update();
}
//else
//{
//}
}
}
}
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
I have been working on a Breakout game and have just about everything done except for the brick collision. The ball bounces of the wall and paddle fine, but when it comes to the brick it goes straight through them. I'm pretty sure the problem is in the checkBrick() part of the main class, but have no idea what to do about it.
Main Class:
import java.awt.*;
import java.awt.event.KeyEvent;
import java.applet.*;
import java.util.Random;
import javax.swing.JOptionPane;
public class Breakout extends Applet implements Runnable {
Ball ball = new Ball();
Paddle paddle = new Paddle(135, 375);
Brick[] brick = new Brick[50];
private int bX[] = new int[50];
private int bY[] = new int[50];
private int bW[] = new int[50];
private int bH[] = new int[50];
Thread t;
Random trajectory = new Random();
boolean lose;
Image buffer = null;
// The life cycle of the Applet
// Sets up window
public void init() {
setSize(377, 500);
buffer = createImage(377, 500);
// setBackground(Color.black);
System.out.println("init()");
}
public void start() {
if (t == null) {
t = new Thread(this);
t.start();
}
System.out.println("start()");
}
public void run() {
System.out.println("run()");
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
while (!lose) {
ball.move();
paddle.move();
checkWall();
checkPaddle();
checkBrick();
ball.move();
repaint();
try {
Thread.sleep(30);
} catch (InterruptedException ex) {
}
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
}
JOptionPane.showMessageDialog(null, "Game Over");
System.out.println("Termintated");
System.exit(0);
}
public void stop() {
System.out.println("stop()");
}
public void destroy() {
System.out.println("destroy()");
}
public void paint(Graphics g) {
Graphics screen = null;
screen = g;
g = buffer.getGraphics();
g.setColor(Color.black);
g.fillRect(0, 0, 377, 500);
createBricks(g);
createPaddle(g);
createBall(g);
screen.drawImage(buffer, 0, 0, this);
}
public void update(Graphics g) {
paint(g);
}
private void createBricks(Graphics g) {
int brickIndex = 0;
int brickX = 15, brickY = 160;
int brickW = 30, brickH = 10;
for (int i = 0; i <= 4; i++) {
brickX = 15;
brickY -= 20;
for (int n = 0; n < 10; n++) {
brick[brickIndex] = new Brick();
brick[brickIndex].setBounds(brickX, brickY, brickW, brickH);
bX[brickIndex] = brick[brickIndex].x();
bY[brickIndex] = brick[brickIndex].y();
bW[brickIndex] = brick[brickIndex].w();
bH[brickIndex] = brick[brickIndex].h();
brick[brickIndex].setColor(i);
brick[brickIndex].paint(g);
brickIndex++;
brickX += 35;
}
}
}
private void createPaddle(Graphics g) {
paddle.paint(g);
}
private void createBall(Graphics g) {
ball.paint(g);
}
private void checkWall() {
// If ball hits right wall it will bounce
if ((ball.getX() + ball.getR()) >= 380) {
ball.setVX(trajectory.nextInt(2) + -3);
}
// If ball hits left wall it will bounce
if ((ball.getX() - ball.getR()) < -10) {
ball.setVX(trajectory.nextInt(4) + 1);
}
// If ball hits ceiling it will bounce
if ((ball.getY() + ball.getR()) < 12)
ball.setVY(trajectory.nextInt(5) + 1);
// If ball goes through floor it will subtract a life
if ((ball.getY() + ball.getR()) > 515)
lose = true;
}
private void checkBrick() {
for (int i = 0; i < 50; i++) {
int tempX, tempY, tempW, tempH;
tempX = bX[i];
tempY = bY[i];
tempW = bW[i];
tempH = bH[i];
if ((ball.getX() + ball.getR()) < (tempX + tempW)
&& (ball.getX() + ball.getR()) >= tempX) {
if ((ball.getY() + ball.getR()) > (tempY + tempH)
&& (ball.getY() + ball.getR()) <= tempY) {
System.out.println("Brick " + i + " has been hit.");
}
}
}
}
private void checkPaddle() {
// Check for paddle
if ((ball.getX() + ball.getR()) < (paddle.getX() + 100)
&& (ball.getX() + ball.getR()) >= paddle.getX() + 5) {
if ((ball.getY() + ball.getR()) > (paddle.getY() - 5)
&& (ball.getY() + ball.getR()) <= (paddle.getY() + 5)) {
ball.setVX((trajectory.nextInt(7) + -2) + 1);
ball.setVY(trajectory.nextInt(1) + -3);
}
}
}
// Key Detectors
public boolean keyDown(Event e, int key) {
if (key == Event.RIGHT) {
paddle.setVX(0);
if ((paddle.getX() + 100) < 377)
paddle.setVX(10);
}
if (key == Event.LEFT) {
paddle.setVX(0);
if (paddle.getX() > 0)
paddle.setVX(-10);
}
return true;
}
// To make sure it doesn't just keep moving one way
public boolean keyUp(Event e, int key) {
paddle.setVX(0);
return true;
}
}
Ball Class:
import java.awt.Color;
import java.awt.Graphics;
import java.util.Random;
public class Ball
{
private int x, y; //Position
private int vx, vy; //Velocity
private int r; //radius
//constructor
public Ball()
{
x = 177;
y = 315;
vx = 0;
vy = 5;
r = 15;
}
public void paint(Graphics g)
{
g.setColor(Color.white);
g.fillOval(x, y, r, r);
}
//returns the x of origin
public int getX()
{
return x;
}
//returns the y of origin
public int getY()
{
return y;
}
public int getVX()
{
return vx;
}
//returns the y of origin
public int getVY()
{
return vy;
}
//returns the radius r of the ball
public int getR()
{
return r;
}
//sets the velocity of x to a different value
public void setVX(int vx)
{
this.vx = vx;
}
//sets the velocity of y to a different value
public void setVY(int vy)
{
this.vy = vy;
}
//sets the x value
public void setX(int x)
{
this.x = x;
}
//sets the y value
public void setY(int y)
{
this.y = y;
}
//starts making the ball move by changing its coords
public void move()
{
x+= vx;
y+= vy;
}
}
Paddle Class:
import java.awt.Color;
import java.awt.Graphics;
public class Paddle {
// declares variables for x and y coordinates
int x, y;
//The velocity of to move paddle
int vx;
// constructor that takes in x and y coordinates for paddle
public Paddle(int x, int y)
{
this.x = x;
this.y = y;
}
public void paint(Graphics g)
{
// paints paddle
g.setColor(Color.WHITE);
g.fillRect(x, y, 100, 15);
g.setColor(Color.GREEN);
g.drawRect(x, y, 100, 15);
}
// gets x coordinate of paddle
public int getX() {
return x;
}
// sets x coordinate of paddle
public void setX(int x) {
this.x = x;
}
// gets y coordinate of paddle
public int getY() {
return y;
}
// sets y coordinate of paddle
public void setY(int y) {
this.y = y;
}
public void setVX(int vx)
{
this.vx = vx;
}
//Moves the paddle
public void move()
{
x+=vx;
}
}
Brick Class:
import java.awt.Color;
import java.awt.Graphics;
public class Brick
{
private Color color =(Color.cyan);
private int x, y, w, h;
public Brick()
{
//Garbage values that are there just for declaration
x = 0;
y = 0;
w = 10;
h = 10;
}
//Sets color for the brick
public void setColor(int paintC)
{
switch(paintC)
{
case 0:
color = (Color.magenta);
break;
case 1:
color = (Color.blue);
break;
case 2:
color = (Color.yellow);
break;
case 3:
color = (Color.orange);
break;
default:
color = (Color.red);
break;
}
}
//Sets the location then size of the brick
public void setBounds(int x, int y, int w, int h)
{
this.x = x;
this.y = y;
this.w = w;
this.h = h;
}
//returns x value
public int x()
{
return this.x;
}
//returns y value
public int y()
{
return this.y;
}
//returns width value
public int w()
{
return this.w;
}
//returns height value
public int h()
{
return this.h;
}
//Sets x for the brick
public void setX(int x)
{
this.x = x;
}
//Sets y for the brick
public void setY(int y)
{
this.y = y;
}
public void setW(int w)
{
this.w = w;
}
public void setH(int h)
{
this.h = h;
}
public void paint(Graphics g)
{
g.setColor(color);
g.fillRect(x, y, w, h);
g.setColor(Color.green);
g.drawRect(x, y, w, h);
}
}
I've begin running over your code, quite frankly can't be bothered trying to figure out your logic, but what I believe you're trying to deduce is if the brick "contains" the ball, rather then if the ball intersects with the brick.
You don't care how much of the ball or brick are intersecting, only if the they do...for example...
private void checkBrick() {
int tx = ball.getX();
int ty = ball.getY();
int tw = ball.getR();
int th = ball.getR();
tw += tx;
th += ty;
for (int i = 0; i < 50; i++) {
int tempX, tempY, tempW, tempH;
tempX = bX[i];
tempY = bY[i];
tempW = bW[i];
tempH = bH[i];
int rw = tempW + tempX;
int rh = tempH + tempY;
// overflow || intersect
if ((rw < tempX || rw > tx) &&
(rh < tempY || rh > ty) &&
(tw < tx || tw > tempX) &&
(th < ty || th > tempY)) {
System.out.println("Hit");
}
}
}
Now, I stole this from Rectangle#intersects
Basically, if you used the geometry class from the 2D Graphics API, you could reduce this down to...
private void checkBrick() {
Rectangle b = new Rectangle(ball.getX(), ball.getY(), ball.getR(), ball.getR());
for (int i = 0; i < 50; i++) {
int tempX, tempY, tempW, tempH;
tempX = bX[i];
tempY = bY[i];
tempW = bW[i];
tempH = bH[i];
Rectangle brick = new Rectangle(tempX, tempY, tempW, tempH);
System.out.println("brick = " + brick);
if (b.intersects(brick)) {
System.out.println("Break");
}
}
}
And, yes, I did actually run your code
The problem is that the method checkBrick() is not changing anything, it is just printing if the ball has a collision with the brick.
You may want to change the Ball velocity, as you did within checkWall() and checkPaddle().
private void checkBrick() {
for (int i = 0; i < 50; i++) {
...
if (...) {
ball.setVX(...); // Add these lines setting the correct values
ball.setVY(...);
}
}
}
You may also want to check if your if-conditions are correct, and do what you expected.
Assuming tempH is positive,
((ball.getY() + ball.getR()) > (tempY + tempH)
&& (ball.getY() + ball.getR()) <= tempY)
can't ever be true. The > needs to be < and the <= needs to be >=.
Additionally, you'll need to take some kind of action if the brick is hit, rather than just printing out the fact. Sorry, I'm not sure what's supposed to happen - does the brick disappear? Or the ball bounce? Or both?
Second answer (in addition to other answer which I believe is ALSO a problem), your logic is asking if the ball is contained within a brick, but when you create the ball its radius is greater than the height of a brick, so even correcting that logic won't fix the problem.
You should refactor your code to make it read out like natural language, this would help a lot with debugging (or writing less bugs in the first place!) i.e.
in brick class:
public int bottom()
{
return y;
}
public int top()
{
return y + h;
}
in ball class:
public int bottom()
{
return y - r;
}
public int top() {
return y + r;
}
then in main class:
private boolean withinY(brick) {
return (ball.bottom => brick.bottom() && ball.top =< brick.top());
}
then the logic reads nicer (psuedo):
foreach brick in wall {
if (ball.withinY(brick) and ball.withinX(brick))
BAM!!
}
You're checking if the ball is between the left and right side of the brick, but then checking if the ball is both above AND below the brick, because you've got your greater than and less than's mixed up. Also the center of the ball needs to be subtracted from it's Y position.
if ((ball.getY() + ball.getR()) **>** (tempY + tempH) &&
(ball.getY() **+** ball.getR()) **<=** tempY)
could be
if ((ball.getY() + ball.getR()) < (tempY + tempH) &&
(ball.getY() - ball.getR()) >= tempY)
but I'd suggest finding if the top of the ball is between the top and bottom of the brick, OR if the bottom of the ball is between the top and bottom of the brick:
if (((ball.getY() + ball.getR()) < (tempY + tempH) && (ball.getY() - ball.getR()) >= tempY)) ||
((ball.getY() - ball.getR()) < (tempY + tempH) && (ball.getY() - ball.getR()) >= tempY))) {
CODE
}
And use similar logic for finding between left and right sides of the brick
I'm trying to learn java by following the cs106a course online.
Currently I've arrived at the breakout exersize and I'm having some trouble with bugs
I haven't finished it completely but I want to solves these issues first before I go further.
Problem 1:
when the ball collides with the bricks they don't always seem to be removed from the canvas.
sometimes the brick will be removed on a second time it collides. But there is one row (of yellow bricks) that doesn't respond at all to the ball collisions.
Problem 2:
my paddle can be moved by dragging the mouse. the only problem is. the bricks can also be moved like the paddle.
I know it has something to do with this piece of code
gobj = getElementAt(lastX, lastY);
If I remove it altogether the bricks aren't moveable anymore. which is good. but I'm still
able to move the paddle no matter where I click and drag.
can anyone give me a hint so I can correct the mistakes?
Here's my code below.
Thanks
/*
* File: Breakout.java
* -------------------
* This file will eventually implement the game of Breakout.
*/
import acm.graphics.*;
import acm.program.*;
import acm.util.*;
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import org.omg.CORBA.PUBLIC_MEMBER;
public class breakout extends GraphicsProgram {
/** Width and height of application window in pixels */
public static final int APPLICATION_WIDTH = 400;
public static final int APPLICATION_HEIGHT = 600;
/** Dimensions of game board (usually the same) */
private static final int WIDTH = APPLICATION_WIDTH;
private static final int HEIGHT = APPLICATION_HEIGHT;
/** Dimensions of the paddle */
private static final int PADDLE_WIDTH = 60;
private static final int PADDLE_HEIGHT = 10;
/** Offset of the paddle up from the bottom */
private static final int PADDLE_Y_OFFSET = 30;
/** Number of bricks per row */
private static final int NBRICKS_PER_ROW = 10;
/** Number of rows of bricks */
private static final int NBRICK_ROWS = 8;
/** Separation between bricks */
private static final int BRICK_SEP = 4;
/** Width of a brick */
private static final int BRICK_WIDTH = (WIDTH - (NBRICKS_PER_ROW - 1)* BRICK_SEP)/ NBRICKS_PER_ROW;
/** Height of a brick */
private static final int BRICK_HEIGHT = 8;
/** Radius of the ball in pixels */
private static final int BALL_RADIUS = 10;
/** Offset of the top brick row from the top */
private static final int BRICK_Y_OFFSET = 70;
/** Number of turns */
private static final int NTURNS = 3;
private static final int DELAY = 50;
private static final double X_START = WIDTH / 2;
private static final double Y_START = 450;
public void run() {
world();
play();
}
private void ball() {
ball = new GOval(X_START, Y_START, BALL_RADIUS, BALL_RADIUS);
ball.setFillColor(Color.BLACK);
ball.setFilled(true);
add(ball);
}
private void paddle() {
paddle = new GRect(100, 500, PADDLE_WIDTH, PADDLE_HEIGHT);
paddle.setColor(Color.BLACK);
paddle.setFilled(true);
add(paddle);
}
private void brick(int x, int y, Color c) {
GRect brick = new GRect(x, y, BRICK_WIDTH, BRICK_HEIGHT);
brick.setColor(getBackground());
brick.setFillColor(c);
brick.setFilled(true);
add(brick);
}
private void brickRow(int x, int y, Color c) {
x = BRICK_SEP / 2;
y += BRICK_Y_OFFSET;
for (int i = 0; i < NBRICKS_PER_ROW; i++) {
brick(x, y, c);
x += BRICK_WIDTH + BRICK_SEP;
}
}
private void world() {
//initialize x and y position for the rows of bricks
int x = 0;
int y = 0;
// set starting color for first row
Color c = Color.red;
paddle();
//create 2 rows of bricks and switch colors
for (int i = 0; i < NBRICK_ROWS; i++) {
if (i <= 1) {
c = Color.ORANGE;
} else if (i > 1 && i <= 3) {
c = Color.YELLOW;
} else if (i > 3 && i <= 5) {
c = Color.GREEN;
} else if (i > 5 && i <= 7) {
c = Color.CYAN;
}
brickRow(x, y, c);
y += BRICK_HEIGHT + BRICK_SEP;
}
}
private void moveBall() {
ball.move(xVel, yVel);
}
public void mousePressed(MouseEvent e) {
lastX = e.getX();
lastY = e.getY();
gobj = getElementAt(lastX, lastY);
}
public void mouseDragged(MouseEvent e) {
if (paddle != null) {
paddle.move(e.getX() - lastX, getY());
lastX = e.getX();
lastY = e.getY();
}
//constrain paddle movement
if (paddle.getX() < 0){
paddle.setLocation(0, 500);
}else if (paddle.getX()+BRICK_WIDTH > WIDTH ){
paddle.setLocation(WIDTH-BRICK_WIDTH, 500);
}
}
private void checkForCollision() {
// ball collission with walls
if (ball.getY() > getHeight() - BALL_RADIUS
|| ball.getY() < 0 + BALL_RADIUS) {
yVel = -yVel;
} else if (ball.getX() > getWidth() - BALL_RADIUS
|| ball.getX() < 1 + BALL_RADIUS) {
xVel = -xVel;
// ball collission with paddle
} else if (getElementAt(ball.getX() + BALL_RADIUS, ball.getY()
+ BALL_RADIUS) == paddle) {
yVel = -yVel;
// check for collision with bricks to remove them but ignore collision with paddle
} else if (getElementAt(ball.getX(), ball.getY()) != null
&& getElementAt(ball.getX(), ball.getY()) != paddle) {
remove(getCollidingObject(ball.getX(), ball.getY()));
yVel = -yVel;
} else if (getElementAt(ball.getX() + BALL_RADIUS, ball.getY()
+ BALL_RADIUS) != null
&& getElementAt(ball.getX() + BALL_RADIUS, ball.getY()
+ BALL_RADIUS) != paddle) {
remove(getCollidingObject(ball.getX() + BALL_RADIUS, ball.getY()
+ BALL_RADIUS));
yVel = -yVel;
} else if (getElementAt(ball.getX() + BALL_RADIUS, ball.getY()
+ BALL_RADIUS) != null
&& getElementAt(ball.getX() + BALL_RADIUS, ball.getY()
+ BALL_RADIUS) != paddle) {
remove(getCollidingObject(ball.getX() + BALL_RADIUS, ball.getY()
+ BALL_RADIUS));
yVel = -yVel;
} else if (getElementAt(ball.getX(), ball.getY() + BALL_RADIUS) != null
&& getElementAt(ball.getX(), ball.getY() + BALL_RADIUS) != paddle) {
remove(getCollidingObject(ball.getX(), ball.getY() + BALL_RADIUS));
yVel = -yVel;
}
}
private void play() {
addMouseListeners();
ball();
while (true) {
checkForCollision();
moveBall();
pause(DELAY);
}
}
private GObject getCollidingObject(double x, double y) {
return getElementAt(x, y);
}
private double lastX;
private double lastY;
private double xVel = 5;
private double yVel = 15;
private GOval ball;
private GRect paddle;
private GObject gobj;
}
Why don't you use suggested method in handout 19:
private GObject getCollidingObject()
use also collider:
GObject collider = getCollidingObject();
First of all separate your conditions with walls collisions and (paddle with bricks) game elements collisions into two separate methods cause to simplify else if cascading (decomposition I think you know what it means). You have only two objects what you should worry about paddle and bricks, so you need to compare only on (collider == paddle) and collider != null. Also you use only radius in comparisons but should use diameter (2 * radius). Have fun :)
You're y step for each movement of the ball is too large at 15. Since each brick is only 8 in height, at times you're bypassing the brick before evaluating whether that location contained an object or not. E.g. Lets assume you're last evaluation left you're ball with a Y location of 20 and the closest brick is only length of 2 away. You're next evaluation will not include this brick since you move 15 and the height of the brick is 8, meaning range of y values 22 to 30. You'll be out by 5 away from this brick in you're next evaluation. I know the values should be inverted considering the location of the bricks, but you get the point...
With smaller steps in y values collisions will be checked more regularly and provided you make the step small enough, remove this problem.