JAVA面向对象小程序(二):俄罗斯方块s

今天,我们来使用Eclipse制作一个小游戏《Tetris~》

资源在文末

一、游戏机制

 

a)    7种小块儿随机生成

               i.         一块放置在顶部(随时间下降)

              ii.         另一块置于右侧,提示下一个随机生成块

             iii.         七种形状:“O、L、J、I、T、S、Z”

            

        

b)   墙与边界

               i.         小块的移动范围在边框之内;

              ii.         第一个小块下降到最底部,触底碰“墙”,自身嵌入“墙”;

             iii.         下一小块儿的其中某个小方格,底部碰到“墙”,自身嵌入“墙”;

 JAVA面向对象小程序(二):俄罗斯方块s_第1张图片

 

c)    小块儿的下降、左移、右移、旋转与平移

 

              i.         添加键盘监听事件

             ii.         不允许方块的越界

             iii.         确定旋转中心,以旋转中心的相对位置做其余小块的行列号修改

 

d)   消除与积分

               i.         当某一排(col)被小方格占满,消除某一排

              ii.         被消除排 的上方所有小方块整体下移

             iii.         积分累加

f)   游戏的三种状态:下落、暂停与重新开始

g)    游戏结束的条件

               i.         最上方的生成区域被占,GameOver

 

二、设计需求 / 编程思想

【遵循javabean规范】

       (1)添加两个(至少)构造器【一个无参数,一个有参数】
       (2)属性一般为私有化【外部类访问不到】
       (3)提供公有的get/set 【提供访问途径】
       (4)toString用来描述对象信息
           重写toString属性的信息
       (5)重写equals 【对象的值】
       (6)重写hashCode 【对象的哈希码(地址)】

 

 

几大部件:【我们要把这几个家伙抽象到类 (class) 中】
 

1.单个的小方格——class  Cell

        俄罗斯方块中的最小单位:小方格
        * 特征:属性
        * row--行号 

        * col--列号
 
       * image--对应图片
 
       
 
       * 行为:
 
       * 向左移动
 
       * 向右移动
 
       * 向下移动

对于image属性,我们可以添加BufferImage类的对象image

记得导包(import)

2.每个不同形状的四小块儿组(7种形状)——class  X  extends Tetromino

        形状有:O、L、J、I、T、S、Z

* Cell类继承4块整体(Trtromino)的属性

* 构造器初始化

         * Cell[] cells = new Cell[4];

         * Cell(行号,列号,BufferedImage image)构造器

        整体的四种状态(旋转),其中S、Z、I型可以只有两种状态,O只有一种状态

         *states = new State[4];

         *

*/

3.四个小方格组成一个块儿组——class  Tetromino

 

   四格方块作为一个整体

   共同特征:七种组合的父类

        cell--四个方块(Cell 数组存放)--protected Cell[ ] cells = new Cell[4]; //4个小方格创建,初值为null

        修饰词protected【子类可访问】

====================================================================

共同行为:向左向右..
 * 属性:
 * 4cells 
 * 行为:
 * 重写toString
 * 
 * moveLeft()
 * moveRight()

 * softDrop()软下降——【按键盘'↓'只降一格】

=====================================================================

         四种状态:(相对与旋转中心)

         按照顺时针顺序:

 JAVA面向对象小程序(二):俄罗斯方块s_第2张图片 JAVA面向对象小程序(二):俄罗斯方块s_第3张图片 JAVA面向对象小程序(二):俄罗斯方块s_第4张图片JAVA面向对象小程序(二):俄罗斯方块s_第5张图片

 

4.主类——Tetris--------- extends JPanel

        此类作为程序的入口,加载静态资源【图片缓冲区】

        拥有主方法main(){}

        在主方法中添加:

        1.JFrame(窗体框框)

        2.JPanel(面板&画笔)

        (1).面板会自动调用绘制方法paint(Graphics g)
        (2).重写paint方法,绘制图片背景
        (3).绘制网格和嵌入墙中的方块

        3.在Panel中调用主逻辑start( )

