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