JAVA版俄羅斯方塊學習

軟件的開發過程

  1) 需求(軟件功能的文字描述)
  2) 需求分析(找對象)
  3) 概要設計
    3.1) 數學模型
    3.2) 類的設計
  4) 詳細功能的設計
    4.1) 數據初始化
    4.2) 界面繪製
    4.3) 左右移動功能設計
    4.4) 下落功能設計
    

1 明確業務需求
  用自然語言,將業務功能描述清楚
 
Tetris 專用詞 俄羅斯方塊
Tetromino 專用詞 4格方塊

俄羅斯方塊的基本規則:
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 類 設計
  Cell
   |-- int row
   |-- int col
   |-- image 貼圖
 
  Tetromino
   |--Cell[] cells
               |-- Cell * 4
 
  Tetris 俄羅斯方塊
   |-- Cell[][] wall = 20*10
   |-- Tetromino tetromino 正在下落的方塊
   |-- Tetromino nextOne 下一個方塊
   |-- int lines
   |-- int score
 
填寫初始化代碼(構造器)
  功能:
  toString()

繪製界面: 將對象的"數據"繪製到界面, 界面是數據的表現
  界面不是業務邏輯,
利用API實現界面的繪製工作!
 

5 算法設計,就是如何利用數據的計算實現軟件的功能
 
 4格方塊的初始形態:I S Z J L T O
  就在初始數據的數值狀態設計
 
  四格方塊的下落計算: 就是將每個格子的row+1
  就是將下落業務功能,轉換爲數字計算實現
 
 左右移動
 
 下落流程控制:方塊下落與牆之間的控制關係
   1 合理的文字流程描述
   2 分析文字描述中的功能(動作)爲方法
   3 用流程控制語句連接方法實現功能!
   4 嚴格測試結果! TestCase
   
   1) 如果"能夠下落", 就下落一步
   2) 否則就"着陸到牆裏面"
   3) 着落以後, "銷燬充滿的行", 並且記分
   4) "檢查遊戲結束"了嗎?
   5) 如果還能玩,就生成下一個方塊
 
  下落流程:也是一個功能,要封裝爲一個方法
   A 返回值:是無返回值的方法,有輸出,數據輸出
     結果就是tetromino 和 wall
   B 方法名:softDropAction 軟下落動作
   C 參數:無參數,實際的輸入數據tetromino和wall
   D 過程:
   
 
 
   詳細設計:
   A "能夠下落" 設計爲一個方法
         返回值(輸出): boolean 返回true 表示能夠下落
        方法名: canDrop  能夠下落嗎?
        參數(輸入):是牆和正在下落的方塊
          可以不作爲輸入參數定義,因爲是Tetris的實例變量
      如果將方法 canDrop 定義在 Tetris類中,就可以
      直接利用this獲得實例變量,進而獲得輸入數據
        過程:
        如果4格方塊的某個格子 的行座標到達19就不能
        下落了
       如果4格方塊的某個格子的下方牆上有方塊,就不能
       下落了。
 
 左右移動流程控制
   是一個方法, 是一個功能
   方法名:moveRightAction 向右移動動作
  輸入:tetromino 正在下落的方塊
  輸出:tetromino 正在下落的方塊
  過程:如果正在下落的方塊能夠向右移動就移動一下
    否則就原地不動。如果沒有超過邊界或者沒有壓住
    另外一個方塊,就可以移動。
    也就是:如果能夠移動,輸出的數據一定所有列+1
 
  算法:
   1) 先向右移動一次。
   2) 檢查移動結果是否出界或重回(壓住)
   3) 如果出界了或重回了就退回去(向左移動)
 
 

 分數計算

 界面的繪製
 
 鍵盤事件控制
 
 
 旋轉流程控制
 
 加速下降流程控制
 
 記分
 
 開始流程控制(Timer)
  主刷新(Timer)頻率: 1/100 秒
 0.5秒執行一次下落(是主刷新的50倍)
 
 利用定時器定時的調用 下落流程
 如果暫停時候, 就不執行下落流程來
 
 實現過程:
 1) 在Tetris類中添加屬性 Timer, 用於啓動主刷新頻率
 2) 在Tetris類中添加屬性 level 是下落的間隔次數
   如: 50 表示50次刷新以後,執行一次 下落, 如果減少,
   加快下落
 3)  在Tetris類中添加屬性 stepIndex 是下落計數
  每當 stepIndex % level == 0 時候 執行一次 下落
 4) 在Tetris類中添加屬性  pause 暫停執行狀態控制
 5) 在action() 方法添加主刷新Timer的啓動
   在主刷新中 添加代碼 控制下落
   
 
 暫停流程控制
 設置暫停狀態 true
 
 繼續流程控制
 
 結束流程控制
 
 修改鍵盤事件
 
 修改paintScore方法
 
 
