初始需要定義的一些常量,和使用的庫。
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 */ }
剛開始學,還有很多地方實現的不完善;


