OOP面向对象编程之俄罗斯方块项目实现过程

这是用纯java编译的俄罗斯方块,讲这个游戏的是达内的刘苍松老师,苍老师讲课我们学生都比较喜欢,下面我把当时的做这个游戏学习笔记分享给大家,先看下游戏截图:

 

 

重点学习笔记:

Tetris 俄罗斯方块
Tetromino 四格方块

业务需求(清晰明确) -> 业务对象模型(理清对象关系)->
数据建模(用数据表达对象,和对象的关系)-> 类的设计->
概要编码(语法知识)-> 详细功能设计(语法知识)->


1 明确业务需求
  用自然语言,将业务功能描述清楚

俄罗斯方块的基本规则:
1、一个用于摆放小型正方形的平面虚拟场地,其标准大小:
行宽为10,列高为20,以每个小正方形为单位。
2、一组由4个小型正方形组成的规则图形,英文称为Tetromino,
中文通称为方块共有7种,分别以S、Z、L、J、I、O、T这7个字母的
形状来命名。
I:一次最多消除四层
J(左右):最多消除三层,或消除二层
L:最多消除三层,或消除二层
O:消除一至二层
S(左右):最多二层,容易造成孔洞
Z (左右):最多二层,容易造成孔洞
T:最多二层

(1)玩家操作有:旋转方块,以格子为单位左右移动方块,
让方块加速落下。
(2)方块移到区域最下方或是着地到其他方块上无法移动时,
就会固定在该处,而新的方块出现在区域上方开始落下。
(3)当区域中某一列横向格子全部由方块填满,则该列会消
失并成为玩家的得分。同时删除的列数越多,得分指数上升。
(4)当固定的方块堆到区域最上方而无法消除层数时,则游
戏结束。
(6)一般来说,游戏还会提示下一个要落下的方块,熟练的
玩家会计算到下一个方块,评估要如何进行。由于游戏能不断
进行下去对商业用游戏不太理想,所以一般还会随着游戏的进
行而加速提高难度。
(7)预先设置的随机发生器不断地输出单个方块到场地顶部


2 业务分析
  找到有哪些业务对象根据图片的分析
 
  tetris(俄罗斯方块)
    |-- score 累计分数
    |-- lines 销毁的行数
    |-- wall(墙 20行X10列)
    |    |-- 20row(行)
    |         |-- 10 col cell(列)
    |-- tetromino 正在下落的(4格方块,有7种形态)
    |      |-- 4 cell
    |-- nextOne 下一个准备下落的方块
           |-- 4 cell

3 数据模型, 一切业务对象转换为数字表示 
  场地按照行列划分为 20×10格子
   格子有属性row, col, color
  
  
4 类的数据结构设计
 package com.tarena.tetris
 
   Cell 格子
     |-- int row 行
     |-- int col 列
     |-- Image image 贴图
  
   Tetromino 四格方块 有7个子类
     |-- Cell[] cells 包含4个格子
         
   Tetris 俄罗斯方块 继承于 JPanel
     |-- int score
     |-- int lines
     |-- Cell[][] wall = new Cell[20][10]
     |-- Tetromino tetromino
     |-- Tetromino nextOne

随机生产4格方块: 使用简单工厂方法模式.

绘制软件的界面

    
5 数据的初始化(构造器)