====================================================================

        把游戏的主逻辑封装入start( )方法中

        1.KeyListener(开启键盘监听)

              封装匿名内部类  keyPressed(KeyEvent e){

                                        【注意:k要小写】

                                        【Action后马上重绘】
                                          repaint();

                                       };

        2.面板添加监听事件

        3.当前对象设置成焦点

        4.设置程序睡眠【每300ms生成新的小块儿】

三、代码实现

 

首先强调,Leo遵循的编写顺序为:

    (1).Cell类——小细胞

    (2).Tetromino类——四个小细胞组成一个整体

    (3).七种形状类“O、L、J、I、T、S、Z”——继承Tetromino的属性和行为

    (4).最后是主类Tetris——作为游戏的主逻辑程序的入口

1.Cell类

import java.awt.image.BufferedImage;
/*
 * 俄罗斯方块中的最小单位:小方格
 * 特征:属性
 * 		row--行号 
 * 		col--列号
 * 		image--对应图片
 * 
 * 		行为:
 * 		向左
 * 		向右
 * 		向下
 */
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 left(){
		col --;
	}
	public void right(){
		col ++;
	}
	public void drop(){
		row ++; 
	}



	@Override
	public String toString() {
		return "(" + row + ", " + col + ")" ;
	}
	
}

2.Tetromino类

 

import java.util.Arrays;
/*
 * 4格方块作为一个整体
 * 属性:
 * 		4cells 
 * 行为:
 * 		重写toString
 * 
 * 		moveLeft()
 * 		moveRight()
 * 		softDrop()软下降,按↓箭头只下移一个单位
 */
public class Tetromino {
	protected Cell[] cells = new Cell[4];	//4个null
	//每个方块都向左  一个单位移动
	public void moveLeft(){
		for (int i = 0; i < cells.length; i++) {
			Cell cell = cells[i];
			cell.left();
		}
	}
	//每个方块都向右  一个单位移动
	public void moveRight(){
		for (int i = 0; i < cells.length; i++) {
			Cell cell = cells[i];
			cell.right();
		}
	}
	//每个方块都向下  一个单位移动
	public void softDrop(){
		//强循环数组遍历
		for(Cell c:cells){
			c.drop();
		}
	}
	@Override
	public String toString() {
		return  Arrays.toString(cells) ;
	}
	public static Tetromino randomOne(){
		//随机生成4格方块
		//四小块作为一整体,t赋为null
		Tetromino t = null ;
		int num = (int)(Math.random()*7);
		switch(num){
				case 0:t=new T();break;
				case 1:t=new O();break;
				case 2:t=new L();break;
				case 3:t=new J();break;
				case 4:t=new I();break;
				case 5:t=new Z();break;
				case 6:t=new S();break;
		}
		return t;
		
	}
	public static Tetromino randomOne(){
		//随机生成4格方块
		//四小块作为一整体,t赋为null
		Tetromino t = null ;
		int num = (int)(Math.random()*7);
		switch(num){
				case 0:t=new T();break;
				case 1:t=new O();break;
				case 2:t=new L();break;
				case 3:t=new J();break;
				case 4:t=new I();break;
				case 5:t=new Z();break;
				case 6:t=new S();break;
		}
		return t;	
	}
	
	//旋转四格方块:顺时针
	public void rotateRight(){
		//旋转一次,计数器自增1
		count++;
		State s = state[count%state.length];
		//需要获取轴的行号和列号
		Cell c = cells[0];
		int row = c.getRow();
		int col = c.getCol();
		cells[1].setRow(row+s.row1);
		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);
	}
	//旋转四格方块:逆时针
	public void rotateLeft(){
		//旋转一次,计数器自减1
		count--;
		State s = state[count%state.length];
		//需要获取轴的行号和列号
		Cell c = cells[0];
		int row = c.getRow();
		int col = c.getCol();
		cells[1].setRow(row+s.row1);
		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);
	}
	/*
	 * 定义内部类,用于封装每次
	 * 旋转后的相对于轴的其他三个小格子的行列号
	 */
	public class State{
		/*
		 * 设置8个坐标int
		 * 分别存储四格方块元素的相对位置
		 */
		int row0,col0;
		int row1,col1;
		int row2,col2;
		int row3,col3;
		public State() {}
		public State(int row0, int col0, int row1, int col1, int row2, int col2, int row3, int col3) {
			super();
			this.row0 = row0;
			this.col0 = col0;
			this.row1 = row1;
			this.col1 = col1;
			this.row2 = row2;
			this.col2 = col2;
			this.row3 = row3;
			this.col3 = col3;
		}
	
}

