java语言的科学与艺术-编程练习---打砖块游戏

初始需要定义的一些常量,和使用的库。

 1 import acm.graphics.*;
 2 import acm.program.*;
 3 import acm.util.*;
 4 
 5 import java.applet.*;
 6 import java.awt.*;
 7 import java.awt.event.*;
 8 
 9 public class Breakout extends GraphicsProgram {
10 
11 /** Width and height of application window in pixels */
12     public static final int APPLICATION_WIDTH = 400;
13     public static final int APPLICATION_HEIGHT = 600;
14 
15 /** Dimensions of game board (usually the same) */
16     private static final int WIDTH = APPLICATION_WIDTH;
17     private static final int HEIGHT = APPLICATION_HEIGHT;
18 
19 /** Dimensions of the paddle */
20     private static final int PADDLE_WIDTH = 60;
21     private static final int PADDLE_HEIGHT = 10;
22 
23 /** Offset of the paddle up from the bottom */
24     private static final int PADDLE_Y_OFFSET = 30;
25 
26 /** Number of bricks per row */
27     private static final int NBRICKS_PER_ROW = 10;
28 
29 /** Number of rows of bricks */
30     private static final int NBRICK_ROWS = 10;
31 
32 /** Separation between bricks */
33     private static final int BRICK_SEP = 4;
34 
35 /** Width of a brick */
36     private static final int BRICK_WIDTH =
37       (WIDTH - (NBRICKS_PER_ROW - 1) * BRICK_SEP) / NBRICKS_PER_ROW;
38 
39 /** Height of a brick */
40     private static final int BRICK_HEIGHT = 8;
41 
42 /** Radius of the ball in pixels */
43     private static final int BALL_RADIUS = 10;
44 
45 /** Offset of the top brick row from the top */
46     private static final int BRICK_Y_OFFSET = 70;
47 
48 /** Number of turns */
49     private static final int NTURNS = 3;

第一步:创建游戏需要的砖块,砖块的行数和每行的数量都是确定的(NBRICK_ROWS,NBRICKS_PER_ROW),最上一排砖块距窗口顶部距离 (BRICK_Y_OFFSET)。然后是颜色的填充,利用行数判断所需的颜色。

 1 for(int i = 0; i < NBRICK_ROWS; i++){
 2             for(int j = 0; j < NBRICKS_PER_ROW; j++){
 3                 GRect brick = new GRect(0 + j * (BRICK_WIDTH + BRICK_SEP), BRICK_Y_OFFSET + i * (BRICK_HEIGHT + BRICK_SEP), BRICK_WIDTH, BRICK_HEIGHT);
 4                 brick.setFilled(true);
 5                 if(i < 2){
 6                     brick.setColor(Color.RED);
 7                 } else if(i < 4){
 8                     brick.setColor(Color.ORANGE);
 9                 } else if(i < 6){
10                     brick.setColor(Color.YELLOW);
11                 } else if(i < 8){
12                     brick.setColor(Color.GREEN);
13                 } else {
14                     brick.setColor(Color.CYAN);
15                 }
16                 add(brick);
17             }
18         }

第二步:创建游戏所需的挡板

挡板的大小位置都是确定的;

1 private void createPaddle(){
2         /* HEIGHT 定义的是窗口高度所以要使用getHeight();*/
3         GRect paddle = new GRect((WIDTH - PADDLE_WIDTH) / 2, getHeight() - PADDLE_HEIGHT - PADDLE_Y_OFFSET, PADDLE_WIDTH, PADDLE_HEIGHT);
4         paddle.setFilled(true);
5         paddle.setColor(Color.BLACK);
6         add(paddle);
7         
8     }

比较有挑戓性的部分是能够让挡板随着鼠标移劢。挡板只需要在x轴方向上移动。

加入鼠标侦听事件:addMouseListeners9();

和鼠标拖拽事件的代码:

 1 /* 单击鼠标事件 */
 2 public void mousePressed(MouseEvent e){
 3     last = new GPoint(e.getPoint());
 4     gobj = getElementAt(last);
 5 }
 6 /* 鼠标拖动事件 */
 7 public void mouseDragged(MouseEvent e){
 8     if(gobj != null){
 9         gobj.move(e.getX() - last.getX(), 0);
10         last = new GPoint(e.getPoint());
11     }
12 }

这样就可以使用鼠标拖动挡板了。但是还需要求挡板能移动出游戏的边界,所以加入判断条件

(gobj.getX() > 0 || e.getX() - last.getX() > 0) && (gobj.getX() + gobj.getWidth() < getWidth() || e.getX() - last.getX() < 0)

当挡板移动到左边界时,鼠标需要向右移动才有效,当挡板移动到右边界时,鼠标需要向左移动才有效。

1 /* 鼠标拖动事件 */
2 public void mouseDragged(MouseEvent e){
3     if(gobj != null && (gobj.getX() > 0 || e.getX() - last.getX() > 0) && (gobj.getX() + gobj.getWidth() < getWidth() || e.getX() - last.getX() < 0)){
4         gobj.move(e.getX() - last.getX(), 0);
5         last = new GPoint(e.getPoint());
6     }
7 }

第三步:创建一个小球,使其在墙内反弹;

程序需要记录小球的速度。它由两个独立分量组成,你们可以按照下面的例子声明实例变量:
private double vx, vy;
速度分量表示在每个时间区间位置的变化量。一开始,小球向下运劢,初始速度vy可以设
为+3.0(在Java 中,y 值向屏幕往下增加)。如果每个回合小球的路线都相同,游戏会很无
聊,因此,vx 分量的值应该随机选取。

1. 声明一个实例变量rgen, 随机数生成器:
private RandomGenerator rgen = RandomGenerator.getInstance();

2. 初始化vx 变量:
vx = rgen.nextDouble(1.0, 3.0);
if (rgen.nextBoolean(0.5)) vx=-vx;
返段代码生成一个1.0到3.0间的双浮点型随机数赋给 vx,并按0.5 的概率将速度
取反。这种算法比调用
nextDouble(-3.0, +3.0)
好很多,后者可能会出现小球几乎垂直下落的情况,返对玩家来说太过于简单。

然后是让小球在墙壁之间来回弹跳,先忽略挡板和砖块的影响。

 1 /* 创建一个反弹的小球 */
 2     private void createBall(){
 3         GOval ball = new GOval((getWidth() - 2 * BALL_RADIUS) / 2, (getHeight() - 2 * BALL_RADIUS) / 2, BALL_RADIUS, BALL_RADIUS);
 4         ball.setFilled(true);
 5         add(ball);
 6         vy = 3.0;
 7         vx = rgen.nextDouble(1.0, 3.0); 
 8         if(rgen.nextBoolean(0.5)) vx = -vx;
 9         while(true){
10             ball.move(vx,vy);
11             pause(PAUSE_TIME);
12             if(ball.getX() < 0 || ball.getX() + 2 * BALL_RADIUS > getWidth()) vx = -vx;
13             if(ball.getY() < 0 || ball.getY() + 2 * BALL_RADIUS > getHeight()) vy = -vy;
14         }
15     }

 

第四步:碰撞检测
现在到了有趣的部分。为了使突破游戏更真实,需要判断小球是否和屏幕中其它物体产生了碰撞。

这里我为了让其他方法能获取ball和paddle的数据定义两个实例变量:

private GOval BALL;
private GRect PADDLE;

然后分别在createBall和createPaddle中加入 BALL = ball 和 PADDLE = paddle;

创建碰撞检测的方法

 1 private GObject getCollidingObject(){
 2         /* 小球的正切正方形的四个顶点 */
 3         double x1 = BALL.getX();
 4         double y1 = BALL.getY();
 5         double x2 = BALL.getX() + 2 * BALL_RADIUS;
 6         double y2 = BALL.getY();
 7         double x3 = BALL.getX() + 2 * BALL_RADIUS;
 8         double y3 = BALL.getY() + 2 * BALL_RADIUS;
 9         double x4 = BALL.getX();
10         double y4 = BALL.getY() + 2 * BALL_RADIUS;
11         if(getElementAt(x1,y1) != null){
12             return getElementAt(x1,y1);
13         } else if(getElementAt(x2,y2) != null){
14             return getElementAt(x2,y2);
15         } else if(getElementAt(x3,y3) != null){
16             return getElementAt(x3,y3);
17         } else if(getElementAt(x4,y4) != null){
18             return getElementAt(x4,y4);
19         } else{
20             return null;
21         }
22     }

是用小球的正切正方形的四个点进行判断十分碰到挡板或砖块;碰到挡板垂直反弹,碰到砖块,消除方块并反弹。

 

if(collider == PADDLE){
vy = -vy;
} else if (collider != null){
vy = -vy;
remove(collider);
n++;

}

第五步:尾声

1.加入游戏获胜和失败的判断条件;

2.加入获胜和失败的反馈;

3.加入回合数;

 完整代码:

/*
 * File: Breakout.java
 * -------------------
 * Name:
 * Section Leader:
 * 
 * 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.*;

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 = 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: run() */
/** Runs the Breakout program. */
    public void run() {
        createAllBrick(NBRICK_ROWS, NBRICKS_PER_ROW);
        createPaddle();
        int i = 0;
        while(i < NTURNS){
            createBall();
            i++;
            if (isWin == 1) break;
        }
        if(i == 3){
            add(new GLabel("YOU LOSE", getWidth() / 2, getHeight() / 2));
        }
    }
    /*
     * 根据提供的数据创建游戏需要的砖块;
     */
    private void createAllBrick(int row, int rank){
        for(int i = 0; i < row; i++){
            for(int j = 0; j < rank; j++){
                GRect brick = new GRect(0 + j * (BRICK_WIDTH + BRICK_SEP), BRICK_Y_OFFSET + i * (BRICK_HEIGHT + BRICK_SEP), BRICK_WIDTH, BRICK_HEIGHT);
                brick.setFilled(true);
                if(i < 2){
                    brick.setColor(Color.RED);
                } else if(i < 4){
                    brick.setColor(Color.ORANGE);
                } else if(i < 6){
                    brick.setColor(Color.YELLOW);
                } else if(i < 8){
                    brick.setColor(Color.GREEN);
                } else {
                    brick.setColor(Color.CYAN);
                }
                add(brick);
            }
        }
    }
    /*
     * 创建一个可以用鼠标拖动的挡板;
     * 挡板不能被拖出屏幕边界;
     */
    private void createPaddle(){
        /* HEIGHT 定义的是窗口高度所以要使用getHeight();*/
        GRect paddle = new GRect((WIDTH - PADDLE_WIDTH) / 2, getHeight() - PADDLE_HEIGHT - PADDLE_Y_OFFSET, PADDLE_WIDTH, PADDLE_HEIGHT);
        PADDLE = paddle;
        paddle.setFilled(true);
        paddle.setColor(Color.BLACK);
        add(paddle);
        addMouseListeners();
    }
    /* 单击鼠标事件 */
    public void mousePressed(MouseEvent e){
        last = new GPoint(e.getPoint());
        gobj = getElementAt(last);
    }
    /* 鼠标拖动事件 */
    public void mouseDragged(MouseEvent e){
        if(gobj != null && (gobj.getX() > 0 || e.getX() - last.getX() > 0) && (gobj.getX() + gobj.getWidth() < getWidth() || e.getX() - last.getX() < 0)){
            gobj.move(e.getX() - last.getX(), 0);
            last = new GPoint(e.getPoint());
        }
    }
    /* 创建一个反弹的小球 */
    private void createBall(){
        GOval ball= new GOval((getWidth() - 2 * BALL_RADIUS) / 2, (getHeight() - 2 * BALL_RADIUS) / 2, BALL_RADIUS, BALL_RADIUS);
        ball.setFilled(true);
        BALL = ball;
        add(ball);
        int n = 0; //记录消除的砖块数目;
        vy = 3.0;
        vx = rgen.nextDouble(1.0, 3.0); 
        if(rgen.nextBoolean(0.5)) vx = -vx;
        while(true){
            ball.move(vx,vy);
            GObject collider = getCollidingObject();
            pause(PAUSE_TIME);
            if(collider == PADDLE){
                vy = -vy;
            } else if (collider != null){
                vy = -vy;
                remove(collider);
                n++;
                if (n == 100){
                    add(new GLabel("YOU WIN!", getWidth() /2, getHeight() / 2));//显示消息;
                    remove(ball);
                    isWin = 1;
                    break;
                }
            }
            if(ball.getX() < 0 || ball.getX() + 2 * BALL_RADIUS > getWidth()) vx = -vx;
            if(ball.getY() < 0) vy = -vy;
            if(ball.getY() + 2 * BALL_RADIUS > getHeight()){
                remove(ball);
                break;
            }
        }
    }
    private GObject getCollidingObject(){
        /* 小球的正切正方形的四个顶点 */
        double x1 = BALL.getX();
        double y1 = BALL.getY();
        double x2 = BALL.getX() + 2 * BALL_RADIUS;
        double y2 = BALL.getY();
        double x3 = BALL.getX() + 2 * BALL_RADIUS;
        double y3 = BALL.getY() + 2 * BALL_RADIUS;
        double x4 = BALL.getX();
        double y4 = BALL.getY() + 2 * BALL_RADIUS;
        if(getElementAt(x1,y1) != null){
            return getElementAt(x1,y1);
        } else if(getElementAt(x2,y2) != null){
            return getElementAt(x2,y2);
        } else if(getElementAt(x3,y3) != null){
            return getElementAt(x3,y3);
        } else if(getElementAt(x4,y4) != null){
            return getElementAt(x4,y4);
        } else{
            return null;
        }
    }
    /* 创建一个随机数生成器 */
    private RandomGenerator rgen = RandomGenerator.getInstance();
    /* 小球移动的实例变量 */
    private double vx, vy; 
    /* 小球移动的暂停时间 */
    private int PAUSE_TIME = 20;
    private GOval BALL;
    private GRect PADDLE;
    private int isWin = 0;/* 是否获胜 */
    private GObject gobj; /* The object being dragged */
    private GPoint last; /* The last mouse position */
    
}

刚开始学,还有很多地方实现的不完善;

 

 

posted on 2012-12-17 14:49  mybluecode  阅读(4461)  评论(2编辑  收藏  举报