Java写俄罗斯方块,了解一下

Java俄罗斯方块目录:

  1. Java俄罗斯方块 ---(一)游戏场景篇
  2. Java俄罗斯方块 ---(二)游戏操作与逻辑篇
  3. Java写俄罗斯方块(完整版)

简要分析:

俄罗斯方块的规则在这里就不细说了,大家都知道,控制方块的移动,到底即停,当方块能填满一行时,便消除那一行的方块,并计分...

我们将用Swing来完成整个游戏的制作。

首先我们来看看游戏运行时的图片。

 

(游戏图片 )

 

上图是游戏制作过程中我截的一张图片,从上图中,我们可以把游戏分为多个区域:

1)正在下落的四格方块

2)下一个下落的四格方块

3)游戏有一个主场景区,即左边的网格线区域

4)计分区,暂时还没绘制

根据俄罗斯方块的规则,我们知道,一个四格方块,是可以左右和往下移动的,并且还能变换形状。

然后我们在来看一张图片:

 

(经典俄罗斯方块的方块形状)

 

从上图我们可以看出来,每个方块都由四个小方格通过不同的排列组成,并且,我们可以把游戏主场景区想象成用二维数组来表示的区域,事实上,确实是用二维数组来制作游戏主区域。

在这里,我们暂且把这个区域称为------墙(Wall)

把每个小方格称为------单元格(Cell)

每个不同的形状其实就是4个单元格按不同顺序组合而来,所以,我们可以用不同的坐标来表示不同的形状。

我们可以根据四格方块的不同形状,给他们一个名字:I,T,L,J,S,Z,O

所以,在这里我们用面向对象的思想,分别定义Cell类,和七个四格方块类。

Cell()类:

    共同特征:行号,列号,图片

    共同行为:向左,向右,向下移动,提供JavaBean相关规范,JavaBean规范就是程序员在定义类时,默认遵守的一种规范如:

  1.     添加两个构造器,无参,全参
  2. 属性一般都是私有化(封装)
  3. 提供公有(public)的get/set方法
  4. 重写toString()方法,toString方法默认返回地址信息,重写后用来描述属性的信息
  5. 重写equals方法和hashCode方法
import java.awt.image.BufferedImage;

/*
 * 俄罗斯方块中的最小单位:方格(细胞)
 * 特征(属性):
 * 		row--行号
 * 		col--列号
 * 		image--对应的图片
 *  行为(方法)
 *  	left()
 *  	right()
 *  	drop()
 */
public class Cell {
	private int row;
	private int col;
	private BufferedImage image;
	
	@Override
	public String toString() {
		return "(" + row + ", " + col + ")";
	}

	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 Cell() {}
	
	public Cell(int row, int col, BufferedImage image) {
		this.row = row;
		this.col = col;
		this.image = image;
	}
	
	/*向左移动*/
	public void left() {
		col--;
	}
	/*向右移动*/
	public void right() {
		col++;
	}
	/*向下移动*/
	public void drop() {
		row++;
	}
}

 

根据每个四格方块的共性,即移动等。我们可以让七个四格方块继承一个父类------Tetromino类,用于描述四格方块的行为,左移,右移,下落,生成一个方块。

 

Tetromino()类代码:

共同特征:cells--四个小方块(用数组表示)--权限修饰词protected

共同行为:向左,向右,向下移动,提供JavaBean规范

添加randomOne()方法---用来随机生成一个四格方块

import java.util.Arrays;

/*
 * 四格方块
 * 属性:
 * 		--cells----四个方块
 * 行为:
 * 		moveLeft()
 * 		moveRight()
 * 		softDrop()
 */
public class Tetromino {
	protected Cell[] cells=new Cell[4];
	/*四格方块向左移动
	 * 实际上:就是每个方块向左移动
	 */
	
