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

剛開始學,還有很多地方實現的不完善;

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM