Java小游戏【俄罗斯方块】

代码实现

首先强调,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;
		}
	
}

七种图形类:I、J、L、O、S、T、Z

public 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[2];
        //初始化两种状态的相对坐标
        states[0]=new State(0,0,0,-1,0,1,0,2);
        states[1]=new State(0,0,-1,0,1,0,2,0);
    }
 
}
public 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[4];
        states[0]=new State(0,0,0,-1,0,1,1,1);
        states[1]=new State(0,0,-1,0,1,0,1,-1);
        states[2]=new State(0,0,0,1,0,-1,-1,-1);
        states[3]=new State(0,0,1,0,-1,0,-1,1);
    }
}
public 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[4];
        states[0]=new State(0,0,0,-1,0,1,1,-1);
        states[1]=new State(0,0,-1,0,1,0,-1,-1);
        states[2]=new State(0,0,0,1,0,-1,-1,1);
        states[3]=new State(0,0,1,0,-1,0,1,1);
    }
}
public 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[0];
    }
}
public 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[2];
        //初始化两种状态的相对坐标
        states[0]=new State(0,0,0,1,1,-1,1,0);
        states[1]=new State(0,0,1,0,-1,-1,0,-1);
    }
}
public 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);
    }
}
public 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[2];
        //初始化两种状态的相对坐标
        states[0]=new State(0,0,-1,-1,-1,0,0,1);
        states[1]=new State(0,0,-1,1,0,1,1,0);
    }
}

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();
	}
}

效果

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值