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) 编辑 收藏 举报