The problem I'm trying to solve is building a pyramid centered in the window built with individual bricks. With the code blow I expect the there should be 1 brick on the 1st row, two bricks on the 2nd row, 3 on the 3rd row all the way up to 12 bricks on the base of the pyramid. Instead only 1 brick is appearing in the center of the screen. What could I do to correct my code?
import acm.program.*;
import acm.graphics.*;
public class BrickPyramid extends GraphicsProgram{
public void run() {
/** xBrick, yBrick trying to calculate for the center of the window **/
double xBrick = (getWidth() - BRICK_WIDTH) / 2 ;
double yBrick = (getHeight() - BRICK_HEIGHT) /2 ;
/** getting the size of the brick by multiplying the brick width by brick height **/
double buildingBrick = BRICK_WIDTH * BRICK_HEIGHT;
for (int i = 0 ; i <= 12; i ++ ){
for (int j = 0; j < BRICKS_IN_BASE; j++){
double x = i * buildingBrick;
double y = j * buildingBrick;
/* adding the brick with starting point xbrick, ybrick **/
GRect brick = new GRect (xBrick, yBrick, BRICK_WIDTH, BRICK_HEIGHT);
add(brick);
}
}
}
private static final int BRICK_WIDTH = 100;
private static final int BRICK_HEIGHT = 50;
private static final int BRICKS_IN_BASE = 12;
}
You construct all of your bricks with this line executed many times:
GRect brick = new GRect (xBrick, yBrick, BRICK_WIDTH, BRICK_HEIGHT);
but you never change the values of any of the variables passed to the constructor so the bricks are all drawn in the same place.
You need to change these values for each brick. Maybe use i and j to determine those values?
don't know if you are still looking for an answer but this code does the job:
public class Pyramid extends GraphicsProgram {
private static final int BRICK_WIDTH = 30;
private static final int BRICK_HEIGHT = 12;
public void run() {
double xBrick = (getWidth() - BRICK_WIDTH) / 2 ;
double yBrick = (getHeight() - BRICK_HEIGHT) /2 ;
for (int n=1;n<15;n++){
for (int m=0;m<=n-1;m++){
GRect Brick =new GRect(xBrick-(BRICK_WIDTH*(n-1))/2+m*BRICK_WIDTH,yBrick+BRICK_HEIGHT*(n-1),BRICK_WIDTH,BRICK_HEIGHT);
add(Brick);
}
}
}
}
Related
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?
This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 6 years ago.
import acm.graphics.*;
import acm.program.*;
import acm.util.*;
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.util.EventListener;
public class Breakout1 extends GraphicsProgram {
public static final int APPLICATION_WIDTH = 400;
public static final int APPLICATION_HEIGHT = 600;
private static final int WIDTH = APPLICATION_WIDTH;
private static final int HEIGHT = APPLICATION_HEIGHT;
private static final int PADDLE_WIDTH = 60;
private static final int PADDLE_HEIGHT = 10;
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 = 10;
/**
* 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;
/* Method: init() */
/**
* Sets up the Breakout program.
*/
public void init() {
add (new GImage("C:\\Users\\Home\\Desktop\\beach.jpg"));
drawCanvas();
drawBrickWall();
drawPaddle();
drawBall();
addMouseListeners();
}
/* Method: run() */
/**
* Runs the Breakout program.
*/
public void run() {
}
private GRect brick;
private GOval ball;
private GRect paddle;
private void drawBrickWall() {
for (int i = 0; i < NBRICK_ROWS; i++)
for (int j = 0; j < NBRICKS_PER_ROW; j++)
drawBrick(i, j);
}
private void drawBrick(int row, int col) {
double x, y; // brick location
GRect brick = new GRect(BRICK_WIDTH, BRICK_HEIGHT);
x = computeXOffset() + col * (BRICK_WIDTH + BRICK_SEP);
y = BRICK_Y_OFFSET + row * (BRICK_HEIGHT + BRICK_SEP);
brick = new GRect(x, y, BRICK_WIDTH, BRICK_HEIGHT);
brick.setFilled(true);
add(brick, x, y);
if (row < 2) {
brick.setColor(Color.RED);
} else if (row == 2 || row == 3) {
brick.setColor(Color.ORANGE);
} else if (row == 4 || row == 5) {
brick.setColor(Color.YELLOW);
} else if (row == 6 || row == 7) {
brick.setColor(Color.GREEN);
} else if (row == 8 || row == 9) {
brick.setColor(Color.CYAN);
}
}
private double computeXOffset() {
return 0.5 * (WIDTH - (NBRICKS_PER_ROW - 1) * BRICK_SEP - BRICK_WIDTH * NBRICKS_PER_ROW);
}
private void drawPaddle() {
GRect paddle = new GRect(PADDLE_WIDTH, PADDLE_HEIGHT);
paddle.setFilled(true);
add(paddle, 0.5 * (WIDTH - PADDLE_WIDTH), HEIGHT - PADDLE_Y_OFFSET - PADDLE_HEIGHT);
addMouseListeners();
}
private boolean isMouseXInsideWindow(int x) {
return ((x > PADDLE_WIDTH / 2) && (x < (WIDTH - PADDLE_WIDTH / 2)));
}
public void mouseMoved(MouseEvent e) {
if (isMouseXInsideWindow(e.getX())) {
paddle.setLocation(paddle.getX() - PADDLE_WIDTH / 2, paddle.getY());
}
}
private void drawBall(){
GOval ball = new GOval(2*BALL_RADIUS, 2*BALL_RADIUS);
ball.setFilled(true);
add(ball, 0.5*WIDTH - BALL_RADIUS , 0.5*HEIGHT - BALL_RADIUS );
}
private void drawCanvas(){
GLabel myLabel= new GLabel ("Welcome to my Breakout Game!");
myLabel.setFont("Serif-bold-24");
myLabel.setColor(Color.CYAN);
add(myLabel,getXCenter(myLabel), getYCenter(myLabel));
}
private double getXCenter (GObject g){
return (getWidth()-g.getWidth())/2;
}
private double getYCenter (GObject g){
return (getHeight()-g.getHeight())/2;
}
}
it keeps giving me error NULLPOINTEXCEPTION as I run the program, Can someone try to check what is wrong with the mouseMoved(MouseEvent e), the paddle won't move and keeps giving me error. but the rest of the code are working.
The Paddle you refer to as a data member doesn't seem to have been instantiated. You do create a Paddle, but it hasn't been assigned to the member variable, which is what you use in the rest of your code.
So, in your code, within drawPaddle(), you create a Paddle like this:
GRect paddle = new GRect(...).
However, this is never assigned to the class local variable paddle; therefore once drawPaddle() is called, your reference to the allocated variable is essentially lost. When you come to calling isMouseXInsideWindow(int), you try to use paddle.x; but this refers to the member variable paddle belonging to the class itself, which has been uninitialized.
So, when you originally allocate a paddle, it needs to be assigned to the classes' member variable instead of becoming a local variable. It's a real easy fix; within drawPaddle(), simply use paddle = new GRect(...) instead of GRect paddle = new GRect(...); this ensures we allocate the reference to the member variable paddle, rather than creating a local one whose reference we lose track of after the method has been called.
To avoid these problems in future, you should try designing your method calls so they expect a GRect to be passed into them. This allows you to be a lot more specific about how your methods work, and will force you to be careful with how you manage your references. Additionally, you'll find it'll help you a whole bunch if in future you wanted to add a second paddle, or a third or a fourth...
As a side note, whenever you're struggling with NullPointerExceptions, it helps to narrow down the exact source of the error. You already knew the problem had to lie somewhere in the mouseMoved(...) method, so you could try printing various variables you use to the console to see if they print something valid, print null, or don't even get printed at all (which indicates that the application terminated somewhere earlier than where you placed your print statement).
This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 7 years ago.
Ok so I have this program which creates a
NanOrg
object that acts as a moving ball that bounces off the walls and other NanOrgs
`public class NanOrg extends Ellipse2D.Float{
public int x_speed, y_speed;
private int d;
private int width = MainWindow.WIDTH;
private int height = MainWindow.HEIGHT;
private ArrayList<NanOrg> nanorgs;
Rectangle2D r = new Rectangle2D.Float(super.x, super.y, d, d);
public NanOrg(int diameter, ArrayList<NanOrg> nanorgs){
super((int) (Math.random() * (MainWindow.WIDTH - 20) + 1), (int) (Math.random() * (MainWindow.HEIGHT - 20) + 1), diameter, diameter);
this.d = diameter;
this.x_speed = (int) (Math.random() * 5 + 1);
this.y_speed = (int) (Math.random() * 5 + 1);
this.nanorgs = nanorgs;
}
public void move(){
for(NanOrg nanorg : nanorgs){
if(nanorg != this && nanorg.intersects(r)){
int tempx = x_speed;
int tempy = y_speed;
x_speed = nanorg.x_speed;
y_speed = nanorg.y_speed;
nanorg.x_speed = tempx;
nanorg.y_speed = tempy;
break;
}
}
if(super.x < 0){
super.x = 0;
x_speed = Math.abs(x_speed);
}
else if(super.x > width - d){
super.x = width - d;
x_speed = -Math.abs(x_speed);
}
if(super.y < 0){
super.y = 0;
y_speed = Math.abs(y_speed);
}
else if(super.y > height - d){
super.y = height - d;
y_speed = -Math.abs(y_speed);
}
super.x += x_speed;
super.y += y_speed;
}
}` and a
Oil
object which represents unmoving droplets of oil.
public class Oil extends Ellipse2D.Float {
private int width = MainWindow.WIDTH;
private int height = MainWindow.HEIGHT;
private int d;
private ArrayList<Oil> oils;
Rectangle2D rect = new Rectangle2D.Float(super.x, super.y, d, d);
public Oil(int diameter){
super((int) (Math.random() * (MainWindow.WIDTH - 20) + 1), (int) (Math.random() * (MainWindow.HEIGHT - 20) + 1), diameter, diameter);
this.d = diameter;
}
}
The NanOrgs are spawn at a random location and move in a random direction and speed. The NanOrg's are able to bounce of eachother, but I need help figuring out how to deal with the collision between the Oil and NanOrg, specifically so the NanOrg can "eat" the oil by removing it. I have two other Classes:
MainWindow and PaintSurface
The MainWindow Class creates the window as a JApplet.
`public class MainWindow extends JApplet{
public static final int WIDTH = 500;
public static final int HEIGHT = 500;
private PaintSurface canvas;
public void init(){
this.setSize(WIDTH, HEIGHT);
this.setName("NanOrg Program");
canvas = new PaintSurface();
this.add(canvas, BorderLayout.CENTER);
Timer t = new Timer(20, e -> {canvas.repaint();});
t.start();
}
}
The PaintSurface Class "draws" the Oil and NanOrgspublic class PaintSurface extends JComponent{
public ArrayList<NanOrg> nanorgs = new ArrayList<NanOrg>();
public ArrayList<Oil> oils = new ArrayList<Oil>();
public PaintSurface(){
for(int i = 0; i < 5; i++)
nanorgs.add(new NanOrg(10, nanorgs));
for(int o = 0; o < 30; o++)
oils.add(new Oil(10));
}
public void paint(Graphics g){
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.MAGENTA);
for(NanOrg nanorg : nanorgs){
nanorg.move();
g2.fill(nanorg);
}
for(Oil oil : oils){
g2.setColor(Color.BLACK);
g2.fill(oil);
}
}
}`
That's my program, so just to recap: I have created it so the NanOrgs bounce off each other and the walls, but I need help on what I need to code for the NanOrg to "eat" the Oil(e.g remove it) and where to place the code that removes the Oil. I have tried
public void eatOil(){
for(Oil oil : oils){
if(oil.intersects(r)){
oils.remove(oil);
}
}
}
in the NanOrg Class and then called the method in my PaintSurface class, but when I ran it, I got the NullPointerException. Then I tried doing that same code in the Oil and PaintSurface Class, but I couldn't get either of the two classes to use "r" as the variable defined in NanOrg. So if you could just help me figure out what to do that would be great or tell me what to do if i'm doing something wrong. If you have questions to try and clarify ypur knowledge please ask and i'll answer to the best of my abilities.
You have to create a method inside NanOrgs to detect if a given NanOrg and given Oil intersect each other. You'll need to maintain a list of Oil instances in Oil class just like the list of NanOrgs in NanOrg class.
Then on every call to move() of NanOrg you'll have to check if that NanOrg intersects any Oil droplets in the Oil list. There if you find they intersect, you'll have to remove Oil from the list in Oil.
That's all.
private void addBricks(){
for (int i = 0; i < NBRICK_ROWS; i++){
for (int j = 0; j < NBRICKS_PER_ROW; j++){
int y = BRICK_Y_OFFSET + (i * (BRICK_HEIGHT + BRICK_SEP));
int x = (BRICK_X_OFFSET) + (j * (BRICK_WIDTH + BRICK_SEP));
brick = new GRect (x, y, BRICK_WIDTH, BRICK_HEIGHT );
add (brick);
}
}
}
I am trying to make a breakout game here and this is a snippet of code.
I have added a set of rectangles to my canvas. Later in my program I would like to remove some of them when the ball hits them. The problem is that I do not know how to refer to them specifically. That is to say that I do not know what parameters to input in remove() since all of them were created as brick
Thanks!
Whole program below
import acm.graphics.*;
import acm.program.*;
import acm.util.*;
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class Breakout extends GraphicsProgram {
/** Width and height of application window in pixels */
public static final int APPLICATION_WIDTH = 440;
public static final int APPLICATION_HEIGHT = 660;
/** Dimensions of game board (usually the same) */
private static final int WIDTH = 400;
private static final int HEIGHT = 600;
/** 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 = 10;
/** 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;
/** Offset of the side bricks from the sides of game window so that the whole set of bricks are in the centre */
private static final int BRICK_X_OFFSET = ((WIDTH - NBRICKS_PER_ROW * (BRICK_WIDTH + BRICK_SEP) + BRICK_SEP) / 2);
/** Number of turns */
private static final int NTURNS = 3;
/** Game speed */
private static final int DELAY = 1;
/* Method: run() */
/** Runs the Breakout program. */
public void run() {
addMouseListeners();
addWorld();
runGame();
}
private void addWorld(){
setSize (APPLICATION_WIDTH, APPLICATION_HEIGHT);
addPlayingBox();
addBricks();
addPaddle();
}
/* lives and number of bricks will be check before game runs
* if there are either no lives of bricks left game is over
* ball and ball physics are only added to the screen after there is a mouse click.
*/
private void runGame(){
while (true){
// add a gameover label
waitForClick();
addBall();
addBallMotion();
}
}
//adds the playing area onto screen
private void addPlayingBox(){
topBounds = new GLine (0, 0, WIDTH, 0);
bottomBounds = new GLine (0, HEIGHT, WIDTH, HEIGHT);
leftBounds = new GLine (0, 0, 0, HEIGHT);
rightBounds = new GLine (WIDTH, 0, WIDTH, HEIGHT);
add (topBounds);
add (bottomBounds);
add (leftBounds);
add (rightBounds);
}
/*bricks are added to the top row and left column 1st and consequently to the its right column
* and to the next row when a row is all filled up
*/
private void addBricks(){
for (int i = 0; i < NBRICK_ROWS; i++){
for (int j = 0; j < NBRICKS_PER_ROW; j++){
int y = BRICK_Y_OFFSET + (i * (BRICK_HEIGHT + BRICK_SEP));
int x = (BRICK_X_OFFSET) + (j * (BRICK_WIDTH + BRICK_SEP));
brick = new GRect (x, y, BRICK_WIDTH, BRICK_HEIGHT );
colorBrick(brick, i);
add (brick);
}
}
}
// every consecutive 2 rows are colored the same
private void colorBrick(GRect brick, int rowNumber){
brick.setFilled (true);
switch (rowNumber + 1) {
case 1: case 2: brick.setColor(Color.red);
break;
case 3: case 4: brick.setColor(Color.orange);
break;
case 5: case 6: brick.setColor(Color.yellow);
break;
case 7: case 8: brick.setColor(Color.green);
break;
case 9: case 10:brick.setColor(Color.cyan);
break;
}
}
//adds paddle to screen
private void addPaddle(){
paddle = new GRect (WIDTH / 2 - PADDLE_WIDTH / 2, HEIGHT - PADDLE_Y_OFFSET, PADDLE_WIDTH, PADDLE_HEIGHT);
paddle.setFilled(true);
paddle.setColor (Color.BLACK);
add (paddle);
}
//creates motion for the paddle according to mouse movement
public void mouseMoved(MouseEvent e){
paddle.setLocation ((e.getX() - PADDLE_WIDTH / 2), (double) (HEIGHT - PADDLE_Y_OFFSET));
/* checks if the paddle within the playing area
* if not, the paddles will stay at the extremities*/
if ( paddle.getX() > (WIDTH - PADDLE_WIDTH)){
paddle.setLocation((double) (WIDTH - PADDLE_WIDTH), (double) (HEIGHT - PADDLE_Y_OFFSET));
}
if ( paddle.getX() < 0){
paddle.setLocation((double) 0, (double) (HEIGHT - PADDLE_Y_OFFSET));
}
}
/* creates the ball
*/
private void addBall(){
double ballInitXPosition = WIDTH / 2 - BALL_RADIUS;
double ballInitYPosition = HEIGHT / 2 - BALL_RADIUS;
ball = new GOval (ballInitXPosition, ballInitYPosition,
BALL_RADIUS * 2, BALL_RADIUS * 2);
ball.setFilled(true);
ball.setColor(Color.BLACK);
add (ball);
}
/* kick start the ball motion with predetermined values
*/
private void addBallMotion(){
// y component of starting velocity; pixels per second
vy = 0.3;
/* x component of starting velocity; pixels per second
* which ranges according to the random generator
* can be negative or positive, ball can go left or right with equal chances
* angle must not be 0 or 180 degrees otherwise the ball will not move anywhere else
*/
vx = rgen.nextDouble(0.0, 0.5);
if (rgen.nextBoolean(0.5)){
vx = -vx;
}
while (true){
ball.move(vx, vy);
checkCollision();
pause(DELAY);
}
}
private void checkCollision(){
checkWallCollision();
checkBrickAndPaddleCollision();
}
//checks for wall collision
private void checkWallCollision(){
if ( (getCollidingObject() == leftBounds) || (getCollidingObject() == rightBounds )){
vx = -vx;
}
//checks for floor and ceiling collision collision
if ( (getCollidingObject() == topBounds) ){
vy = -vy;
}
if ( (getCollidingObject() == bottomBounds)){
remove (ball);
runGame();
}
}
/* check if there is any collision with brick or paddle
*/
private void checkBrickAndPaddleCollision(){
GObject collider = getCollidingObject();
/* brick is removed and y direction of ball is reversed
* bricks counter goes down by 1 and score goes up by 1
*/
if (collider != paddle &&
collider != null &&
collider != topBounds &&
collider != rightBounds &&
collider != bottomBounds &&
collider != leftBounds){
remove(collider);
vy = -vy;
}
/* collide with paddle, y direction is reversed
*/
else if (collider == paddle){
vy = -vy;
}
}
//check for collision at the 4 sides of the ball
//starting with the top and going clockwise
//returns the object nearest to the screen that touches the sides of the ball
private GObject getCollidingObject(){
GObject ballTop = getElementAt ( (ball.getX() + BALL_RADIUS), (ball.getY() - .1) );
GObject ballRight = getElementAt ( (ball.getX() + (BALL_RADIUS * 2) + .1), (ball.getY() + BALL_RADIUS) );
GObject ballBottom = getElementAt ( (ball.getX() + BALL_RADIUS), ball.getY() + (BALL_RADIUS * 2) + .1);
GObject ballLeft= getElementAt ( (ball.getX() - .1), (ball.getY() + BALL_RADIUS) );
if (ballTop != null){
return (ballTop);
}
else if (ballRight != null){
return (ballRight);
}
else if (ballBottom != null){
return (ballBottom);
}
else if (ballLeft != null){
return (ballLeft);
}
return (null);
}
private GRect paddle; // creates a paddle that only moves linearly according to mouses' x coordinate
private GRect brick;
private GOval ball;
private double vx, vy; // x and y components of the ball's velocity
private RandomGenerator rgen = RandomGenerator.getInstance();
// creates a bounding box that is the playing area
private GLine topBounds;
private GLine bottomBounds;
private GLine leftBounds;
private GLine rightBounds;
}
You'll have to maintain a separate mapping of which bricks exist. I imagine you'd need something like this to do collision detection as well. I would do something like the following.
public class Game
{
Collection<Brick> bricks;
Ball ball;
public void initLocation()
{
for (int i = 0; i < NBRICK_ROWS; i++)
{
for (int j = 0; j < NBRICKS_PER_ROW; j++)
{
int y = BRICK_Y_OFFSET + (i * (BRICK_HEIGHT + BRICK_SEP));
int x = (BRICK_X_OFFSET) + (j * (BRICK_WIDTH + BRICK_SEP));
bricks.add(new Brick(x, y));
}
}
}
public void drawBricks()
{
for (Brick brick : bricks)
{
brickRect = new GRect(x, y, BRICK_WIDTH, BRICK_HEIGHT);
add(brick);
}
}
}
public class Brick
{
int x;
int y;
public Brick(int x, int y)
{
this.x = x;
this.y = y;
}
}
public class Ball
{
int x;
int y;
}
This code isn't complete but should give you an idea on how to keep a reference to the brick. When you're checking for collision, you can iterate through the bricks, find any the have a collision with the ball and then call remove() on that brick.
So, if you are using add() from an external library (acm.graphics), you'll need to keep your own copies of bricks that you add. Perhaps add a 2D array of boolean values or a structure like #thattolleyguy suggested to your class then a method to add them
private void addBrick(int x, int y)
{
brickArray[x][y] = true; //your own copy of brick locations
int yloc = BRICK_Y_OFFSET + (i * (BRICK_HEIGHT + BRICK_SEP));
int xloc = (BRICK_X_OFFSET) + (j * (BRICK_WIDTH + BRICK_SEP));
brick = new GRect (xloc, yloc, BRICK_WIDTH, BRICK_HEIGHT );
add (brick);
}
Then you can easily remove them from your array (set that location to false) and from your external library (or you may need a redraw) with a removeBrick method.
Also
The object reference brick is only referenced within this function so it only needs to be local
private void addBricks()
{
GRect brick;
...
add(brick);
...
}
If I was you, I would store all GRects in a List<GRect>. This list should be at the center of your operations.
I suppose the drawing are triggered by repaint() method. Possibly in a sperate thread, you need to check the distance between the ball and each GRect, when you find a GRect intersected with the ball, you can pass that GRect to remove().
One simple solution is to keep an isVisible flag in each GRect and modify it in remove(). BTW, Java Objects are passed by reference, your modification will take effects.
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.