6 详细功能设计(动作, 方法)
 
  右移动流程设计
   1) 先向右移动一步
   2) 检查是否移出边界(子功能/子方法)
   3) 如果移出边界, 再向左移动一步
   4) 重写绘制界面
   实现:
   在Tetris类中添加方法 moveRightAction() 因为Tetris
   包含 需要用到数据, 如墙的边界.
  
   检查是否移出边界(子功能/子方法)
   outOfBounds()
  
  下落功能设计
  (1) 获取键盘事件: 绑定在下箭头按下时候执行下落动作
     利用Java Swing 提供的键盘监听器API来处理.
    
  (2) 在4格方块上添加下落动作(方法), 利用算法实现方块位置
    的变化(row 和 col 的变化)
   
  (3) 调用界面的重绘方法, 按照新数据(变化以后的行列数据)
    重新绘制界面 repaint(), repaint()会尽快调用paint方法
 
  1) 完善是下落流程控制
    边界检查, 着陆到墙上, 消除满行并记分, 游戏结束检查
    重合检查
  
   下落流程功能描述:
   1) 如果能够下落(canDrop())就下落一步
   2) 如果不能下落就着陆到墙中(landToWall())
   3) 着陆以后要消除(destroyLines())满行并且记分
   4) 游戏结束检查
   5) 生成下一个方块
      
  下落功能: Tetris类中添加方法: softDropAction()
    if(canDrop()){
     tetromino.softDrop();
    }else{
     landToWall();
     destroyLines();
     checkGameOver();
     tetromino = nextOne;
     nextOne = randomOne();
    }
 
  加速落下
   
  2) 旋转流程实现
   1) 处理键盘事件
   2) 执行旋转流程
     流程包括, 先旋转,
     检测是否出界和重合
     转回去.
    
   详细设计: 在 Tetromino 类上添加
      rotateRight()
      rotateLeft()
     
   在Tetris 上添加 rotateRightAction()
  
   
  3) 开始流程 Tetris 上添加startAction()
    3.1) 清空墙
    3.2) 清理分数
    3.3) 初始化方块
    3.4) 初始结束状态
 3.5) 启动定时器 执行下落流程
    
  4) 暂停流程 Tetris 上添加pauseAction()
    4.1) 取消定时器
    4.2) 设置暂停状态
 
  5) 继续流程 Tetris 上添加continueAction()
    5.1) 取消暂停状态
    5.2) 启动定时器
 
  6) 检查结束游戏流程 Tetris 上添加checkGameOver()
    6.1) 检查是否结束
    6.2) 如果结束就取消定时器
    6.3) 设置结束状态
 
  7) 游戏状态显示 更新paintScore()
   7.1) 根据暂停状态显示 继续/暂停
   7.2) 根据结束状态, 显示结束状态. 

7 界面绘制(利用Java Swing API实现)
  根据数据,绘制界面
  如:
    创建墙的数据, 再绘制墙
    有正在下落的方块对象, 才能绘制 下落的方块


关于软件调试(debug)
  1) 调试软件的前提是知道什么是正确.
     1.1) 正确的结果(表现)
     1.2) 正确的数据是什么
  2) 按照正确确定测试方案
     2.1) 找到实际运算时候的数据(println(data))
  3) 比较正确和错误的数据, 分析出错的原因
 
 
OOP 面向对象编程 与 俄罗斯方块项目实现过程
  核心原则: 计算机只能处理数据计算
     程序: 数据结构 + 算法
         = 对输入数据的算法加工得到输出数据的过程
    
 OOP 是 如何将 业务过程(俄罗斯方块) 转换为数据计算的方法
 
 面向对象的思考分析过程
 1) 根据业务需求找到对象, 分析对象之间的关系
 2) 利用合理的数据模型描述对象.(解析几何课程)
   可以将对象进行数量化, 用数字来表达对象.
 3) 类的设计, 定义出 类属性(数据模型)
   已经对象之间的关系(用到了面向对象的语法知识)
   注意: 要有面向对象的语法表达合理的逻辑关系
  
    Cell 格子
     |-- int row 行
     |-- int col 列
     |-- Image image 贴图
  
   Tetromino 四格方块 有7个子类 (四格方块有7种)
     |-- Cell[] cells 包含4个格子
         
   Tetris 俄罗斯方块 继承于 JPanel
     |-- int score
     |-- int lines
     |-- Cell[][] wall = new Cell[20][10]
     |-- Tetromino tetromino 多态
     |-- Tetromino nextOne  多态
  
  4) 实现概要设计(概要编码)
  5) 详细数据设计
    详细数据结构设计
      墙的设计 2维数组
      4格方块的数据设计
  
  6) 界面绘制(利用Java Swing API 绘制界面, 贴图)
  7) 功能算法设计
    原则: 一切功能(子功能) 都是方法: 动词就是方法
    实现策略: 将功能映射到数学模型, 研究数据的变化规律
      找出算法测试.
     
    下落
    移动
    旋转
    消行
    记分
  
  8) 事件绑定(利用Java Swing API 实现事件绑定)
 
  9) 调试: Debug
    最重要原则: 知道理想的数据输入和数据输出(通过数据模型)
    模拟计算得出. 在找到软件实际计算的数据, 比较两种数据的
    不同, 找出错误的原因.
    
    

