我是个初学者,目前正在学习教Java的斯坦福大学免费CS106A课程e。第二次分配让您从零开始使用ACM程序和图形包构建突破。
因为这是一门在线课程,而且我没有任何人可以复习我的代码,所以我想我可能会问你我犯了什么错误。我肯定有很多。
我很难弄清楚该把这些行为放在哪里。我有一个Ball类,我想在其中放置碰撞检测和反应,但最后不得不将它放在主程序中,因为ACM不允许我从球内检查球周围的帆布。
我还可以从任何地方获得主程序的实例,只有它的类,这不是我所需要的。对怎么做有什么想法吗?其他例子也是问题。例如,有什么方法可以让我不把Paddle's实例传递给构造函数就可以在Ball中得到它?有没有方法调用它?
我不知道该在哪里使用私有/公共和静态。我觉得我的很多用途都是变通的。
你怎么会以不同的方式建造这个呢?我应该停止犯什么大错误?组织得很好吗?
import acm.graphics.*;
import acm.program.*;
import acm.util.*;
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
public class Breakout extends GraphicsProgram {
public void init() {
setBricks();
add(paddle);
add(ball,WIDTH/2,500);
lifeBar.setColor(Color.WHITE);
add(lifeBar,20,60);
addMouseListeners();
setBackground(Color.BLACK);
}
public void run() {
while(gameOver != true) {
pause(20);
checkCollision(ball);
ball.move();
if (prize != null) {
prize.drop();
if (prize.leftScreen()) remove(prize);
}
}
addMouseListeners();
}
private void setBricks() {
for (int row = 0; row < NBRICK_ROWS; row++) {
Color rowColor = checkColor(row);
for (int col = 0; col < NBRICKS_PER_ROW; col++) {
add(new Brick(col,row,rowColor));
}
}
}
public void mouseClicked(MouseEvent e) {
removeBrick(getElementAt(e.getX(),e.getY()));
}
public void mouseMoved(MouseEvent e) {
paddle.followMouse(e.getX());
}
private Color checkColor(int row) {
switch (row) {
case 0:
case 1: return Color.RED;
case 2:
case 3: return Color.ORANGE;
case 4:
case 5: return Color.YELLOW;
case 6:
case 7: return Color.GREEN;
case 8:
case 9: return Color.CYAN;
}
return Color.CYAN;
}
private void removeBrick(GObject brick) {
/* add test to see if removing brick is possible */
if (brick != null && brick instanceof Brick) {
remove(brick);
if (((Brick) brick).isSpecial()) callPrize(brick.getX(),brick.getY());
if (Brick.decreaseBricks()) callGameOver(true);
}
}
private void removeLife() {
lives--;
lifeBar.setLabel("Lives: " + lives);
if (lives <= 0) callGameOver(false);
}
public void callPrize(double x, double y) {
prize = new Prize(x,y,Color.MAGENTA, paddle);
add(prize);
}
public void callGameOver(boolean won) {
GRect gameOverScreen = new GRect(0,0,WIDTH,HEIGHT);
gameOverScreen.setFilled(true);
gameOverScreen.sendToFront();
GLabel gameOverLabel = new GLabel("");
if (won == true) {
gameOverScreen.setColor(Color.WHITE);
gameOverLabel.setColor(Color.BLUE);
gameOverLabel.setLabel("WOO YOU WON!!!");
if (lives == 3) gameOverLabel.setLabel("HOLY SHIT PERFECT SCORE!!!");
} else {
gameOverScreen.setColor(Color.RED);
gameOverLabel.setColor(Color.WHITE);
gameOverLabel.setLabel("HAH WHAT A LOSER");
}
add(gameOverScreen);
add(gameOverLabel,WIDTH/2-gameOverLabel.getWidth()/2,HEIGHT/2+gameOverLabel.getAscent()/2);
gameOver = true;
}
private void checkCollision(Ball ball) {
double r = ball.getRadius();
ball.move();
double x = ball.getX();
double y = ball.getY();
ball.moveBack();
GObject collider = null;
int dir = 0;
// Check walls + keep within bounds (keep from getting stuck)
if (x-r <= 0) {
ball.setLocation(r,y);
ball.changeDirection(1);
} else if (x+r >= WIDTH) {
ball.setLocation(WIDTH-r,y);
ball.changeDirection(1);
}
if (y-r <= 0) {
ball.setLocation(x,r);
ball.changeDirection(2);
// less life
} else if (y+r >= HEIGHT) {
removeLife();
ball.restart();
}
//Check elements
//Check right
if (getElementAt(x+r,y) != null && getElementAt(x+r,y) != ball && !(getElementAt(x+r,y) instanceof GLabel) && !(getElementAt(x+r,y) instanceof Prize)) {
ball.changeDirection(1);
collider = getElementAt(x+r,y);
}
//Check left
if (getElementAt(x-r,y) != null && getElementAt(x-r,y) != ball && !(getElementAt(x-r,y) instanceof GLabel) && !(getElementAt(x-r,y) instanceof Prize)) {
ball.changeDirection(1);
collider = getElementAt(x-r,y);
}
//Check bottom
if (getElementAt(x,y+r) != null && getElementAt(x,y+r) != ball && !(getElementAt(x,y+r) instanceof GLabel) && !(getElementAt(x,y+r) instanceof Prize)) {
ball.changeDirection(2);
collider = getElementAt(x,y+r);
}
//Check top
if (getElementAt(x,y-r) != null && getElementAt(x,y-r) != ball && !(getElementAt(x,y-r) instanceof GLabel) && !(getElementAt(x,y-r) instanceof Prize)) {
ball.changeDirection(2);
collider = getElementAt(x,y-r);
}
ball.changeDirection(dir);
if (collider instanceof Brick) removeBrick(collider);
if (collider == paddle) {
ball.setDirection(paddle.collide(x,r));
}
}
/* Init things */
private Paddle paddle = new Paddle();
private Ball ball = new Ball(BALL_RADIUS);
/** 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) */
public static final int WIDTH = APPLICATION_WIDTH;
public static final int HEIGHT = APPLICATION_HEIGHT;
/** Offset of the paddle up from the bottom */
public static final int PADDLE_Y_OFFSET = 30;
/** Number of bricks per row */
public static final int NBRICKS_PER_ROW = 10;
/** Number of rows of bricks */
private static final int NBRICK_ROWS = 10;
/** Radius of the ball in pixels */
private static final int BALL_RADIUS = 10;
/** Offset of the top brick row from the top */
public static final int BRICK_Y_OFFSET = 70;
/** Number of turns */
private static final int NTURNS = 3;
/** Points */
private int lives = NTURNS;
/** Game is Over */
private boolean gameOver = false;
/* Prize on Board */
Prize prize = null;
private GLabel lifeBar = new GLabel("Lives: " + lives);
}Ball类import acm.graphics.*;
import acm.util.*;
import java.awt.*;
public class Ball extends GCompound {
public Ball(double radius) {
r = radius;
GOval ball = new GOval(-r,-r,r*2,r*2);
ball.setFilled(true);
ball.setColor(new Color(200,200,200));
GOval light = new GOval(-r+3,-r+3,r*2-6,r-2);
light.setFilled(true);
light.setColor(new Color(255,255,255,150));
add(ball);
add(light);
direction = rgen.nextDouble(220,320);
direction = 290;
}
public void move() {
this.movePolar(velocity,direction);
}
public void moveBack() {
this.movePolar(velocity, (direction+180)%360);
}
public void restart() {
this.setVisible(false);
pause(100);
this.setVisible(true);
pause(100);
this.setVisible(false);
pause(100);
this.setLocation(Breakout.WIDTH/2,Breakout.HEIGHT/2);
this.setVisible(true);
direction = rgen.nextDouble(220,320);
}
public double getRadius() {
return r;
}
public void changeDirection(int xory) {
if (xory == 1) direction -= (direction-270)*2;
if (xory == 2) direction -= (direction-180)*2;
}
public double getDirection() {
return direction;
}
public static void setVelocity(double v) {
velocity = v;
}
public void setDirection(double d) {
direction = d;
}
private double r;
private double direction;
private static final double STARTING_VELOCITY = 5.0;
private static double velocity = STARTING_VELOCITY;
private RandomGenerator rgen = RandomGenerator.getInstance();
}Brick类import acm.graphics.*;
import acm.util.*;
import java.awt.*;
public class Brick extends GCompound {
public Brick(int colNum, int rowNum, Color color) {
if (rgen.nextBoolean(0.01)) {
color = Color.MAGENTA;
special = true;
}
GRect fill = new GRect(0,0,BRICK_WIDTH, BRICK_HEIGHT);
fill.setFilled(true);
fill.setColor(color);
GRect light = new GRect(0,0,BRICK_WIDTH,BRICK_HEIGHT/2);
light.setFilled(true);
light.setColor(new Color(255,255,255,120+10*rowNum));
if (special == true) light.setColor(new Color(255,255,255,120));
add(fill);
add(light);
// Find and set location
double x = START_X + colNum * (BRICK_WIDTH + BRICK_SEP);
double y = START_Y + rowNum * (BRICK_HEIGHT + BRICK_SEP);
setLocation(x,y);
// Add to bricks remaining;
bricksRemaining++;
}
public static int getBricksRemaining() {
return bricksRemaining;
}
/* decreases bricks remaining and checks for end */
public static boolean decreaseBricks() {
bricksRemaining--;
if (bricksRemaining < 75) Ball.setVelocity(7); //Set the velocity higher if
if (bricksRemaining < 50) Ball.setVelocity(9);
if (bricksRemaining < 25) Ball.setVelocity(11);
return (bricksRemaining > 0) ? false : true;
}
public boolean isSpecial() {
return special;
}
/** Separation between bricks */
public static final int BRICK_SEP = 4;
/** Width of a brick */
public static final int BRICK_WIDTH =
(Breakout.WIDTH - (Breakout.NBRICKS_PER_ROW - 1) * BRICK_SEP) / Breakout.NBRICKS_PER_ROW;
/** Height of a brick */
public static final int BRICK_HEIGHT = 12;
/** Starting point X and Y values for first brick */
private static final double START_X = (Breakout.WIDTH - ((Breakout.NBRICKS_PER_ROW * BRICK_WIDTH) + (Breakout.NBRICKS_PER_ROW - 1) * BRICK_SEP)) / 2 ;
private static final int START_Y = Breakout.BRICK_Y_OFFSET;
private boolean special = false;
private static int bricksRemaining = 0;
private RandomGenerator rgen = RandomGenerator.getInstance();
}Paddle类import acm.graphics.*;
import java.awt.*;
public class Paddle extends GCompound {
public Paddle() {
fill = new GRect(0,0, PADDLE_STARTING_WIDTH, PADDLE_HEIGHT);
fill.setFilled(true);
fill.setColor(Color.GRAY);
light = new GRect(0,PADDLE_HEIGHT/2, PADDLE_STARTING_WIDTH, PADDLE_HEIGHT/2);
light.setFilled(true);
light.setColor(new Color(0,0,0,50));
add(fill);
add(light);
this.setLocation((Breakout.WIDTH - PADDLE_STARTING_WIDTH) / 2, PADDLE_Y);
}
public void followMouse(double mouseX) {
if (mouseX < paddleWidth/2) {
this.setLocation(0, PADDLE_Y);
} else if (mouseX > Breakout.WIDTH - paddleWidth/2) {
this.setLocation(Breakout.WIDTH - paddleWidth,PADDLE_Y);
} else {
this.move(mouseX-paddleWidth/2 - this.getX(), 0);
}
}
public double collide(double ballX, double r) {
double paddleX = this.getX();
setColor(Color.RED);
double hitSpot = ballX - paddleX;
double maxPaddle = paddleWidth + r;
double minPaddle = -r;
double paddleRange = maxPaddle - minPaddle;
double minAngle = 160;
double maxAngle = 20;
double angleRange = maxAngle - minAngle;
double newDirection = ((hitSpot * angleRange) / paddleRange) + minAngle;
return newDirection;
}
private void widen() {
paddleWidth += 30;
this.move(-15, 0);
fill.setSize(paddleWidth, PADDLE_HEIGHT);
light.setSize(paddleWidth, PADDLE_HEIGHT / 2);
}
public void checkForPrize(Prize prize, double x, double y) {
if ((y >= PADDLE_Y) && (y <= PADDLE_Y + PADDLE_HEIGHT) && x > this.getX() && x < this.getX() + paddleWidth) {
prize.pickUp();
this.widen();
}
}
/** Dimensions of the paddle */
private static final int PADDLE_STARTING_WIDTH = 60;
private static final int PADDLE_HEIGHT = 15;
private GRect fill;
private GRect light;
private int paddleWidth = PADDLE_STARTING_WIDTH;
/** Dimensions of the paddle */
public static final int PADDLE_Y = Breakout.HEIGHT - Breakout.PADDLE_Y_OFFSET;
}Prize类import acm.graphics.*;
import java.awt.*;
public class Prize extends GOval {
public Prize(double x, double y, Color color, Paddle pad) {
super(x + Brick.BRICK_HEIGHT/2, y + Brick.BRICK_WIDTH/2, PRIZE_RADIUS * 2, PRIZE_RADIUS * 2);
setFilled(true);
setColor(color);
paddle = pad;
}
public void drop() {
this.move(0, PRIZE_SPEED);
paddle.checkForPrize(this,this.getX()+PRIZE_RADIUS,this.getY()+PRIZE_RADIUS*2);
}
public boolean leftScreen() {
if (this.getY() - PRIZE_RADIUS > Breakout.HEIGHT || pickedUp == true) {
return true;
} else {
return false;
}
}
public void pickUp() {
pickedUp = true;
}
private boolean pickedUp = false;
private Paddle paddle;
public static final int PRIZE_RADIUS = 5;
public static final int PRIZE_SPEED = 10;
}发布于 2012-01-16 16:19:15
我没有尝试进入高水平的设计,你的游戏,但以下是一些细节,你可能想要修复。
if (won == true)可以写成if (won)gameOverLabel.setLabel((lives == 3)?"HOLY SHIT PERFECT SCORE!!!":"WOO YOU WON!!!");instanceof通常是一件坏事。decreaseBricks()方法中可能有一个问题:您可能希望按另一个顺序进行测试,并以其他顺序分隔,您可以简单地使用return (bricksRemaining > 0) (可能返回bricksRemaining会工作,但我只是不记得它在Java中是如何工作的)leftScreen()方法可以是return <your big expression>;。发布于 2012-01-17 02:08:36
如果没有多余的"this."s,您的代码将更加可读性。
private void checkCollision(Ball ball) {
double r = ball.getRadius();
//// If you moved the ball BEFORE checkCollision, you wouldn't need
//// to move it here, and move it back only to move it again, AFTER.
ball.move();
double x = ball.getX();
double y = ball.getY();
//// There's no point in finishing this function after removeLife,
//// so test for it up-front and get it out of the way.
// less life
if (y+r >= HEIGHT) {
removeLife();
ball.restart();
return;
}
ball.moveBack();
GObject collider = null;
int dir = 0;
// Check walls + keep within bounds (keep from getting stuck)
if (x-r <= 0) {
ball.setLocation(r,y);
ball.changeDirection(1);
} else if (x+r >= WIDTH) {
ball.setLocation(WIDTH-r,y);
ball.changeDirection(1);
}
if (y-r <= 0) {
ball.setLocation(x,r);
ball.changeDirection(2);
}
//Check elements如果建立了“区域”(y值范围),那么这个“检查元素”部分可能要快得多,在该区域中,您希望与砖块或桨碰撞,并且只测试球在其区域内时是否与这些东西发生碰撞。
为每个给定的参数对调用一次getElementAt,并重用结果,执行起来更快,更容易阅读。
你不会同时碰到两块砖的情况,这些砖是并排的,自上而下的,或者是触角的。您只处理检测到的第二次碰撞。但是,如果您修复了这个问题,请不要忘记,同一块砖也可能在多个方向上匹配。
你只需测试一个或两个方向的砖(S),球目前正每个方向一个+/-x (除非方向是90)和+/-y。
由于砖块没有移动,根据最初放置砖块的位置,编写测试与之碰撞的砖块的功能可能更容易。那么,将你的砖块放在一个二维数组中会有帮助。然后,您可以单独处理桨,而忘记了GLabels。
无论是分区还是特殊外壳,砖块碰撞都免除了检查(对撞机安装的砖块)的需要,这是很好避免的。
向上移动时,不需要检查是否有划桨。
在这里和这里调用时,使用consts或枚举,而不是1或2。
public void changeDirection(int xory) {
if (xory == 1) direction -= (direction-270)*2;
if (xory == 2) direction -= (direction-180)*2;
}正如已经评论过的,这些都是奇怪的方向表达式。它们在技术上对x轴反射和y轴反射是正确的,但却是一种非常迂回的表达方式:
direction = ((xory==1) ? 180 : 0) - direction;从未打过电话:
public static int getBricksRemaining() {
return bricksRemaining;
}bricksRemaining,decreaseBricks,et.阿尔。应该由主要的突破类来处理,这样砖块就不需要依靠球了。
//// These should be static consts.
double minAngle = 160;
double maxAngle = 20;
double angleRange = maxAngle - minAngle;
public void checkForPrize(Prize prize, double x, double y) {
if ((y >= PADDLE_Y) && (y <= PADDLE_Y + PADDLE_HEIGHT) && x > getX() && x < getX() + paddleWidth) {
prize.pickUp();
//// Moving the next statement into Prize::pickUp makes the purpose of the Prize
//// more self-evident within its class and would allow different prizes to have
//// different effects.
widen();
}
}发布于 2012-01-17 10:12:45
把Ball类中的changeDirection分解为两种方法,并简化每一种方法中的数学。有点像
public void bounceVertical(){
direction = 540 - direction;
}
public void bounceHorizontal(){
direction = 360 - direction;
}再加上其他答卷人提出的好建议。
https://codereview.stackexchange.com/questions/7865
复制相似问题