这个是学完面向对象之后写的,也是跟着老师写的,跟飞扬小鸟不同的是,写这个的时候,对java的语法结构有了很多的了解,对java有了一定的语感,对java的基础知识和数据类型也有了系统的认识。
写的时候,首先跟着老师分析了俄罗斯方块的业务需求,然后根据业务需求设计出大致的类和方法,在写游戏的时候,根据具体的需求继续添加设计需要的方法,下面是我自己根据业务需求文档写的程序设计大致结构:
俄罗斯方块 对象: Cell //组成俄罗斯方块的基础一个一个小格子 属性: int row;//格子所在行 int col;//格子所在列 BufferedImage image;//格子所在位置填充的图片 方法: /*get set方法*/ getRow() getCol() getImage() setRow(row) setCol(col) setImage(Image) /*格子下落方法*/ drop() /*格子左移*/ moveLeft(){ /*格子右移*/ moveRight(){ Tetromino //抽象类,定义4格方块 属性: Cell[] cells //组成4格方块的格子 state //变形用数组 type //数组列长 方法: Tetromino tetromino()//工厂方法:生成随机一种4格方块 softDrop()//软下落 moveLeft()//四格方块左移 moveRight()//四格方块右移 rotate()//四格方块变形 (T S J L I Z O 类继承于Tetromino类) Tetris //俄罗斯方块类,整个游戏的运行和操作 属性:tetromino//正在下落的4格方块 nextOne//即将进场的4个方块 score//分数 lines//总共消除的行数 wall[][]//游戏场地 ROWS//游戏场地的总行数 COLS//游戏场地的总列数 方法: /*四格方块的运动方法*/ softDropAction() hardDropAction() moveLeftAction() moveRightAction() /*消除方法 及 消除一行的方法*/ destroy() deletLine() /*判断方法*/ fullLines() //判断一行满了没有 outOfWall()//判断4格方块出界了没有 coincide()//判断4个方块有没有重合 isgameOver()//判断游戏结束了没有 canDrop()//判断4个方块还能不能下落
下面就是具体的代码了
首先是Cell类:
package com.tarena.tetris; import java.awt.image.BufferedImage; /** * 组成俄罗斯方块的格子 * * @author * */ class Cell { /** * 格子所在的列、行、图片 */ public int col; public int row; BufferedImage image; public Cell(int row, int col, BufferedImage image) { super(); this.col = col; this.row = row; this.image = image; } public int getCol() { return col; } public void setCol(int col) { this.col = col; } public int getRow() { return row; } public void setRow(int row) { this.row = row; } public BufferedImage getImage() { return image; } public void setImage(BufferedImage image) { this.image = image; } //下落 public void drop(){
这个是Tetromino类:
package com.tarena.tetris; import java.util.Random; /** * 四格方块类 * * @author * */ class Tetromino { Cell[] cells = new Cell[4]; int type;// 方块可变换的种类 int[][] state;// 用于变形的数据 /** * 工厂方法,随机生成方块 生成[0,7)的随机数,用t接受,用来决定生成四格方块的类型 */ public Tetromino randomOne() { Random r = new Random(); int t = r.nextInt(7); switch (t) { case 0: return new I(); case 1: return new J(); case 2: return new L(); case 3: return new O(); case 4: return new S(); case 5: return new Z(); case 6: return new T(); } return null; } int ss = 10;// 用语变形取余数循环 /* * 变形 */ public void rotateRight() { ss++; Cell o = cells[0]; int row = o.getRow(); int col = o.getCol(); cells[1].setRow(row + state[ss % type][2]); cells[1].setCol(col + state[ss % type][3]); cells[2].setRow(row + state[ss % type][4]); cells[2].setCol(col + state[ss % type][5]); cells[3].setRow(row + state[ss % type][6]); cells[3].setCol(col + state[ss % type][7]); } public void rotatLeft() { ss--; Cell o = cells[0]; int row = o.getRow(); int col = o.getCol(); cells[1].setRow(row + state[ss % type][2]); cells[1].setCol(col + state[ss % type][3]); cells[2].setRow(row + state[ss % type][4]); cells[2].setCol(col + state[ss % type][5]); cells[3].setRow(row + state[ss % type][6]); cells[3].setCol(col + state[ss % type][7]); } /* * 下落 右移 左移 */ public void softDrop() { for (Cell cell : cells) { cell.drop(); } } public void moveRight() { for (Cell cell : cells) { cell.moveRight(); } } public void moveLeft() { for (Cell cell : cells) { cell.moveLeft(); } } } class I extends Tetromino { public I() { cells[0] = new Cell(0, 4, Tetris.I); cells[1] = new Cell(0, 3, Tetris.I); cells[2] = new Cell(0, 5, Tetris.I); cells[3] = new Cell(0, 6, Tetris.I); type = 2; state = new int[][] { { 0, 0, 0, -1, 0, 1, 0, 2 }, { 0, 0, -1, 0, 1, 0, 2, 0 } }; } } class T extends Tetromino { public T() { cells[0] = new Cell(0, 4, Tetris.T); cells[1] = new Cell(0, 3, Tetris.T); cells[2] = new Cell(0, 5, Tetris.T); cells[3] = new Cell(1, 4, Tetris.T); type = 4; state = new int[][] { { 0, 0, 0, 1, 0, -1, -1, 0 }, { 0, 0, 1, 0, -1, 0, 0, 1 }, { 0, 0, 0, -1, 0, 1, 1, 0 }, { 0, 0, -1, 0, 1, 0, 0, -1 } }; } } class J extends Tetromino { public J() { cells[0] = new Cell(0, 4, Tetris.J); cells[1] = new Cell(0, 3, Tetris.J); cells[2] = new Cell(0, 5, Tetris.J); cells[3] = new Cell(1, 5, Tetris.J); type = 4; state = new int[][] { { 0, 0, 0, 1, 0, -1, -1, -1 }, { 0, 0, 1, 0, -1, 0, -1, 1 }, { 0, 0, 0, -1, 0, 1, 1, 1 }, { 0, 0, -1, 0, 1, 0, 1, -1 } }; } } class L extends Tetromino { public L() { cells[0] = new Cell(0, 4, Tetris.L); cells[1] = new Cell(0, 3, Tetris.L); cells[2] = new Cell(0, 5, Tetris.L); cells[3] = new Cell(1, 3, Tetris.L); type = 4; state = new int[][] { { 0, 0, 0, 1, 0, -1, -1, 1 }, { 0, 0, 1, 0, -1, 0, 1, 1 }, { 0, 0, 0, -1, 0, 1, 1, -1 }, { 0, 0, -1, 0, 1, 0, -1, -1 } }; } } class O extends Tetromino { public O() { cells[0] = new Cell(0, 4, Tetris.O); cells[1] = new Cell(0, 5, Tetris.O); cells[2] = new Cell(1, 4, Tetris.O); cells[3] = new Cell(1, 5, Tetris.O); type = 2; state = new int[][] { { 0, 0, 0, 1, 1, 0, 1, 1 }, { 0, 0, 0, 1, 1, 0, 1, 1 } }; } } class S extends Tetromino { public S() { cells[0] = new Cell(0, 4, Tetris.S); cells[1] = new Cell(0, 5, Tetris.S); cells[2] = new Cell(1, 3, Tetris.S); cells[3] = new Cell(1, 4, Tetris.S); type = 2; state = new int[][] { { 0, 0, 0, 1, 1, -1, 1, 0 }, { 0, 0, -1, 0, 1, 1, 0, 1 } }; } } class Z extends Tetromino { public Z() { cells[0] = new Cell(1, 4, Tetris.Z); cells[1] = new Cell(0, 3, Tetris.Z); cells[2] = new Cell(0, 4, Tetris.Z); cells[3] = new Cell(1, 5, Tetris.Z); type = 2; state = new int[][] { { 0, 0, -1, -1, -1, 0, 0, 1 }, { 0, 0, -1, 1, 0, 1, 1, 0 } }; } }
最后是游戏类也即Tetris类:
package com.tarena.tetris; import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.image.BufferedImage; import java.util.Arrays; import javax.imageio.ImageIO; import javax.swing.JFrame; import javax.swing.JPanel; /** * 俄罗斯方块的主体程序 * * @author 锟铰斤拷 * */ @SuppressWarnings("serial") public class Tetris extends JPanel { /** * 各种图片数据 */ protected static BufferedImage I; protected static BufferedImage J; protected static BufferedImage L; protected static BufferedImage O; protected static BufferedImage S; protected static BufferedImage T; protected static BufferedImage Z; protected static BufferedImage background; protected static BufferedImage gameOver; private static final int COLS = 10; private static final int ROWS = 20; /** * 消行得分数据 */ private static int lines = 0; private static int score = 0; protected Tetromino tetromino = new Tetromino().randomOne();// 下落的方块 protected Tetromino nextOne = new Tetromino().randomOne();//下一个方块 private Cell wall[][] = new Cell[ROWS][COLS];// 墙 /** * 游戏状态数据 */ private static int state; private static final int PLAYING = 0; private static final int GAMEOVER = 1; private static final int PAUSE = 2; private static final String[] State = { "[P]Pause", "[S]Restart", "[C]Continue" }; /** * 方块下落速度控制数据 */ private static int speed = 10; private int index = -5; private int ss = 0; static { try { I = ImageIO.read(Tetris.class.getResource("I.png")); J = ImageIO.read(Tetris.class.getResource("J.png")); L = ImageIO.read(Tetris.class.getResource("L.png")); O = ImageIO.read(Tetris.class.getResource("O.png")); S = ImageIO.read(Tetris.class.getResource("S.png")); T = ImageIO.read(Tetris.class.getResource("T.png")); Z = ImageIO.read(Tetris.class.getResource("Z.png")); background = ImageIO.read(Tetris.class.getResource("tetris.png")); gameOver = ImageIO.read(Tetris.class.getResource("game-over.png")); } catch (Exception e) { e.printStackTrace(); } } /** * 定义action方法控制游戏运行 */ public void action() { /** * 按键控制游戏 */ KeyListener l = new KeyAdapter() { public void keyPressed(KeyEvent e) { int key = e.getKeyCode(); /* * 各种按键功能 */ if (key == KeyEvent.VK_Q) { System.exit(0); } // if (key == KeyEvent.VK_P) { // state=PAUSE; // return; // } // if (state == GAMEOVER) { // if (key == KeyEvent.VK_S) { // restart(); // // } // // } if (state == PAUSE) { if (key == KeyEvent.VK_C) { state = PLAYING; }else{ return; } } switch (key) { case KeyEvent.VK_DOWN: softDropAction(); break; case KeyEvent.VK_LEFT: moveLeftAction(); break; case KeyEvent.VK_RIGHT: moveRightAction(); break; case KeyEvent.VK_UP: rotateAction(); break; case KeyEvent.VK_SPACE: hardDropAcation(); break; case KeyEvent.VK_P: state=PAUSE; break; case KeyEvent.VK_S: if(state==GAMEOVER){ restart(); } break; } repaint(); } }; this.addKeyListener(l); this.requestFocus(); while (true) { if (speed > 1 && score - ss >= 10) { ss += 10; speed--; } if (index++ % speed == 0&&state==PLAYING&&!isGameOver()) { softDropAction(); } try { Thread.sleep(100); } catch (Exception e1) { e1.printStackTrace(); } repaint(); } } protected void restart() { wall = new Cell[ROWS][COLS]; tetromino = new Tetromino().randomOne(); nextOne = new Tetromino().randomOne(); lines = 0; score = 0; speed = 10; ss = 0; index = -5; state=PLAYING; } /* * 快速下落 */ protected void hardDropAcation() { while (canDrop()) { tetromino.softDrop(); } inWall(); destroyLines(); if (!isGameOver()) { tetromino = nextOne; nextOne = new Tetromino().randomOne(); } } /* * 实现方块的旋转 */ protected void rotateAction() { tetromino.rotateRight(); if (outOfWall() || coincide()) { tetromino.rotatLeft(); } } /* * 右移 */ protected void moveRightAction() { tetromino.moveRight(); if (outOfWall() || coincide()) { tetromino.moveLeft(); } } /* * 判断方块有没有和墙上的格子重合 */ private boolean coincide() { Cell[] cells = tetromino.cells; for (Cell cell : cells) { int row = cell.getRow(); int col = cell.getCol(); if (wall[row][col] != null) { return true; } } return false; } /* * 左移 */ protected void moveLeftAction() { tetromino.moveLeft(); if (outOfWall() || coincide()) { tetromino.moveRight(); } } /* *判断出没出界 */ private boolean outOfWall() { Cell[] cells = tetromino.cells; for (Cell cell : cells) { int row = cell.getRow(); int col = cell.getCol(); if (row > ROWS - 1 || row < 0 || col > COLS - 1 || col < 0) { return true; } } return false; } /* * 缓慢下落 */ protected void softDropAction() { if (canDrop()) { tetromino.softDrop(); } else { inWall(); destroyLines(); if (!isGameOver()) { tetromino = nextOne; nextOne = new Tetromino().randomOne(); } else { state = GAMEOVER; } } } /* * 将方块放到墙上 */ private void inWall() { Cell[] cells = tetromino.cells; for (Cell cell : cells) { int row = cell.getRow(); int col = cell.getCol(); // BufferedImage image=cell.getImage(); wall[row][col] = cell; } } /* * 判断游戏是否结束 */ private boolean isGameOver() { Cell[] cells = nextOne.cells; for (Cell cell : cells) { int row = cell.getRow(); int col = cell.getCol(); if (wall[row][col] != null) { state=GAMEOVER; return true; } } return false; } /** * 判断是否还能下落 * * @return */ private boolean canDrop() { Cell[] cells = tetromino.cells; for (Cell cell : cells) { int row = cell.getRow(); int col = cell.getCol(); if (row >= ROWS - 1 || wall[row + 1][col] != null) { return false; } } return true; } /* * 消行 得分 */ int[] scores = { 0, 1, 10, 50, 100 }; private void destroyLines() { int line = 0; for (int row = 0; row < ROWS; row++) { if (fullLine(row)) { deleteLine(row); line++; } } lines += line; score += scores[line]; } /* * 判断一行是否满了 */ private boolean fullLine(int row) { for (int i = 0; i < COLS; i++) { if (wall[row][i] == null) { return false; } } return true; } /* * 删除一行 */ private void deleteLine(int row) { for (int i = row; i > 0; i--) { wall[i] = Arrays.copyOf(wall[i - 1], COLS); } Arrays.fill(wall[0], null); } /** * 重写绘图方法 */ public void paint(Graphics g) { g.drawImage(background, 0, 0, null);//背景图 g.translate(15, 15);// 换坐标 paintWall(g); paintTetris(g); paintNextOne(g); paintScores(g); paintState(g); } /* * 绘出游戏所处的状态 */ private void paintState(Graphics g) { String str = State[state]; g.setColor(new Color(0x667799)); Font font = g.getFont(); Font f = new Font(font.getName(), font.getStyle(), 30); g.setFont(f); g.drawString(str, 290, 160 + 56 + 56); if (state == GAMEOVER) { g.translate(-15, -15); g.drawImage(gameOver, 0, 0, null); } } /* * 画出得分 */ private void paintScores(Graphics g) { String str = "SCORE:" + score; g.setColor(new Color(0x667799)); Font font = g.getFont(); Font f = new Font(font.getName(), font.getStyle(), 30); g.setFont(f); g.drawString(str, 290, 160); str = "LINES:" + lines; g.drawString(str, 290, 160 + 56); } /** * 画出下一个出场的方块 * * @param g */ private void paintNextOne(Graphics g) { Cell[] cells = nextOne.cells; for (Cell cell : cells) { int x = (cell.getCol() + 10) * SIZE; int y = (cell.getRow() + 1) * SIZE; BufferedImage image = cell.getImage(); g.drawImage(image, x, y, null); } } static final int SIZE = 26; /** * 画出正在下落的方块 * * @param g */ private void paintTetris(Graphics g) { Cell[] cells = tetromino.cells; for (Cell cell : cells) { int x = cell.getCol() * SIZE; int y = cell.getRow() * SIZE; BufferedImage image = cell.getImage(); g.drawImage(image, x, y, null); } } /** * 画出墙 * * @param g */ private void paintWall(Graphics g) { for (int row = 0; row < ROWS; row++) { for (int col = 0; col < COLS; col++) { int y = row * SIZE; int x = col * SIZE; g.drawRect(x, y, SIZE, SIZE); if (wall[row][col] != null) { Cell cell = wall[row][col]; BufferedImage image = cell.getImage(); g.drawImage(image, x, y, null); } } } } public static void main(String[] args) { JFrame frame = new JFrame(); frame.setSize(525, 550); Tetris panel = new Tetris(); frame.add(panel); frame.setLocationRelativeTo(null); frame.setUndecorated(true); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); panel.action(); } }
这一次,知道加注释了,很多地方注释写的不详细,但是因为格式、命名规范很多,就算很久没看了,依然可以很轻松看懂每个方法、每个变量的具体功能,也不在像第一次写飞扬小鸟那样很吃力很生涩,借助于功能分析文档,基本上可以独立完成全部代码,而不用参照老师的代码。