TETRIS 项目开发笔记
java学习一个月了,没有什么进展,期间又是复习Linux,又是看Android,瞻前顾后,感觉自己真的是贪得无厌,
学习的东西广而不精,所以写出的文章也就只能泛泛而谈。五一小长假,哪里都没有去,也不想去,刚刚无聊刷新了下
朋友圈,朋友们不是在玩,就是在吃,突然一下子感觉自己老了许多。岁月真是把杀猪刀,夺走了我们的青春,但却无
法夺走我们的激情。
好好复习了!
在老师引领下,算是把人生中的第一个Java项目敲完了,感觉对于学习OOP的朋友,应该有所帮助,先做个笔记吧
等后期有时间再添些自己的Feature。
package com.manue1.tetris; import javax.swing.JFrame; /** * 游戏窗口 * @author Manue1 * @version 1.0 * */ public class GameFrame extends JFrame{ private static final long serialVersionUID = 1L; private Tetris tetris; public GameFrame(){ tetris =new Tetris(); add(tetris); setSize(530,580); setDefaultCloseOperation(EXIT_ON_CLOSE); setLocationRelativeTo(null); } public static void main(String[] args){ GameFrame frame =new GameFrame(); frame.setVisible(true); frame.tetris.action(); //创建4个格子 } }
1 package com.manue1.tetris; 2 3 import java.awt.image.BufferedImage; 4 5 /** 6 * 定义格子 7 * @author Manue1 8 */ 9 public class Cell extends Object{ 10 private int row; int col; 11 private BufferedImage image; 12 13 public Cell(int row, int col, BufferedImage image) { 14 super(); 15 this.row = row; 16 this.col = col; 17 this.image = image; 18 } 19 20 public int getRow() { 21 return row; 22 } 23 24 public void setRow(int row) { 25 this.row = row; 26 } 27 28 public int getCol() { 29 return col; 30 } 31 32 public void setCol(int col) { 33 this.col = col; 34 } 35 36 public BufferedImage getImage() { 37 return image; 38 } 39 40 public void setImage(BufferedImage image) { 41 this.image = image; 42 } 43 44 @Override 45 public String toString() { 46 return "Cell [col=" + col + ", image=" + image + ", row=" + row + "]"; 47 } 48 49 50 public void moveLeft(){ 51 col--; 52 } 53 public void moveRight(){ 54 col++; 55 } 56 public void moveDrop(){ 57 row++; 58 } 59 60 61 }
1 package com.manue1.tetris; 2 import java.util.Arrays; 3 import java.util.Random; 4 5 /** 6 * 4格方块 7 * 只能被子类使用 8 * @author Manue1 9 */ 10 public abstract class Tetromino { 11 12 protected Cell[] cells =new Cell[4];//{^,^,^,^} 13 14 /*旋转状态 15 * 16 */ 17 protected State[] states; 18 /* 旋转状态的序号 19 * 20 */ 21 protected int index =10000; 22 /*内部类 23 * 24 */ 25 protected class State { 26 int row0, col0, row1, col1, row2, col2, row3, col3; 27 public State(int row0, int col0, int row1, int col1, int row2, 28 int col2, int row3, int col3) { 29 this.row0 = row0; 30 this.col0 = col0; 31 this.row1 = row1; 32 this.col1 = col1; 33 this.row2 = row2; 34 this.col2 = col2; 35 this.row3 = row3; 36 this.col3 = col3; 37 } 38 } 39 /** 向右转 */ 40 public void rotateRight() { 41 42 index++; 43 State s = states[index % states.length]; 44 Cell o = cells[0]; 45 int row = o.getRow(); 46 int col = o.getCol(); 47 cells[1].setRow(row + s.row1); 48 cells[1].setCol(col + s.col1); 49 cells[2].setRow(row + s.row2); 50 cells[2].setCol(col + s.col2); 51 cells[3].setRow(row + s.row3); 52 cells[3].setCol(col + s.col3); 53 } 54 55 /** 向左转 */ 56 public void rotateLeft() { 57 index--; 58 State s = states[index % states.length]; 59 Cell o = cells[0]; 60 int row = o.getRow(); 61 int col = o.getCol(); 62 cells[1].setRow(row + s.row1); 63 cells[1].setCol(col + s.col1); 64 cells[2].setRow(row + s.row2); 65 cells[2].setCol(col + s.col2); 66 cells[3].setRow(row + s.row3); 67 cells[3].setCol(col + s.col3); 68 } 69 70 71 72 //工厂方法,随机产生4格方块 73 74 public static Tetromino randomOne(){ 75 Random random =new Random(); 76 int type =random.nextInt(7); 77 switch(type){ 78 case 0: 79 return new T(); 80 case 1: 81 return new I(); 82 case 2: 83 return new S(); 84 case 3: 85 return new J(); 86 case 4: 87 return new L(); 88 case 5: 89 return new Z(); 90 case 6: 91 return new O(); 92 } 93 94 return null; 95 96 } 97 98 99 //方块下落 左右移动 一个格子 100 public void moveRight(){ 101 for(int i=0;i<cells.length ;i++){ 102 cells[i].moveRight(); 103 } 104 } 105 public void moveLeft(){ 106 for(int i=0;i<cells.length ;i++){ 107 cells[i].moveLeft(); 108 } 109 } 110 public void sofeDrop(){ 111 for(int i=0;i<cells.length ;i++){ 112 cells[i].moveDrop(); 113 } 114 } 115 //显示方块中每个格子的行列信息 116 public String toString(){ 117 return Arrays.toString(cells); 118 } 119 120 } 121 122 // 定义七类格子 123 class T extends Tetromino{ 124 public T(){ 125 cells[0]=new Cell(0,4,Tetris.T); 126 cells[1]=new Cell(0,3,Tetris.T); 127 cells[2]=new Cell(0,5,Tetris.T); 128 cells[3]=new Cell(1,4,Tetris.T); 129 states = new State[4]; 130 states[0] = new State(0, 0, 0, -1, 0, 1, 1, 0); 131 states[1] = new State(0, 0, -1, 0, 1, 0, 0, -1); 132 states[2] = new State(0, 0, 0, 1, 0, -1, -1, 0); 133 states[3] = new State(0, 0, 1, 0, -1, 0, 0, 1); 134 } 135 } 136 class S extends Tetromino{ 137 public S(){ 138 cells[0]=new Cell(0,4,Tetris.S); 139 cells[1]=new Cell(0,5,Tetris.S); 140 cells[2]=new Cell(1,3,Tetris.S); 141 cells[3]=new Cell(1,4,Tetris.S); 142 143 144 states = new State[] { new State(0, 0, 0, -1, -1, 0, -1, 1), 145 new State(0, 0, -1, 0, 0, 1, 1, 1) }; 146 147 } 148 } 149 class Z extends Tetromino{ 150 public Z() { 151 cells[0] = new Cell(1, 4, Tetris.Z); 152 cells[1] = new Cell(0, 3, Tetris.Z); 153 cells[2] = new Cell(0, 4, Tetris.Z); 154 cells[3] = new Cell(1, 5, Tetris.Z); 155 156 states = new State[] { new State(0, 0, -1, -1, -1, 0, 0, 1), 157 new State(0, 0, -1, 1, 0, 1, 1, 0) }; 158 } 159 } 160 class O extends Tetromino{ 161 public O() { 162 cells[0] = new Cell(0, 4, Tetris.O); 163 cells[1] = new Cell(0, 5, Tetris.O); 164 cells[2] = new Cell(1, 4, Tetris.O); 165 cells[3] = new Cell(1, 5, Tetris.O); 166 states = new State[] { new State(0, 0, 0, 1, 1, 0, 1, 1), 167 new State(0, 0, 0, 1, 1, 0, 1, 1) }; 168 } 169 } 170 class J extends Tetromino{ 171 public J() { 172 cells[0] = new Cell(0, 4, Tetris.J); 173 cells[1] = new Cell(0, 3, Tetris.J); 174 cells[2] = new Cell(0, 5, Tetris.J); 175 cells[3] = new Cell(1, 5, Tetris.J); 176 177 states = new State[] { new State(0, 0, 0, -1, 0, 1, 1, 1), 178 new State(0, 0, -1, 0, 1, 0, 1, -1), 179 new State(0, 0, 0, 1, 0, -1, -1, -1), 180 new State(0, 0, 1, 0, -1, 0, -1, 1) }; 181 } 182 } 183 class L extends Tetromino{ 184 public L() { 185 cells[0] = new Cell(0, 4, Tetris.L); 186 cells[1] = new Cell(0, 3, Tetris.L); 187 cells[2] = new Cell(0, 5, Tetris.L); 188 cells[3] = new Cell(1, 3, Tetris.L); 189 190 states = new State[] { new State(0, 0, 0, 1, 0, -1, -1, 1), 191 new State(0, 0, 1, 0, -1, 0, 1, 1), 192 new State(0, 0, 0, -1, 0, 1, 1, -1), 193 new State(0, 0, -1, 0, 1, 0, -1, -1) }; 194 } 195 } 196 class I extends Tetromino{ 197 public I() { 198 cells[0] = new Cell(0, 4, Tetris.I); 199 cells[1] = new Cell(0, 3, Tetris.I); 200 cells[2] = new Cell(0, 5, Tetris.I); 201 cells[3] = new Cell(0, 6, Tetris.I); 202 203 states = new State[] { new State(0, 0, 0, -1, 0, 1, 0, 2), 204 new State(0, 0, -1, 0, 1, 0, 2, 0) }; 205 } 206 }
1 package com.manue1.tetris; 2 import java.awt.Color; 3 import java.awt.Font; 4 import java.awt.Graphics; 5 import java.awt.event.KeyAdapter; 6 import java.awt.event.KeyEvent; 7 import java.awt.image.BufferedImage; 8 import java.util.Arrays; 9 import java.util.Timer; 10 import java.util.TimerTask; 11 12 import javax.swing.JPanel; 13 import javax.imageio.ImageIO; 14 15 /** 16 * 俄罗斯方块面板 17 * @author Manue1 18 * @version 1.0 19 */ 20 public class Tetris extends JPanel { 21 22 23 private static final long serialVersionUID = 1L; 24 /* 25 * 分数 墙 正在 下落的方块 下一个方块 26 */ 27 public static final int FONT_COLOR=0x667799; 28 public static final int FONT_SIZE=30; 29 private int score; 30 private int lines; // 销毁的行数 31 private Cell[][] wall = new Cell[ROWS][COLS]; // 墙 32 private Tetromino tetromino; // 正在下落的四格方块 33 private Tetromino nextOne; 34 public static final int ROWS = 20; // 行数 35 public static final int COLS = 10; // 列数 36 37 /* 38 * 背景图片 39 */ 40 private static BufferedImage background; 41 private static BufferedImage overImage; 42 public static BufferedImage T; 43 public static BufferedImage S; 44 public static BufferedImage I; 45 public static BufferedImage L; 46 public static BufferedImage J; 47 public static BufferedImage O; 48 public static BufferedImage Z; 49 50 /* 51 * 静态代码块 52 */ 53 static { 54 try { // 从Tetris类所在的包中读取图片文件到内存对象 55 background = ImageIO.read(Tetris.class.getResource("tetris.png")); 56 overImage = ImageIO.read(Tetris.class.getResource("game-over.png")); 57 T = ImageIO.read(Tetris.class.getResource("T.png")); 58 I = ImageIO.read(Tetris.class.getResource("I.png")); 59 S = ImageIO.read(Tetris.class.getResource("S.png")); 60 Z = ImageIO.read(Tetris.class.getResource("Z.png")); 61 J = ImageIO.read(Tetris.class.getResource("J.png")); 62 L = ImageIO.read(Tetris.class.getResource("L.png")); 63 O = ImageIO.read(Tetris.class.getResource("O.png")); 64 65 } catch (Exception e) { 66 e.printStackTrace(); 67 } 68 } 69 70 // 在Tetris类中重写 绘制方法 绘制背景图片 71 public void paint(Graphics g) { 72 g.drawImage(background, 0, 0, null); 73 g.translate(15, 15); // 坐标系平移 74 paintWall(g); // 画墙 75 paintTetromino(g); 76 paintnextOne(g); 77 paintScore(g);//绘制分数 78 if (gameOver) { 79 g.drawImage(overImage, 0, 0, null); 80 } 81 } 82 83 /* 84 * 在Tetris添加启动方法action() 85 */ 86 public void action() { 87 wall = new Cell[ROWS][COLS]; 88 tetromino = Tetromino.randomOne(); 89 nextOne = Tetromino.randomOne(); 90 91 /* 处理键盘按下事件, 在按下按键时候执行下落方法 92 * 监听键盘事件 创建监听器对象, 注册监听器 93 */ 94 95 this.addKeyListener(new KeyAdapter() { 96 public void keyPressed(KeyEvent e) { 97 int key = e.getKeyCode();// [c] 98 99 if (key == KeyEvent.VK_Q) {//Q表示退出 100 System.exit(0);// 结束Java进程 101 } 102 if (gameOver) { 103 if (key == KeyEvent.VK_S) {//S表示开始 104 startAction(); 105 repaint(); 106 } 107 return; 108 } 109 if (pause) {// pause = true 110 if (key == KeyEvent.VK_C) {//C表示继续 111 continueAction(); 112 repaint(); 113 } 114 return; 115 } 116 117 118 119 120 switch (key) { 121 case KeyEvent.VK_DOWN: 122 //tetromino.sofeDrop(); 123 softDropAction(); 124 125 break; 126 case KeyEvent.VK_RIGHT: 127 //tetromino.moveRight(); 128 moveRightAction(); 129 break; 130 case KeyEvent.VK_LEFT: 131 //tetromino.moveLeft(); 132 moveLeftAction(); 133 break; 134 case KeyEvent.VK_SPACE : 135 136 hardDropAction(); 137 break; 138 case KeyEvent.VK_UP: 139 rotateRightAction(); 140 break; 141 case KeyEvent.VK_P://按键盘上的P表示暂停 142 pauseAction(); 143 break; 144 } 145 repaint();// 再画一次! 146 } 147 }); 148 149 this.requestFocus(); //为面板请求焦点 150 151 this.setFocusable(true); //面板可以获得焦点 152 /* 153 * 计时器 自动下落 154 */ 155 timer = new Timer(); 156 timer.schedule( 157 new TimerTask(){ 158 public void run(){ 159 //tetromino.sofeDrop(); 160 softDropAction(); 161 repaint(); 162 } 163 },1000,1000); 164 165 166 } 167 168 public static final int CELL_SIZE = 26; 169 170 /* 171 * 画墙 172 */ 173 174 private void paintWall(Graphics g) { 175 for (int row = 0; row < wall.length; row++) { 176 Cell[] line = wall[row]; 177 // line 代表墙上的每一行 178 for (int col = 0; col < line.length; col++) { 179 Cell cell = line[col]; 180 // cell 代表墙上的每个格子 181 int x = col * CELL_SIZE; 182 int y = row * CELL_SIZE; 183 if (cell == null) { 184 g.drawRect(x, y, CELL_SIZE, CELL_SIZE); 185 186 } else { 187 g.drawImage(cell.getImage(), x - 1, y - 1, null); 188 189 } 190 // g.drawString(row+","+col, x,y+CELL_SIZE); 191 } 192 } 193 } 194 195 /* 196 * 正在下落 方块 197 */ 198 public void paintTetromino(Graphics g) { 199 if (tetromino == null) {return;} // 如果是空 直接不执行了 输入数据的有效性检测 200 Cell[] cells = tetromino.cells; 201 for (int i = 0; i < cells.length; i++) { 202 Cell cell = cells[i]; 203 int x = cell.getCol() * CELL_SIZE; 204 int y = cell.getRow() * CELL_SIZE; 205 g.drawImage(cell.getImage(), x, y, null); 206 } 207 } 208 /* 209 * 下一个下落的方块 210 */ 211 public void paintnextOne(Graphics g) { 212 if (nextOne == null) {return;} // 如果是空 直接不执行了 输入数据的有效性检测 213 Cell[] cells = nextOne.cells; 214 for (int i = 0; i < cells.length; i++) { 215 Cell cell = cells[i]; 216 int x = (cell.getCol()+10) * CELL_SIZE; 217 int y = (cell.getRow()+1) * CELL_SIZE; 218 g.drawImage(cell.getImage(), x, y, null); 219 } 220 } 221 222 /*在Tetris类中添加方法 223 * 检测正在下路方块是否出界 224 */ 225 /** 检查当前正在下落的方块是否出界了 */ 226 private boolean outOfBounds() { 227 Cell[] cells = tetromino.cells; 228 for (int i = 0; i < cells.length; i++) { 229 Cell cell = cells[i]; 230 int col = cell.getCol(); 231 if (col < 0 || col >= COLS) { 232 return true; 233 } 234 } 235 return false; 236 } 237 /** 检查正在下落的方块是否与墙上的砖块重叠 */ 238 private boolean coincide() { 239 Cell[] cells = tetromino.cells; 240 for (int i = 0; i < cells.length; i++) { 241 Cell cell = cells[i]; 242 int row = cell.getRow(); 243 int col = cell.getCol(); 244 // 如果墙的row,col位置上有格子,就重叠了! 245 if (row >= 0 && row < ROWS && col >= 0 && col <= COLS 246 && wall[row][col] != null) { 247 return true;// 重叠 248 } 249 } 250 return false; 251 } 252 /** 在Tetris 类上添加方法, 向右移动的流程控制 */ 253 public void moveRightAction() { 254 // 尝试先向右移动, 如果发现超出了边界, 就 255 // 向左移动, 修正回来. 256 tetromino.moveRight();// coincide重叠 257 if (outOfBounds() || coincide()) { 258 tetromino.moveLeft(); 259 } 260 } 261 public void moveLeftAction() { 262 tetromino.moveLeft(); 263 if (outOfBounds() || coincide()) { 264 tetromino.moveRight(); 265 } 266 } 267 268 269 270 271 272 273 /* 下落流程控制 274 * 275 */ 276 277 public void softDropAction() { 278 if (canDrop()) { 279 tetromino.sofeDrop(); 280 } else { 281 landIntoWall(); 282 destoryLines(); 283 checkGameOverAction(); 284 tetromino = nextOne; 285 nextOne = Tetromino.randomOne(); 286 } 287 } 288 public void hardDropAction(){ 289 while(canDrop()){ 290 tetromino.sofeDrop(); 291 } 292 landIntoWall(); 293 destoryLines(); 294 checkGameOverAction(); 295 tetromino = nextOne; 296 nextOne = Tetromino.randomOne(); 297 298 } 299 300 301 302 303 private static int[] scoreTable={0,1,10,50,100}; 304 305 306 /* 调用删除行方法。循环让上面所有的行落下来 307 308 */ 309 private void destoryLines() { 310 int lines = 0; 311 for (int row = 0; row < wall.length; row++) { 312 if (fullCells(row)) { 313 deleteRow(row); 314 lines++; 315 } 316 } 317 this.score += scoreTable[lines]; 318 this.lines += lines; 319 } 320 321 322 /* 323 * 删除行 324 */ 325 private void deleteRow(int row) { 326 for (int i = row; i >= 1; i--) { 327 System.arraycopy(wall[i - 1], 0, wall[i], 0, COLS); 328 } 329 Arrays.fill(wall[0], null); 330 } 331 332 333 /* 判断行是否被填充 334 * 335 */ 336 private boolean fullCells(int row){ 337 Cell[] line =wall[row]; 338 /* 339 for(Cell cell : line){ 340 if(cell==null) return false; 341 } 342 */ 343 for(int i=0;i<line.length;i++){ 344 if(line[i]==null)return false; 345 } 346 return true; 347 } 348 349 350 351 /*下落到最低行停止 352 * 353 */ 354 private void landIntoWall(){ 355 Cell[] cells=tetromino.cells; 356 for(int i=0;i<cells.length;i++){ 357 Cell cell = cells[i]; 358 int row,col; 359 row=cell.getRow(); 360 col=cell.getCol(); 361 wall[row][col]=cell; 362 } 363 } 364 365 366 private boolean canDrop() { 367 Cell[] cells = tetromino.cells; 368 for (int i = 0; i < cells.length; i++) { 369 Cell cell = cells[i]; 370 int row = cell.getRow(); 371 if (row == ROWS - 1) { 372 return false; 373 } 374 } 375 for (Cell cell : cells) {// Java 5 以后可以使用 376 int row = cell.getRow() + 1; 377 int col = cell.getCol(); 378 if (row >= 0 && row < ROWS && col >= 0 && col <= COLS 379 && wall[row][col] != null) { 380 return false; 381 } 382 } 383 return true; 384 } 385 386 /*绘制分数 387 * 388 */ 389 private void paintScore(Graphics g) { 390 int x = 290; 391 int y = 160; 392 g.setColor(new Color(FONT_COLOR)); 393 Font font = g.getFont();// 取得g当前字体 394 font = new Font(font.getName(), font.getStyle(), FONT_SIZE); 395 g.setFont(font);// 更改了g的字体 396 String str = "SCORE:" + score; 397 g.drawString(str, x, y); 398 y += 56; 399 str = "LINES:" + lines; 400 g.drawString(str, x, y); 401 y += 56; 402 str = "[P]Pause"; 403 if (pause) { 404 str = "[C]Continue"; 405 } 406 if (gameOver) { 407 str = "[S]Start!"; 408 } 409 g.drawString(str, x, y); 410 411 412 } 413 414 415 416 /** 在Tetris类中添加 旋转流程控制方法 */ 417 public void rotateRightAction() { 418 tetromino.rotateRight(); 419 if (outOfBounds() || coincide()) { 420 tetromino.rotateLeft(); 421 } 422 } 423 424 425 426 /* 427 * 流程处理 428 */ 429 private Timer timer; 430 private boolean pause;//是否为暂停状态 431 private boolean gameOver;//是否为游戏结束状态 432 private long interval = 600;// 间隔时间 433 434 435 /** 在Tetris类中添加 开始流程控制 */ 436 public void startAction() { 437 pause = false; 438 gameOver = false; 439 score = 0; 440 lines = 0; 441 clearWall(); 442 tetromino = Tetromino.randomOne(); 443 nextOne = Tetromino.randomOne(); 444 timer = new Timer(); 445 timer.schedule(new TimerTask() { 446 public void run() { 447 softDropAction(); 448 repaint(); 449 } 450 }, interval, interval); 451 } 452 /** 453 * 清除墙上的方块 454 */ 455 private void clearWall() { 456 for (Cell[] line : wall) { 457 Arrays.fill(line, null); 458 } 459 } 460 /** 461 * 暂停 462 */ 463 public void pauseAction() { 464 timer.cancel(); 465 pause = true; 466 } 467 /** 468 * 继续 469 */ 470 public void continueAction() { 471 timer = new Timer(); 472 timer.schedule(new TimerTask() { 473 @Override 474 public void run() { 475 softDropAction(); 476 repaint(); 477 } 478 }, interval, interval); 479 pause = false; 480 } 481 /** 482 * 游戏结束 483 */ 484 public void checkGameOverAction() { 485 if (wall[0][4] != null) { 486 timer.cancel(); 487 gameOver = true; 488 } 489 } 490 }