如何繪製背景圖片
 1) 將圖片素材文件複製到 com.tarena.tetris 包中
 2) 在Tetris類中聲明靜態變量 background
 3) 使用靜態代碼塊 加載磁盤文件到內存對象
    將使用到 圖片讀取API: ImageIO
 4) 在paint 方法中繪製背景圖片
 
 可能出現的錯誤:
 1) 文件名/包搞錯了!
 
如何繪製 正在下落的方塊
 1) 先在action方法中生產 正在下落的方塊和下一個方塊
 2) 在paint方法中調用 paintTetromino()方法
 3) 在Tetris類中 增加paintTetromino()方法
    將正在下落的4格方塊的每個格子逐一繪製出來
 
如何使方塊移動
 1) 處理鍵盤事件(API), 獲得用戶何時按下 ->  <-
 2) 當按下 -> 按鍵時候, 執行當前4格方塊 向右移動 方法
 3) 向右移動 方法 會改變當前4格方塊的每個格子的列座標
 4) 調用繼承於JPanel類的 repaint(), 這個方法會盡快的
  調用 paint()
 5) paint() 會根據當前數據(已經被右移動改變的數據)
  繪製全部面板效果

 6) 用戶的感受就是, 方塊動了.


Cell.java
package 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 int getRow() {
		return row;
	}
	public void setRow(int row) {
		this.row = row;
	}
	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 void drop(){
		row++;
	}
	public void moveLeft(){
		col--;
	}
	public void moveRight(){
		col++;
	}
}
Tetromino.java
</pre><pre name="code" class="java">Tetromino

package Tetris;
import java.util.Arrays;
import java.util.Random;

/**
 * 4格方塊類 
 *
 */