根据笔记的内容,贴出完整的代码:
  


  ------------------------------------------------------格子类 Cell.java---------------------------------------------------

 

package com.tarena.tetris;

import java.awt.image.BufferedImage;

/** 格子 */
public class Cell {
	private int row;
	private int col;
	private BufferedImage image;
	
	public Cell(int row, int col, BufferedImage image) {
		super();
		this.row = row;
		this.col = col;
		this.image = image;
	}
	
	public void drop(){
		row++;
	}

	public void moveLeft(){
		col--;
	}
	
	public void moveRight(){
		col++;
	}
	
	public int getCol() {
		return col;
	}

	public void setCol(int col) {
		this.col = col;
	}

	public BufferedImage getImage() {
		return image;
	}

	public void setImage(BufferedImage image) {
		this.image = image;
	}

	public int getRow() {
		return row;
	}

	public void setRow(int row) {
		this.row = row;
	}
	
	public String toString(){
		return row+","+col;
	}
	
}



----------------------------------------四格方块类Tetromino.java----------------------------------
package com.tarena.tetris;

import java.util.Arrays;
import java.util.Random;

/** 四格方块  */
public abstract class Tetromino {
	protected Cell[] cells = new Cell[4];
	/** 旋转状态 */
	protected State[] states;
	/** 获取旋转状态序号 */
	protected int index = 10000;
	protected class State{
		int row0,col0,row1,col1,row2,col2,row3,col3;
		public State(int row0, int col0, int row1, int col1, int row2, int col2, int row3, int col3) {
			this.row0 = row0;
			this.col0 = col0;
			this.row1 = row1;
			this.col1 = col1;
			this.row2 = row2;
			this.col2 = col2;
			this.row3 = row3;
			this.col3 = col3;
		}
	}
	/** 向右转 */
	public void rotateRight(){
		// 1) 获得当前的轴
		// 2) 获得下个状态的变化数据
		// 3) 利用+法实现数据变化
		//states[0]=new State(0,0,0,-1,0,1,1,0);
		//states[1]=new State(0,0,-1,0,1,0,0,-1);
		//states[2]=new State(0,0,0,1,0,-1,-1,0);
		//states[3]=new State(0,0,1,0,-1,0,0,1);
		Cell o = cells[0];//t0=[3,4][3,3][3,5][4,4]
		int row = o.getRow();//3
		int col = o.getCol();//4
		index++;//10001
		//               10001 % 4 = 1
		State s = states[index % states.length];//
		//s = (0,0,-1,0,1,0,0,-1)
		//t1=[3,4][2,4][4,4][3,3]
		cells[1].setRow(row+s.row1);//3+-1 = 2->cells[1].row
		cells[1].setCol(col+s.col1);//4+0 = 4->cells[1].col
		cells[2].setRow(row+s.row2);//3+1 = 4->cells[2].row
		cells[2].setCol(col+s.col2);//4+0 = 4->cells[2].col
		cells[3].setRow(row+s.row3);//3+0 = 3->cells[2].row
		cells[3].setCol(col+s.col3);//4+-1 = 3->cells[2].col
	}
	/** 向右转 */
	public void rotateLeft(){
		Cell o = cells[0];//t0=[3,4][3,3][3,5][4,4]
		int row = o.getRow();//3
		int col = o.getCol();//4
		index--;//10000
		State s = states[index % states.length];//
		cells[1].setRow(row+s.row1);//3+-1 = 2->cells[1].row
		cells[1].setCol(col+s.col1);//4+0 = 4->cells[1].col
		cells[2].setRow(row+s.row2);//3+1 = 4->cells[2].row
		cells[2].setCol(col+s.col2);//4+0 = 4->cells[2].col
		cells[3].setRow(row+s.row3);//3+0 = 3->cells[2].row
		cells[3].setCol(col+s.col3);//4+-1 = 3->cells[2].col
	}

	
	/** 私有构造器 */
	private Tetromino(){
	}
	/** 下落一步 */
	public void softDrop(){
		for(int i =0; i<cells.length; i++){
			cells[i].drop();
		}
	}
	public void moveLeft(){
		for(int i =0; i<cells.length; i++){
			cells[i].moveLeft();
		}
	}
	public void moveRight(){
		for(int i =0; i<cells.length; i++){
			cells[i].moveRight();
		}
	}
	