	/*四格方块向左移动*/
	public void moveLeft() {
		for(Cell c:cells)
			c.left();
	}
	/*四格方块向右移动*/
	public void moveRight() {
		for(Cell c:cells)
			c.right();
	}
	/*四格方块向下移动*/
	public void softDrop() {
		for(Cell c:cells)
			c.drop();
	}
	@Override
	public String toString() {
		return "[" + Arrays.toString(cells) + "]";
	}
	/*随机生成一个四格方块*/
	public static Tetromino randomOne() {
		Tetromino t = null;
		int num=(int)(Math.random()*7);//random的范围是0.00-0.99(即包含0不包含1)
		switch (num) {
		case 0:t=new T();break;
		case 1:t=new O();break;
		case 2:t=new I();break;
		case 3:t=new J();break;
		case 4:t=new L();break;
		case 5:t=new S();break;
		case 6:t=new Z();break;
		}
		return t;
	}
}

 

然后在创建7个小方块的类,来继承Tetromino类,也就是把他们的共同点写到一个父类中,子类在描述各自的特性。

 

根据父类Tetromino,定义出七种子类,给属性赋具体元素。

L类代码:

public class L extends Tetromino{
	/*
	 * 提供构造器进行初始化
	 * L型的四格方块的位置
	*/
	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);
	}
}

其他6个按照方块所在的坐标编写,点击查看坐标,这样一来,便写好了最基本的类。

然后在创建一个主类,这个类就是游戏的入口,也是游戏的所有逻辑所在。

Tetris类代码:

提供静态属性,加载静态资源,游戏图片,场景等--静态代码块

 

  1. 面板会自动调用绘制方法paint(Graphics g)
  2. 重写paint方法,绘制图片背景,绘制网格和嵌入墙中的方块。
  3. 绘制网格和嵌入墙中的方块。paintWall(Graphics g)
        提供属性wall---是一个Cell类型的二维数组,20行,10列
        属性CELL_SIZE---一个方块的宽度
  4. 提供属性
        currentOne---正在下落的四格方块
        nextOne---下一个将要下落的四格方块
  5. 提供绘制正在下落的方块的方法
        paintCurrentOne(Graphics g)
        在重写的paint方法中取调用
  6. 编写start()方法,用于封装游戏主要逻辑
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 javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;

/*
 * 俄罗斯方块的主类:
 * 前提:必须是一块面板Jpanel,可以嵌入窗口
 * 面板上自带一个画笔,有一个功能:自动绘制
 * 其实是调用了JPanel里的paint()方法
 * 
 * 
 * (1)加载静态资源
 */
public class Tetris extends JPanel{
	
	/*属性:正在下落的四格方块*/
	private Tetromino currentOne = Tetromino.randomOne();
	/*属性:将要下落的四格方块*/
	private Tetromino nextOne = Tetromino.randomOne();
	/*属性:墙,20行 10列的 表格  宽度为26*/
	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;
	public static  BufferedImage gameover;
	static {
		try {
			/*
			 * getResource(String url)
			 * url:加载图片的路径
			 * 相对位置是同包下
			 */
			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"));
			gameover = ImageIO.read(Tetris.class.getResource("game-over.png"));
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/*
	 * 重写JPanel类中的paint(Graphics g)方法
	 * 
	 */
	public void paint(Graphics g) {
		//绘制背景
		/*
		 * g:画笔
		 * g.drawImage(image,x,y,null)
		 * image:绘制的图片
		 * x:开始绘制的横坐标
		 * y:开始绘制的纵坐标
		 */
		g.drawImage(background, 0,0, null);
		//平移坐标轴
		g.translate(15, 15);
		//绘制墙
		paintWall(g);
		//绘制正在下落的四格方块
		paintCurrentOne(g);
		//绘制下一个将要下落的四格方块
		paintNextOne(g);
	}
	/*
	 * 绘制下一个将要下落的四格方块
	 * 绘制到面板的右上角的相应区域
	 * @param g
	 */
	public void paintNextOne(Graphics g) {
		//获取nextOne对象的四个元素
		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);
		}
	}
	
