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

今天,我们来使用Eclipse制作一个小游戏《Tetris~》(未完待续)

                                                                                            ——Written by Leonardo

一、游戏机制

a)    7种小块儿随机生成

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

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

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

            

        

b)   墙与边界

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

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

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

 

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.单个的小方格——Cell

        俄罗斯方块中的最小单位:小方格
 * 特征:属性
 * row--行号 
 * col--列号
 * image--对应图片
 * 
 * 行为:
 * 向左移动
 * 向右移动
 * 向下移动

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

记得导包(import)

2.每个不同形状的四小块儿组(7种形状)——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.四个小方格组成一个块儿租——Tetromino

   四格方块作为一个整体

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

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

        修饰词protected【子类可访问】

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

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

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

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

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

         按照顺时针顺序:

   


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类

/*
 * 俄罗斯方块中的最小单位:小方格
 * 特征:属性
 * 		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类

/*
 * 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;
		
	}
	
	
}


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

下面以“T”形状为例

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);
		cells[2]= new Cell(0,5,Tetris.T);
		cells[3]= new Cell(1,4,Tetris.T);
		state = new State[4];
		//第一个状态
		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(未完待续)

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

	
	//图片加载:静态
	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;
				}
				//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
				currentOne = nextOne;
				nextOne = Tetromino.randomOne();
			}
			/*
			 * 下落之后,重新进行绘制
			 * 才会在看到下一步的动作
			 * repaint方法,也是JPanel类的方法
			 * 此方法调用paint
			 */
			repaint();
		}
	}
	
	protected void moveRightAction() {
		currentOne.moveRight();;
		//先判断越界,再判断重合
		if(outOfBounds()||coincide()){
			currentOne.moveLeft();
		}
		
	}
	//监听使用left键控制向左的行为
	protected void moveLeftAction() {
		currentOne.moveLeft();
		//先判断越界,再判断重合
		if(outOfBounds()||coincide()){
			currentOne.moveRight();
		}
	}
	//判断越界
	public boolean outOfBounds(){
		Cell[] cells = currentOne.cells;
		for(Cell c : cells){
			int col = c.getCol();
			if(col<0 || col>9){
				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();
			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();
	}
}


    

猜你喜欢

转载自blog.csdn.net/adidas74891496/article/details/80464245