	public String toString() {
		return Arrays.toString(cells);
	}
	
	/** 只能通过这个工厂方法创建 4格方块的实例
	 * 也就是说, 只能使用这个方法随机生成方块! 别无他法
	 * */
	public static Tetromino randomOne(){
		Random r = new Random();
		int type = r.nextInt(7);
		switch(type){
		case 0: return new T();//静态方法只能使用静态内部类
		case 1: return new S();
		case 2: return new Z();
		case 3: return new L();
		case 4: return new J();
		case 5: return new O();
		case 6: return new I();
		}
		return null;
	}
	
	/** 利用内部类封装了子类实现的细节 */
	private static 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);
			states = new State[4];
			states[0]=new State(0,0,0,-1,0,1,1,0);
			states[1]=new State(0,0,-1,0,1,0,0,-1);
			states[2]=new State(0,0,0,1,0,-1,-1,0);
			states[3]=new State(0,0,1,0,-1,0,0,1);
		}
	}

	private static 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);
			states = new State[] { new State(0, 0, 0, 1, 0, -1, 0, -2),
					new State(0, 0, -1, 0, 1, 0, 2, 0) };

		}
	}

	private static 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);
			states = new State[]{
					new State(0,0, 0,1, 1,-1, 1,0 ),
					new State(0,0, -1,0, 1,1, 0,1 )};
		}
	}

	private static 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);
			states = new State[]{
					new State(0,0, -1,-1, -1,0, 0,1 ),
					new State(0,0, -1,1, 0,1, 1,0 )};
		}
	}

	private static 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);
			states = new State[]{
					new State(0,0, 0,-1, 0,1, 1,-1 ),
					new State(0,0, -1,0, 1,0, -1,-1),
					new State(0,0, 0,1, 0,-1, -1,1),
					new State(0,0, 1,0, -1,0, 1,1)};
		}
	}

	private static 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);
			states = new State[]{
					new State(0,0, 0,-1, 0,1, 1,1),
					new State(0,0, -1,0, 1,0, 1,-1),
					new State(0,0, 0,1, 0,-1, -1,-1),
					new State(0,0, 1,0, -1,0, -1,1 )};
		}
	}

	private static 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);
			states = new State[]{
					new State(0,0, 0,1, 1,0, 1,1 ),
					new State(0,0, 0,1, 1,0, 1,1 )};
		}
	}
}

 
 
----------------------------俄罗斯方块类Tetris.java---------------------------------------------
 
<pre class="java" name="code">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 java.util.Timer;
import java.util.TimerTask;

import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
/**
 * 俄罗斯方块类 
 *
 */
public class Tetris extends JPanel {
	/** 分数 */
	private int score;
	/** 行数 */
	private int lines;
	public static final int ROWS = 20;//行数
	public static final int COLS = 10;//列数
	/** 方块墙, 墙是Tetris的组件 */
	private Cell[][] wall = new Cell[ROWS][COLS];
	/** 正在下落的方块 */
	private Tetromino tetromino;
	/** 下一个下落的方块 */
	private Tetromino nextOne;
	// 图片素材的装载
	/** 背景图片 */
	public static BufferedImage background; 
	public static BufferedImage gameOver; 
	public static BufferedImage I; 
	public static BufferedImage T; 
	public static BufferedImage S; 
	public static BufferedImage J; 
	public static BufferedImage L; 
	public static BufferedImage O; 
	public static BufferedImage Z; 
	