	/*绘制正在下落的四格方块
	 * 取出数组的元素
	 * 绘制元素的图片
	 * 横坐标x
	 * 纵坐标y 
	 */
	public void paintCurrentOne(Graphics g){
		Cell[] cells = currentOne.cells;
		for(Cell c:cells)
		{
			int x = c.getCol()*CELL_SIZE;
			int y = c.getRow()*CELL_SIZE;
			g.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() {
			/*
			 * KeyPressed()
			 * 是键盘按钮 按下去所调用的方法
			 */
			@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:
				
					break;
				default:
					break;
				}
				//按一次重新绘制一次
				repaint();
			}
			

			
		};
		//面板添加监听事件对象
		this.addKeyListener(l);
		//面板对象设置成焦点
		this.requestFocus();
		
		while(true) {
			/*
			 * 当程序运行到此,会进入睡眠状态,
			 * 睡眠时间为300毫秒,单位为毫秒
			 * 300毫秒后,会自动执行后续代码
			 */
			try {
				Thread.sleep(300);
			} catch (InterruptedException e) {
				// 抓取打断异常
				e.printStackTrace();
			}

			if(canDrop()) {
				currentOne.softDrop();
			}
			else {
				landToWall();
				//将下一个下落的四格方块赋值给正在下落的变量
				currentOne = nextOne;
				nextOne = Tetromino.randomOne();
			}
			/*
			 * 下落之后,要重新进行绘制,才会看到下落后的位置
			 * repaint方法,也是JPanel类中提供的
			 * 此方法中调用了paint方法
			 */
			repaint();
			elimination();
		}
	}
	
	
	/*
	 * 使用Right键控制四格方块右移
	 */
	public void moveRightAction() {
		currentOne.moveRight();
		if(outOfBounds()||coincide())
			currentOne.moveLeft();
	}

	/*
	 * 使用Left键控制四格方块左移
	 */
	public void moveLeftAction() {
		currentOne.moveLeft();
		if(outOfBounds()||coincide()) {
			currentOne.moveRight();
		}
	}
	private 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 boolean outOfBounds() {
		Cell[] cells = currentOne.cells;
		for(Cell c:cells) {
			int col = c.getCol();
			if(col<0||col>9)
				return true;
		}
		return false;
	}
	
	/*
	 * 使用Down键控制四格方块的下落
	 */
	public void softDropAction() {
		if(canDrop()) {
			currentOne.softDrop();
		}
		else {
			landToWall();
			currentOne = nextOne;
			nextOne = Tetromino.randomOne();
		}
	}
	public boolean canDrop() {
		Cell[] cells = currentOne.cells;
		/* 
		 * 
		 */
		for(Cell c:cells) {
			/* 获取每个元素的行号,列号
			 * 判断:
			 * 只要有一个元素的下一行上有方块
			 * 或者只要有一个元素到达最后一行
			 * 就不能再下落了
			 */
			int row = c.getRow();
			int col = c.getCol();
			if (row==19) {//判断是否到达底部
				return false;
			}
			else if(wall[row+1][col]!=null) {//判断下方是否有方块
				return false;
			}
			
		}
		return true;
	}
	/*
	 * 当不能再下落时,需要将四格方块嵌入到墙中
	 * 也就是存储到二维数组中相应的位置上
	 */
	public void landToWall() {
		Cell[] cells = currentOne.cells;
		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 panel = new Tetris();
		//将面板嵌入窗口
		frame.add(panel);
		
		//2:设置为可见
		frame.setVisible(true);
		//3:设置窗口的尺寸
		frame.setSize(535, 595);
		//4:设置窗口居中
		frame.setLocationRelativeTo(null);
		//5:设置窗口关闭,即程序中止
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
				
		//游戏的主要逻辑封装在start方法中
		panel.start();
	}
}
  • 88
    点赞
  • 338
    收藏
    觉得还不错? 一键收藏
  • 124
    评论
评论 124
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值