Stanford cs106a - Прорывное упражнение

Я пытаюсь изучить Java, следуя онлайн-курсу cs106a. В настоящее время я пришел к упражнению прорыва, и у меня возникли проблемы с ошибками.

Я еще не закончил его полностью, но я хочу сначала решить эти проблемы, прежде чем идти дальше.

Проблема 1:

когда мяч сталкивается с кирпичами, кажется, что они не всегда отрываются от холста. иногда кирпич будет удален при втором столкновении. Но есть один ряд (из желтых кирпичей), который вообще не реагирует на удары мяча.

Проблема 2:

мое весло можно перемещать, перетаскивая мышь. единственная проблема. кирпичи также можно перемещать, как весло.

Я знаю, что это как-то связано с этим фрагментом кода.

gobj = getElementAt(lastX, lastY);

Если я уберу его вообще, кирпичи больше не будут двигаться. и это хорошо. но я все еще могу перемещать весло независимо от того, куда я нажимаю и перетаскиваю.

может ли кто-нибудь дать мне подсказку, чтобы я мог исправить ошибки?

Вот мой код ниже. Спасибо

   /*  
 * 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;

}

person Ramin    schedule 30.06.2011    source источник
comment
Одна из проблем заключается в том, что у вас есть один огромный божественный класс, что делает ваш код трудным для понимания. анализировать или отлаживать. Не думали о рефакторинге и разбиении задачи на отдельные классы?   -  person Hovercraft Full Of Eels    schedule 01.07.2011
comment
@Ramin Обычно я не комментирую вопросы такого типа, но мне скучно в мой последний рабочий день здесь. Ваш код предполагает, что при столкновении никогда не будет перекрытия, и мяч всегда будет аккуратно ударяться о кирпич в верхнем левом углу (или в правом нижнем углу). Вероятно, вам следует переоценить код обнаружения столкновений. Это проблема, которая решалась миллион раз — быстрый поиск обнаружения столкновений скажет вам все, что вам нужно знать.   -  person SRM    schedule 01.07.2011
comment
@Hovercraft Full Of Eels Я думаю, вы имеете в виду, что мой частный метод void checkForCollision () { слишком сложен и запутан? у вас, вероятно, есть хорошая точка там. Я посмотрю, смогу ли я структурировать это лучше.   -  person Ramin    schedule 01.07.2011
comment
@Ramin Нет, я думаю, он имеет в виду то, что сказал. Я вижу по крайней мере три класса, которые вы могли бы реорганизовать оттуда. Я бы даже посмотрел на абстрагирование системы обработки событий (чтобы вы могли назначить несколько жестких событий, например мышь, клавиатуру, одному и тому же мягкому событию, например MoveUpEvent)   -  person SRM    schedule 01.07.2011
comment
ок @SRM я понял. Я постараюсь разбить это на разные классы. и я рассмотрю то, что вы сказали об обнаружении столкновений. Я вернусь с улучшенной версией.   -  person Ramin    schedule 01.07.2011
comment
@Ramin Это тоже поможет твоей оценке. Ваш профессор будет впечатлен вашим мастерством ООП для языка ;). Если вы хотите работать в полную силу, вы можете создать класс для кирпичей (это может быть так же просто, как обертка вокруг прямоугольника с некоторым обнаружением столкновений и, возможно, индексом для представления цвета/оценки), один для мира (который содержит и строит кирпичи). кирпичи), один для мяча, один для ракетки и, наконец, один для самого приложения.   -  person SRM    schedule 01.07.2011


Ответы (2)


Почему бы вам не использовать метод, предложенный в раздаточном материале 19:

private GObject getCollidingObject()

используйте также коллайдер:

GObject collider = getCollidingObject();

Прежде всего, разделите ваши условия со столкновениями со стенами и (весло с кирпичами) столкновениями игровых элементов на два отдельных метода, чтобы упростить else if каскадирование (декомпозиция, я думаю, вы знаете, что это значит). У вас есть только два объекта, о которых вам следует беспокоиться paddle и bricks, поэтому вам нужно сравнивать только (collider == paddle) и collider != null. Также вы используете только радиус в сравнениях, но следует использовать диаметр (2 * радиус). Повеселись :)

person Gennadiy Ryabkin    schedule 01.09.2012

Ваш шаг y для каждого движения мяча слишком велик для 15. Поскольку каждый кирпич имеет высоту всего 8, иногда вы обходите кирпич, прежде чем оценить, находится ли в этом месте объект или нет. Например. Давайте предположим, что после последней оценки вы попали в мяч с координатой Y, равной 20, а ближайший кирпич находится на расстоянии всего 2 длины. Ваша следующая оценка не будет включать этот кирпич, так как вы переместите 15, а высота кирпича равна 8, что означает диапазон значений y от 22 до 30. В следующей оценке вы будете на расстоянии 5 от этого кирпича. Я знаю, что значения должны быть инвертированы с учетом расположения кирпичей, но вы поняли...

При меньших шагах значений y коллизии будут проверяться более регулярно, и при условии, что вы сделаете шаг достаточно маленьким, эта проблема устранится.

person Simon    schedule 20.03.2014