	// 加载静态的图片资源
	static{
		try{
			//如下代码可以从package中读取 tetris.png 为图片对象
			//图片文件必须放置在com.tarena.tertis包装中
			background = ImageIO.read(
					Tetris.class.getResource("tetris.png"));
			gameOver = ImageIO.read(
					Tetris.class.getResource("game-over.png"));
			I=ImageIO.read(Tetris.class.getResource("I.png"));
			T=ImageIO.read(Tetris.class.getResource("T.png"));
			S=ImageIO.read(Tetris.class.getResource("S.png"));
			Z=ImageIO.read(Tetris.class.getResource("Z.png"));
			L=ImageIO.read(Tetris.class.getResource("L.png"));
			J=ImageIO.read(Tetris.class.getResource("J.png"));
			O=ImageIO.read(Tetris.class.getResource("O.png"));
		}catch(Exception e){
			e.printStackTrace();
		}
	}
	/** 在Tetris 类中重写 paint方法, 修改原有的绘图方法
	 *  paint 绘制, 涂抹 Graphics: 绘图 这代表画笔
	 * */
	public void paint(Graphics g){
		//draw 画  Image 图片 background背景图片
		g.drawImage(background, 0, 0, null);
		//translate 可以平移绘制坐标系
		g.translate(15, 15);
		paintWall(g);//画墙
		paintTetromino(g);//画正在下落的方块
		paintNextOne(g);//画下一个方块
		paintScore(g);//画分数
		g.translate(-15, -15);
		if(finish){
			g.drawImage(gameOver, 0, 0, null);
		}
	}
	public static final int FONT_COLOR=0x667799;
	public static final int FONT_SIZE=30;//字号
	private void paintScore(Graphics g){
		int x = 290;
		int y = 162;
		g.setColor(new Color(FONT_COLOR));
		//font 字体
		Font font = g.getFont();//获取当前画笔的默认字体
		//用新字号创建字体
		font = new Font(font.getName(),Font.BOLD,FONT_SIZE);
		g.setFont(font);
		g.drawString("SCORE:"+score, x, y);
		y+=56;
		g.drawString("LINES:"+lines, x, y);
		y+=56;
		String str = "[P]Pause";
		if(pause){
			str = "[C]Continue";
		}
		if(finish){
			str = "[S]Start!";
		}
		g.drawString(str, x, y);
	}
	private void paintNextOne(Graphics g){
		if(nextOne==null){
			return;
		}
		Cell[] cells = nextOne.cells;
		for(int i=0; i<cells.length; i++){
			Cell cell = cells[i];
			int x = CELL_SIZE * (cell.getCol()+10);
			int y = CELL_SIZE * (cell.getRow()+1);
			g.drawImage(cell.getImage(), x-1, y-1, null);
		}
	}
	private void paintTetromino(Graphics g){
		//System.out.println(this.tetromino); 
		if(tetromino==null){
			return;
		}
		Cell[] cells = tetromino.cells;
		for(int i=0; i<cells.length; i++){
			Cell cell = cells[i];
			int x = CELL_SIZE * cell.getCol();
			int y = CELL_SIZE * cell.getRow();
			g.drawImage(cell.getImage(), x-1, y-1, null);
		}
	}
	public static final int CELL_SIZE=26;
	/** 将wall(二维数组)的内容绘制到面板上
	 * 每个格子(迭代)都绘制出来.
	 * 如果: 没有方块的地方绘制空白格
	 *   有方块(Cell)的地方, 绘制方块的贴图
	 *  */
	private void paintWall(Graphics g){
		for(int row=0; row<wall.length; row++){
			Cell[] line = wall[row];
			for(int col=0; col<line.length; col++){
				Cell cell = line[col];
				int x = col*CELL_SIZE;
				int y = row*CELL_SIZE;
				if(cell==null){
					g.setColor(new Color(0));
					//drawRect 绘制矩形格子 
					g.drawRect(x, y, CELL_SIZE, CELL_SIZE);
				}else{
					g.drawImage(cell.getImage(), x-1, y-1, null);
				}
			}
		}
	}
	/** Tetris 类中 添加方法 action() 用于 启动软件 */
	public void action(){
		//tetromino = Tetromino.randomOne();
		//System.out.println("action():"+this.tetromino); 
		//nextOne = Tetromino.randomOne();
		//wall[19][5]=new Cell(19,5,T);
		startAction();//启动游戏
		KeyListener l = new KeyAdapter(){
			//在按键按下时候执行
			public void keyPressed(KeyEvent e) {
				int key = e.getKeyCode();
				if(finish){
					if(key==KeyEvent.VK_S){	
						startAction();
						repaint();
					}
					return;
				}
				if(pause){
					if(key==KeyEvent.VK_C){	
						continueAction();
						repaint();
					}
					return;
				}
				switch(key){
				case KeyEvent.VK_P: pauseAction(); break;
				case KeyEvent.VK_DOWN:softDropAction();break;
				case KeyEvent.VK_RIGHT:moveRightAction();break;
				case KeyEvent.VK_LEFT:moveLeftAction();break;
				case KeyEvent.VK_SPACE:hardDropAction();break;
				case KeyEvent.VK_UP:rotateRightAction();break;
				case KeyEvent.VK_Z:rotateLeftAction();break;
				}
				repaint();//尽快的调用 paint() 方法绘制界面
				//按键按下->发现是40->执行softDrop()->修改格子的
				//数据->repaint()->尽快调用paint()->根据当前数据
				//(是已经更改的数据)绘制界面
			}
		};
		this.addKeyListener(l);//在当前面板上绑定了键盘监听l
		this.requestFocus();//为当前面板请求键盘输入焦点
	}	
	/** 在Tetris类中添加 向右移动流程控制方法 */
	public void moveRightAction(){
		tetromino.moveRight();
		if(outOfBounds() || coincide()){//coincide重合
			tetromino.moveLeft();
		}
	}
	/** 检查正在下落的方块是否与墙上的砖块重合 */
	private boolean coincide(){
		Cell[] cells = tetromino.cells;
		for(int i=0; i<cells.length; i++){
			Cell cell = cells[i];
			int row = cell.getRow();int col = cell.getCol();
			if( row>=0 && row<ROWS && col>=0 && col<COLS && 
					wall[row][col]!=null){
				//重合
				return true;
			}
		}
		return false;
	}
	public void moveLeftAction(){
		tetromino.moveLeft();
		if(outOfBounds() || coincide()){
			tetromino.moveRight();
		}
	}
	/** Bound 边界, 检查是否出界的算法 */
	private boolean outOfBounds(){
		Cell[] cells = tetromino.cells;
		for(int i=0; i<cells.length; i++){
			Cell cell = cells[i];
			int col = cell.getCol();
			if(col<0 || col>= COLS){
				return true;
			}
		}
		return false;
	}
	/** Tetris 类中添加下列流程控制方法  */
	public void softDropAction() {
		if (canDrop()) {// 能下落吗?
			tetromino.softDrop();
		} else {
			landToWall();// 四格方块着陆到墙上
			destroyLines();// 销毁满行
			checkGameOver();// 检查游戏是否结束
			tetromino = nextOne;
			nextOne = Tetromino.randomOne();
		}
	}
	public void hardDropAction(){
		while(canDrop()) {// 能下落吗?
			tetromino.softDrop();
		}
		landToWall();// 四格方块着陆到墙上
		destroyLines();// 销毁满行
		checkGameOver();// 检查游戏是否结束
		tetromino = nextOne;
		nextOne = Tetromino.randomOne();
	}
	