3.七种形状方块儿 X 类——extends Tetromino

下面以“T”形状为例

public class T extends Tetromino{
	/*
	 * 继承4块整体(Trtromino)的属性
	 * 构造器初始化
	 * T型四格方块的位置
	 * 000000
	 *   00
	 */
	public T(){
		/*
		 * Cell[] cells = new Cell[4];
		 * Cell(行号,列号,BufferedImage image)构造器
		 */
		cells[0]= new Cell(0,4,Tetris.T);    // 初始状态旋转中心的坐标
		cells[1]= new Cell(0,3,Tetris.T);    // 初始状态1号小细胞..
		cells[2]= new Cell(0,5,Tetris.T);    // 初始状态2号...
		cells[3]= new Cell(1,4,Tetris.T);    // ...
		state = new State[4];                // 【注意】,不要重新声明State,在Tetromino.class中已声明过了
		//第一个状态
		state [0] = new State(0,0, 0,-1, 0,1, 1,0);
		//第二个状态
		state [1] = new State(0,0, -1,0, 1,0, 0,-1);
		state [2] = new State(0,0, 0,1, 0,-1, -1,0);
		state [3] = new State(0,0, 1,0, -1,0, 0,1);
	}
}

 

4.Tetris主类——extends JPanel(待续)


import java.awt.Graphics;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
/*
 * 俄罗斯方块的主类:
 * 前提:	一块面板JPanel,可以嵌入窗口
 * 		面板上自带一个画笔,有一个功能,自动绘制。
 * 		调用了JPanel
 * 加载静态资源
 * 
 */
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{
	/* 
	 * 俄罗斯方块的主类
	 * 功能:	1.加载静态资源(背景图,7种形状的原始图)
	 * 		2.设置窗口
	 * 		3.重写JPanel中的paint()方法,构造器中添加画笔Graphics g
	 * 前提:是一块面板Panel,可被嵌入窗口
	 * 属性:1.正在下落的方块
	 * 		2.即将下落的方块
	 * 		3.添加墙(方格),画举矩形
	 * 
	 */
	//属性:正在下落的方块
	private Tetromino currentOne = Tetromino.randomOne();
	//属性:即将下落的方块
	private Tetromino nextOne = Tetromino.randomOne();
	//属性:墙 20行10列的方格
	private Cell[][] wall= new Cell[20][10];
	//属性:统计分数
	int [] scoresPool = {0,1,3,5,10};
	private int totalScore = 0;
	private int totalLine = 0;
	//图片加载:静态
	private static final int CELL_SIZE = 26;
	public static BufferedImage T;
	public static BufferedImage I;
	public static BufferedImage O;
	public static BufferedImage J;
	public static BufferedImage L;
	public static BufferedImage S;
	public static BufferedImage Z;
	public static BufferedImage background;
	
	static {
		try{
			/*
			 * getResource(String url)
			 * url:加载图片的路径
			 * 操作:将图片文件拖入package Tetris_Day01
			 * 相对位置是同一个包下
			 */
			T = ImageIO.read(Tetris.class.getResource("T.png"));
			I = ImageIO.read(Tetris.class.getResource("I.png"));
			O = ImageIO.read(Tetris.class.getResource("O.png"));
			J = ImageIO.read(Tetris.class.getResource("J.png"));
			L = ImageIO.read(Tetris.class.getResource("L.png"));
			S = ImageIO.read(Tetris.class.getResource("S.png"));
			Z = ImageIO.read(Tetris.class.getResource("Z.png"));
			background = ImageIO.read(Tetris.class.getResource("tetris.png"));
		}catch (Exception e) {
			e.printStackTrace();
		}
	}
	/*
	 * 重写JPanel中的paint()方法
	 */
	public void paint(Graphics g){
		//绘制背景(图片,横坐标,纵坐标,observer)
		/*
		 * g是画笔
		 * g.drawImage(image,x,y,null)
		 * x,y为开始绘制时的坐标
		 */
		g.drawImage(background, 0, 0, null);
		//平移坐标轴
		g.translate(15, 15);
		//绘制墙
		paintWall(g);
		//绘制正在下落的4格方块
		paintCurrentOne(g);
		//绘制下一个将要绘制的4格方块
		paintNextOne(g);
		
	}
	/*
	 * 绘制下一个将要绘制的4格方块
	 * 绘制到右上角相应区域
	 */
	public void paintNextOne(Graphics g) {
		//获取Next对象的4个小方格
		Cell[] cells = nextOne.cells;
		for (Cell c : cells){
			//获取每一个元素的行列号
			int row = c.getRow();
			int col = c.getCol();
			//横坐标
			int x = col*CELL_SIZE+260;
			int y = row*CELL_SIZE+26;
			g.drawImage(c.getImage(), x, y, null);
		}
	}
	
	public void paintCurrentOne(Graphics a){
		//Cell类型的数组指向    4方格整体的对象currentOne的cells数组
		Cell[] cells = currentOne.cells; 
		for (Cell c : cells) {
			int x = c.getCol()*CELL_SIZE;
			int y = c.getRow()*CELL_SIZE;
			a.drawImage(c.getImage(), x, y, null);
		}
	}
	public void paintWall(Graphics a){
		//外层循环控制行数
		//内层循环控制列数
		for (int i = 0; i < 20; i++) {
			for (int j = 0; j < 10; j++) {
				int x = j * CELL_SIZE;
				int y = i * CELL_SIZE;
				Cell cell = wall[i][j];
				if(cell == null)
				{
					a.drawRect(x , y, CELL_SIZE, CELL_SIZE);
				}else {
					a.drawImage(cell.getImage(),x,y,null);
				}
				
			}
		}
	}
	//所有的主逻辑
	public void start(){
		//开启键盘监听事件
		KeyListener l = new KeyAdapter(){
			
			//匿名内部类
			@Override
			public void keyPressed(KeyEvent e) {
				int code = e.getKeyCode();
				switch(code){
				case KeyEvent.VK_DOWN:
					softDropAction();break;
				case KeyEvent.VK_LEFT:
					moveLeftAction();break;
				case KeyEvent.VK_RIGHT:
					moveRightAction();break;
				case KeyEvent.VK_UP:
					rotateRightAction();break;
				case KeyEvent.VK_SPACE:
					handDropAction();
				}
				//Action后马上重绘
				repaint();
			}
		};
		//面板添加监听事件
		this.addKeyListener(l);
		//当前对象设置成焦点
		this.requestFocus();
		while(true){
			/*
			 * 当程序运行到此,进入睡眠状态
			 * 睡眠时间为300毫秒
			 */
			try {
				Thread.sleep(900);
			} catch (InterruptedException e) {
				//打断异常
				e.printStackTrace();
			}
			//判断可以下落
			if(canDrop()){
				currentOne.softDrop();
			}else{
				landToWall();
				//将下一个下落的四格方块赋值给CurrentOne
				destroy();
				currentOne = nextOne;
				nextOne = Tetromino.randomOne();
			}
			/*
			 * 下落之后,重新进行绘制
			 * 才会在看到下一步的动作
			 * repaint方法,也是JPanel类的方法
			 * 此方法调用paint
			 */
			repaint();
		}
	}
	/*
	 *满一行就消除,上方所有方块向下移 
	 */
	public void destroy(){
		//统计销毁行的次数
		int lines = 0;
		
		Cell[] cells = currentOne.cells;
		for (Cell c : cells){
			//取出每个元素的行号
			int row = c.getRow();
			while (row<20){
				if(isFullLine(row)){
					lines++;
					wall[row] = new Cell[10];
					for(int i = row;i>0;i--){
						System.arraycopy(wall[i-1], 0, wall[i], 0, 10);
					}
					wall[0] = new Cell[10];
				}
				row++;
			}		
		}
		totalScore += scoresPool[lines];
		
	}
	//判断行是否为
	public boolean isFullLine(int row) {
		Cell[] line = wall[row];
		
		for(Cell r : line){
			if(r == null){
				return false;
			}
		}
		return true;
	
		
	}
	//顺时针旋转的动作
	public void rotateRightAction() {
		currentOne.rotateRight();
		//先判断越界,再判断重合
		if(outOfBounds()||coincide()){
			currentOne.rotateLeft();
		}				
	} 
	
	protected void moveRightAction() {
		currentOne.moveRight();
		//先判断越界,再判断重合
		if(outOfBounds()||coincide()){
			currentOne.moveLeft();
		}
		
	}
	//监听使用left键控制向左的行为
	protected void moveLeftAction() {
		currentOne.moveLeft();
		//先判断越界,再判断重合
		if(outOfBounds()||coincide()){
			currentOne.moveRight();	//越界,到-1,再移动回来
		}
	}
	//判断越界
	public boolean outOfBounds(){
		Cell[] cells = currentOne.cells;
		for(Cell c : cells){
			int col = c.getCol();
			int row = c.getRow();
			if(col<0 || col>9 || row>19 || row<0){
				return true;
			}
		}
		return false;
	}
	//判断重合
	public boolean coincide(){
		Cell[] cells = currentOne.cells;
		for(Cell c:cells){
			int row = c.getRow();
			int col = c.getCol();
			if(wall[row][col]!=null){
				return true;
			}
		}
		return false;
	}
	public void softDropAction(){
		if(canDrop()){
			currentOne.softDrop();
		}else{
			//将下一个下落的四格方块赋值给CurrentOne
			landToWall();
			destroy();
			currentOne = nextOne;
			nextOne = Tetromino.randomOne();
		}
	}
	
	public void handDropAction(){
//		while(canDrop()){
//			
//		}
		for(;;){
			if(canDrop()){
				currentOne.softDrop();
			}else{
				break;
			}
		}
		landToWall();
		destroy();
		currentOne = nextOne;
		nextOne = Tetromino.randomOne();
	} 
	public boolean canDrop(){
		Cell[] cells = currentOne.cells;
		/*
		 * 4格拿出来,遍历
		 */
		
		for(Cell c:cells){
			/*
			 * 获取每个元素的行、列号
			 * 判断:只要有一个元素的下一行有块
			 * 或 有一个元素到最后一行
			 * 就不能下落了
			 */
			int row = c.getRow();
			int col = c.getCol();
			if(row == 19){
				return false;
			}
			if(wall[row+1][col]!=null){
				return false;
			}
		}
		return true;
	}
	public void landToWall(){
		Cell[] cells = currentOne.cells;
		/*
		 * 4格拿出来,遍历
		 */
		for(Cell c:cells){
			int row = c.getRow();
			int col = c.getCol();
			wall[row][col] = c;
		}
	}
	public static void main(String[] args) {
																//1.创建窗口对象
		JFrame frame = new JFrame("Tetris~");	
		frame.setVisible(true);									//2.可见性
		frame.setSize(535, 580);								//3.size
		frame.setLocationRelativeTo(null); 						//4.居中
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 	//5.关闭并终止游戏
		
		Tetris panel = new Tetris();							//6.创建游戏界面,即面板
		frame.add(panel);										//7.将面板嵌入窗口
//		panel.setBackground(Color.yellow); 						//8.先把面板画成yellow
		//其实调用的是JPanel中的paint()方法
		
		//游戏的主要逻辑封装在start()
		panel.start();
	}
}

链接:https://pan.baidu.com/s/1wswwkTyDRdNGs_TUKYrlPw 密码:t5tj

将资源解压后拷贝至package下 

JAVA面向对象小程序(二):俄罗斯方块s_第6张图片


    

 

你可能感兴趣的:(Java笔记)