public abstract class Tetromino {
	/** 4個格子,留給子類使用的屬性 protected*/
	protected Cell[] cells = new Cell[4];
	/** 隨機參數7種方塊之一 */
	public static Tetromino randomOne(){
		Random random = new Random(); 
		int type = random.nextInt(7);
		switch (type) {
		case 0: return new T();
		case 1: return new S();
		case 2: return new Z();
		case 3: return new I();
		case 4: return new L();
		case 5: return new J();
		case 6: return new O();
		}
		return null;
	}
	public void softDrop(){
		for (int i = 0; i < cells.length; i++) {
			Cell cell = cells[i];
			cell.drop();
		}
	}
	public void moveLeft(){
		for (int i = 0; i < cells.length; i++) {
			Cell cell = cells[i];
			cell.moveLeft();
		}
	}
	public void moveRight(){
		for (int i = 0; i < cells.length; i++) {
			Cell cell = cells[i];
			cell.moveRight();
		}
	}
	@Override
	public String toString() {
		return Arrays.toString(cells);
	}
	/** 存儲旋轉狀態的類,在Tetromino類中添加 */
	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;
		}
	}
	//在Tetromino 類上添加向右轉的方法
	private int index = 10000;
	//s0={row0,col0,row1,col1,row2,col2,row3,col3}
	protected State[] states;//在子類構造器中初始化 //{s0,s1,s2,s3};
	public void rotateRight() {
		//System.out.println(this);
		//輸入1:cells 當前方塊的數據
		//輸入2:sN : s1 s2 s3 s0 s1 s2 s3 ...
		//輸出 cells 旋轉以後的方塊數據
		//算法:cells[0]+sN -> cells
		//如何得到序列:1 2 3 0 1 2 3 0 1 2 3 0
		index ++;//10005
		//index%4 = ?;//1 2 3 0 1 2 3 0 
		//states[index%4]=?//s1 s2 s3 s0 s1 s2 s3...
		State s = states[index%states.length];
		//s = s1
		Cell o = cells[0];//找到軸 t0[0]
		int row = o.getRow();
		int col = o.getCol();
		cells[1].setRow(row + s.row1);//軸的row+s1[row1]=t1.row
		cells[1].setCol(col + s.col1);
		cells[2].setRow(row + s.row2);
		cells[2].setCol(col + s.col2);
		cells[3].setRow(row + s.row3);
		cells[3].setCol(col + s.col3);
		//System.out.println(this);
	}
	public void rotateLeft() {
		index --;//10005
		State s = states[index%states.length];
		Cell o = cells[0];//找到軸 t0[0]
		int row = o.getRow();
		int col = o.getCol();
		cells[1].setRow(row + s.row1);//軸的row+s1[row1]=t1.row
		cells[1].setCol(col + s.col1);
		cells[2].setRow(row + s.row2);
		cells[2].setCol(col + s.col2);
		cells[3].setRow(row + s.row3);
		cells[3].setCol(col + s.col3);
	}
}
/** T 型方塊,包內類 */
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[]{
			new State(0,0, 0,-1, 0,1, 1,0), //s0
			new State(0,0, -1,0, 1,0, 0,-1),//s1
			new State(0,0, 0,1, 0,-1, -1,0),//s2
			new State(0,0, 1,0, -1,0, 0,1)//s3
		}; 
	}
}
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)};
	}
}
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)};
	}
}
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)};
	}
}
class Z extends Tetromino{
	public Z() {
		cells[0] = new Cell(1,4,Tetris.Z);
		cells[1] = new Cell(1,5,Tetris.Z);
		cells[2] = new Cell(0,3,Tetris.Z);
		cells[3] = new Cell(0,4,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)};
	}
}
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) };
	}
}
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)};
	}
}




package 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;

/**
 * 俄羅斯方塊
 *
 */
public class Tetris extends JPanel {
	/** 遊戲的狀態 */
	private int state;
	/** 遊戲正在玩狀態 */
	public static final int PLAYING = 0;
	/** 遊戲暫停狀態 */
	public static final int PAUSE = 1;
	/** 遊戲結束狀態 */
	public static final int GAME_OVER = 2;
	/** 遊戲狀態提示信息 */
	private static final String[] STATE=
	  {"[P]Pause", "[C]Continue", "[S]Restart"};
	/** 下落速度控制 */
	int index = 0;  
	/** speed 的值越小下落速度越快 */
	int speed = 8; 
	
	/** 分數 */
	private int score;
	/** 行數, 銷燬的總行數 */
	private int lines;
	public static final int ROWS = 20;
	public static final int COLS = 10;
	/** 方塊牆 */
	private Cell[][] wall=new Cell[ROWS][COLS];
	/** 正在下落的方塊 */
	private Tetromino tetromino;
	/** 下一個方塊 */
	private Tetromino nextOne;
	
	/** 在Tetris 中添加背景圖屬性 */
	private static BufferedImage background;
	public static BufferedImage gameOverImage;
	public static BufferedImage T;
	public static BufferedImage S;
	public static BufferedImage Z;
	public static BufferedImage J;
	public static BufferedImage L;
	public static BufferedImage I;
	public static BufferedImage O;
	