	public static final int[] SCORE_TABLE={0,1,10,50,100};
	//                                     0 1  2  3   4
	private void destroyLines() {
		int lines = 0;
		for(int row=0; row<wall.length; row++){
			if(fullCells(row)){
				removeLine(row);
				lines++;
			}
		}
		this.lines+=lines;
		this.score+=SCORE_TABLE[lines];
	}
	private boolean fullCells(int row){
		Cell[] line = wall[row];
		for (Cell cell : line) {
			if(cell==null){
				return false;
			}
		}
		return true;
	}
	private void removeLine(int row){
		for(int i=row; i>=1; i--){
			//cp [i-1] -> [i]
			System.arraycopy(wall[i-1], 0, wall[i], 0, COLS);
		}
		Arrays.fill(wall[0], null);
	}
	
	private void landToWall() {
		Cell[] cells = tetromino.cells;
		for(Cell cell: cells){
			int row = cell.getRow();
			int col = cell.getCol();
			wall[row][col]=cell;
		}
	}
	private boolean canDrop(){
		Cell[] cells = tetromino.cells;
		for(int i=0; i<cells.length; i++){
			Cell cell = cells[i];
			int row = cell.getRow();
			if(row==ROWS-1){
				return false;
			}
		}
		for(Cell cell: cells){//增强版foreach循环,简化了迭代
			int row = cell.getRow();
			int col = cell.getCol();
			if(wall[row+1][col]!=null){
				return false;
			}
		}
		return true;
	}
	