	/** 讀取圖片文件到內存對象 */
	//將圖片複製到 com.tarena.tetris包中
	static {
		try{
			background=ImageIO.read(
				Tetris.class.getResource(
						"tetris.png"));
			gameOverImage=ImageIO.read(
					Tetris.class.getResource(
							"game-over.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"));
			J=ImageIO.read(Tetris.class
					.getResource("J.png"));
			L=ImageIO.read(Tetris.class
					.getResource("L.png"));
			O=ImageIO.read(Tetris.class
					.getResource("O.png"));
			I=ImageIO.read(Tetris.class
					.getResource("I.png"));
		}catch(Exception e){
			e.printStackTrace();
		}
	}
	/** 利用重寫方法修改父類JPanel */
	//paint:塗繪, Graphics:圖,畫筆
	public void paint(Graphics g){
		//draw: 繪
		g.drawImage(background, 0, 0, null);
		//g.drawString("Hello", 50, 50);
		g.translate(15, 15);
		paintWall(g);
		paintTetromino(g);
		paintNextOne(g);
		paintScore(g);//繪製分數
		g.translate(-15, -15);
		paintState(g);//繪製遊戲狀態
	}
	//繪製狀態
	private void paintState(Graphics g) {
		//如果state是 0 顯示 Pause 
		//如果state是 1 顯示 Continue
		//如果state是 2 顯示 Restart
		g.drawString(STATE[state], 309, 287);
		if(state == GAME_OVER){
			g.drawImage(gameOverImage,0,0,null);
		}
	}
	//繪製分數
	private void paintScore(Graphics g){
		int x = 293; 
		int y = 162;
		Font font = new Font(
				Font.SANS_SERIF, Font.BOLD, 30);
		g.setFont(font);
		g.setColor(new Color(0x667799)); 
		g.drawString("SCORE:"+score, x, y);
		y+=56;
		g.drawString("LINES:"+lines, x, y);
	}
	