	/** Tetris 类中的 rotateRightAction() 旋转流程控制 */
	public void rotateRightAction(){
		tetromino.rotateRight();
		if(outOfBounds() || coincide()){
			tetromino.rotateLeft();
		}
	}
	
	public void rotateLeftAction(){
		tetromino.rotateLeft();
		if(outOfBounds() || coincide()){
			tetromino.rotateRight();
		}
	}
	
	/** 为了配合 定时事件, 
	 * 还修改了键盘事件监听action()
	 * 以及界面绘制方法 paint() paintScrore() */
	
	/** Tetris 类中 增加2个属性, 和一个Timer实例 */
	private boolean pause;//暂停
	private boolean finish;//结束
	private Timer timer;//下落定时器
	/** 开始流程 */
	public void startAction(){
		clearWall();
		pause = false;
		finish = false;
		tetromino = Tetromino.randomOne();
		nextOne = Tetromino.randomOne();
		lines = 0;
		score = 0;
		timer = new Timer();
		timer.schedule(new TimerTask(){
			public void run() {
				softDropAction();
				repaint();
			}
		}, 700, 700);
	}
	private void clearWall(){
		for(Cell[] line: wall){
			Arrays.fill(line, null);
		}
	}
	public void pauseAction(){
		pause = true;
		timer.cancel();//停止定时器上的一切任务!
	}
	public void continueAction(){
		pause = false;
		timer = new Timer();
		timer.schedule(new TimerTask(){
			public void run() {
				softDropAction();
				repaint();
			}
		}, 700, 700);
	}
	private void checkGameOver() {
		if(wall[0][4]!=null){//游戏该结束了!
			finish = true;
			timer.cancel();
			repaint();
		}
	}
	/**Tetris 类中的main方法, 用于初始化界面 */
	public static void main(String[] args) {
		JFrame frame = new JFrame("俄罗斯方块");
		frame.setSize(525, 580);
		Tetris tetris = new Tetris();//继承JPanel 
		//Background 背景
		tetris.setBackground(new Color(0xffff00));
		frame.add(tetris);//添加俄罗斯方块游戏到窗口框中
		//Default默认 Close关闭 Operation操作
		//当关闭窗口时候, 将Java软件也结束
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setVisible(true);//会尽快调用paint()
		tetris.action();
	}
}

 
笔记及注释都写的很清楚了,大家运行俄罗斯方块类就可以启动游戏了,因为图片差不多十几张,所以图片没上传,需要包含图片的源码请私下联系我,联系方式见博客左侧









 
  • 13
    点赞
  • 71
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值