	public void paintNextOne(Graphics g){
		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, y,
					null);
		}
	}
	/** 在Tetris類中填方法,繪製正在下落的方塊 */
	public void paintTetromino(Graphics g){
		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, y,
					null);
		}
	}
	public static final int CELL_SIZE=26;
	/** 在Tetris 添加畫牆的方法 */
	private void paintWall(Graphics g){
		for(int row=0; row<ROWS; row++){
			for(int col=0; col<COLS; col++){
				Cell cell = wall[row][col];
				int x=CELL_SIZE * col;
				int y=CELL_SIZE * row;
				if(cell==null){
					//g.drawRect(x, y, 
					//	CELL_SIZE, CELL_SIZE);
				}else{
					g.drawImage(cell.getImage(),
							x,y,null);
				}
			}
		}
	}
	
	/** 在Tetris 類中添加方法 */
	public void action(){
		state = PLAYING;
		//wall[19][2]=new Cell(19,2,T);
		//儘快的調用paint()重新繪製面板
		tetromino = Tetromino.randomOne();
		nextOne = Tetromino.randomOne();
		repaint();
		//創建鍵盤事件監聽器對象
		KeyListener l=new KeyAdapter(){
			@Override
			public void keyPressed(KeyEvent e) {
				int key = e.getKeyCode();
				//當state是值是PAUSE時候,只處理c按鍵
				//其它的按鍵的代碼被忽略不再執行
				if(key==KeyEvent.VK_Q){
					System.exit(0);//結束Java進程
				}
				switch(state){
				case GAME_OVER:
					if(key==KeyEvent.VK_S){
						restartAction(); 
					}
					return;
				case PAUSE:
					if(key==KeyEvent.VK_C){
						state=PLAYING;
					}
					//retuer以後的代碼不再執行了
					return;
				}
				switch(key){
				case KeyEvent.VK_DOWN:
					softDropAction();break;
				case KeyEvent.VK_RIGHT:
					moveRightAction();break;
				case KeyEvent.VK_LEFT:
					moveLeftAction();	break;
				case KeyEvent.VK_UP:
					rotateRightAction(); break;
				case KeyEvent.VK_Z:
					rotateLeftAction(); break;
				case KeyEvent.VK_SPACE:
					hardDropAction();	break;
				case KeyEvent.VK_P: 
					state = PAUSE; break;
				}
				repaint();
			}
		};
		//添加監聽器到當前面板(掛載)
		this.addKeyListener(l);
		//爲當前面板申請輸入焦點,有焦點才能接收鍵盤事件
		this.requestFocus();
		
		//在action方法中添加,主控制循環
		for(;;){
			if(state==PLAYING){
				index ++;
				if(index % speed==0){
					softDropAction();
				}
			}
			repaint();
			try{
				Thread.sleep(1000/10);
			}catch(Exception e){
			}
		}
	}
	/** 重新開始遊戲 */
	protected void restartAction() {
		score = 0;
		lines = 0;
		//將牆清空
		wall = new Cell[ROWS][COLS];
		tetromino = Tetromino.randomOne();
		nextOne = Tetromino.randomOne();
		speed = 8;
		index = 0;
		state = PLAYING;
	}
	/** 在Tetris類中添加加速下來功能
	 * 就是下落到不能下落爲止 
	 **/
	private void hardDropAction(){
		//當能夠下落就下落一步,到不能下落爲止
		while(canDrop()){
			tetromino.softDrop();
		}
		landIntoWall();
		destroyLines();
		if(!isGameOver()){
			tetromino = nextOne;
			nextOne = Tetromino.randomOne();
		}else{ //遊戲結束狀態
			state = GAME_OVER;
		}
	}
	
	/** 在Tetris 類中填寫方法 softDropAction */
	private void softDropAction(){
		boolean drop = canDrop();
		//System.out.println("t:"+tetromino); 
		//System.out.println("canDrop:"+drop); 
		if(drop){
			tetromino.softDrop();
		}else{
			// 入牆,land着陸 into進去 Wall 牆
			landIntoWall();//封裝 方塊入牆 的算法
			// 銷燬行 destroyLines,計分
			destroyLines();//封裝銷燬行的算法
			// 檢查是否結束
			// 如果遊戲沒有結束,產生下一個方塊
			if(!isGameOver()){
				tetromino=nextOne;
				nextOne=Tetromino.randomOne();
			}else{
				state = GAME_OVER;
			}
		}
		//System.out.println(tetromino); 
	}
	/** 檢查遊戲是否結束
	 * 如果下一個將要出場的方塊的牆上位置被佔用
	 * 了就結束了!
	 *  */
	private boolean isGameOver() {
		Cell[] cells = nextOne.cells;
		for(int i=0; i<cells.length; i++){
			Cell cell = cells[i];
			int row=cell.getRow();
			int col=cell.getCol();
			if(wall[row][col]!=null){
				return true;
			}
		}
		return false;
	}
	
	/** 銷燬牆上已經滿的行, 並且計分 */
	private int[] scoreTable = {0,1,5,20,100};
	//               lines      0 1 2  3  4
	private void destroyLines() {
		int lines = 0;//當前銷燬的行數
		for(int row=0; row<ROWS; row++){
			if(fullCells(row)){
				deleteRow(row);
				lines++;
			}
		}
		this.score += scoreTable[lines];
		//速度控制,每增加1000分時候,速度
		//的值會變小,速度加快
		//速度的最小值是 1,不能比1更小了
		speed = Math.max(8-score/1000, 1);
		this.lines += lines;
	}
	/** 檢查牆上row行是否是"滿格子",true:滿了 */
	private boolean fullCells(int row) {
		Cell[] line = wall[row];
		for(int i=0; i<line.length; i++){
			Cell cell = line[i];
			if(cell==null){
				return false;
			}
		}
		return true;
	}
	/** 刪除牆上 row對應的行 */
	private void deleteRow(int row) {
		for(int i=row; i>=1; i--){
			System.arraycopy(wall[i-1], 0, 
					wall[i], 0, COLS);
		}
		Arrays.fill(wall[0], null);
	}
	/** 將方塊着陸到牆上 */
	private void landIntoWall() {
		Cell[] cells = tetromino.cells;
		for(int i=0; i<cells.length; i++){
			Cell cell = cells[i];
			int row = cell.getRow();
			int col = cell.getCol();
			wall[row][col]=cell;
		}
	}
	/** 在Tetris類中添加方法 canDrop() */
	private boolean canDrop(){
		//輸入是 wall tetromino 
		//先檢查tetromino是否到達底部
		Cell[] cells = tetromino.cells;
		for(int i=0; i<cells.length; i++){
			//i = 0 1 2 3 
			//cell“引用”了4格方塊的每個格式
			Cell cell = cells[i];
			//如果某個格子的行是19,就不能下落了
			if(cell.getRow() == ROWS-1){
				//System.out.println("到達底部");
				return false;
			}
		}
		
		//檢查tetromino的下方牆上是否有方塊
		for(int i=0; i<cells.length; i++){
			Cell cell = cells[i];
			int row = cell.getRow();
			int col = cell.getCol();
			if(wall[row+1][col]!=null){
				//System.out.println(cell+"的下方有方塊");
				return false;
			}
		}
		return true;
	}
	
	/** Tetris 類中添加向右移動流程控制方法 */
	private void moveRightAction(){
		tetromino.moveRight();
		//outOfBounds出界 concide:重合
		if(outOfBounds() || concide()){
			tetromino.moveLeft();
		}
	}
	private void moveLeftAction(){
		tetromino.moveLeft();
		//outOfBounds出界 concide:重合
		if(outOfBounds() || concide()){
			tetromino.moveRight();
		}
	}
	//檢查當前下落的方塊,是否出界,true:出界了
	private boolean outOfBounds(){
		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)){
				return true;
			}
		}
		return false;
	}
	//檢查當前下落的方塊,是否與牆磚重合,true重合
	private boolean concide(){
		Cell[] cells = tetromino.cells;
		//for (int i = 0; i < cells.length; i++) {
		//	Cell cell = cells[i];
		for(Cell cell: cells){
			int row = cell.getRow();
			int col = cell.getCol();
			if(wall[row][col]!=null){
				return true;
			}
		}
		return false;
	}
	/** 在tetris 類中增加旋轉流程控制方法 */
	public void rotateRightAction(){
		//rotate:原地轉
		tetromino.rotateRight();
		if(outOfBounds() || concide()){
			tetromino.rotateLeft();
		}
	}
	public void rotateLeftAction(){
		//rotate:原地轉
		tetromino.rotateLeft();
		if(outOfBounds() || concide()){
			tetromino.rotateRight();
		}
	}
	
	public static void main(String[] args) {
		//Frame 框,相框,代表窗口框 
		JFrame frame = new JFrame();
		//panel 代表面板
		Tetris panel = new Tetris();
		//Background 背景
		panel.setBackground(Color.YELLOW);
		//窗口中添加面板
		frame.add(panel);
		frame.setUndecorated(true);//去掉窗口邊框
		frame.setSize(535, 580);
		//居中
		frame.setLocationRelativeTo(null);
		//點擊X時候同時關閉程序
		frame.setDefaultCloseOperation(
				JFrame.EXIT_ON_CLOSE);
		//顯示窗口框
		frame.setVisible(true);
		//儘快的調用 paint()方法繪製顯示界面
		//顯示窗口以後,執行 啓動方法action
		panel.action();
	}
}







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章