Java语言实现经典游戏俄罗斯方块

使用Java语言编写的经典游戏俄罗斯方块,能够调整游戏难度、加快下落速度、左右旋转方块等全部功能。(代码已集成在一个class内,如下所示,点击即可运行。内容水平有限,欢迎指正批评。)

演示视频 https://www.bilibili.com/video/BV1Yi4y1R7Xn/

import java.awt.Color;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;

public class Tetris extends JFrame{
	
	/**
	 * D、F变形,方向键移动,空格暂停,Esc退出游戏。
	 */
	private static final long serialVersionUID = 1L;
	private JButton[][] grid = new JButton[20][10];//背景
	
	private int[][][][] relativePosition={{{{0,0},{0,1},{0,2},{0,3}},{{0,0},{1,0},{2,0},{3,0}}},
	        {{{0,0},{1,0},{1,1},{1,2}},{{0,1},{1,1},{2,0},{2,1}},{{0,0},{0,1},{0,2},{1,2}},{{0,0},{0,1},{1,0},{2,0}}},
	        {{{0,1},{1,0},{1,1},{1,2}},{{0,1},{1,0},{1,1},{2,1}},{{0,0},{0,1},{0,2},{1,1}},{{0,0},{1,0},{1,1},{2,0}}},
	        {{{0,2},{1,0},{1,1},{1,2}},{{0,0},{0,1},{1,1},{2,1}},{{0,0},{0,1},{0,2},{1,0}},{{0,0},{1,0},{2,0},{2,1}}},
	        {{{0,0},{0,1},{1,1},{1,2}},{{0,1},{1,0},{1,1},{2,0}}},
	        {{{0,0},{0,1},{1,0},{1,1}}},
	        {{{0,1},{0,2},{1,0},{1,1}},{{0,0},{1,0},{1,1},{2,1}}}};//七种不同方块组合,每种方块组合的旋转角度,每种旋转角度的四个不同方块,每个方块的行列坐标
	
	private boolean[][] gridState = new boolean[20][10];//网格是否被填充,true表示被填充,false表示没有被填充
	private boolean falling = false;//当前是否有方块下降
	private boolean pause = false;//当前游戏是否暂停
	private boolean token = false;//当前令牌是否被占用
	private boolean fallToTheGround;//当前是否落到地上或者其他方格上
	private boolean transformative;//当前能否变形
	private boolean removable;//当前能否左右移动
	private boolean gameOver = false;//当前游戏是否结束(结束条件两个,一个是达到了10000分,一个是触到了天花板)
	
	private int[][] location = new int[4][2];//下降的四个方块各自坐标
	private int time = 1000;//方块停留时间,与游戏难度相关
	private int score = 0;//游戏得分
	private int realTimePlaceL, realTimePlaceR;//方块整体的基准点
	private int shapeKind, angle, angle_ = -1, angleNum;//形状的种类,旋转角度,将要旋转到的角度,此种类能够旋转的角度数
	
	private Color lightColor = new Color(210,210,210);//浅色方块
	private Color darkColor = new Color(20,20,20);//深色背景
	
	private String difficulty;//游戏难度
			
	public Tetris() {
		chooseDifficulty();//选择难度
		interfaceMethod();//渲染UI
		systemTimer();//系统控制方块定时下降
		play();//玩家用键盘操作方块
	}
	
	void chooseDifficulty() {//选择难度
		Object[] possibleValues = {"青铜", "白银", "黄金", "白金", "钻石"};
		Object selectedValue = JOptionPane.showInputDialog(null, 
				"按键说明:\n\n                D、F变形,方向键移动,\n               空格暂停,Esc退出游戏。\n\n请选择难度等级:",
				"开始",JOptionPane.PLAIN_MESSAGE, null,possibleValues, possibleValues[1]);
		if(selectedValue == null) {
			System.exit(0);
		}else {
			switch(difficulty = selectedValue.toString()) {
				case "青铜":
					time = 1500;//每个位置方块组合停留1500ms
					break;
				case "白银":
					time = 1000;//每个位置方块组合停留1000ms
					break;
				case "黄金":
					time = 500;//每个位置方块组合停留500ms
					break;
				case "白金":
					time = 300;//每个位置方块组合停留300ms
					break;
				case "钻石":
					time = 150;//每个位置方块组合停留150ms
					break;
			}
		}
	}
	
	void interfaceMethod() {//渲染UI
		setTitle("Tetris    难度: " + difficulty + "  得分: " + String.valueOf(score));//设置窗口标题
		setResizable(false);//首先隐藏窗口,等全部组件渲染完一起显示
		setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);//设置窗口关闭按钮不起作用,便于下面重写关闭功能
		int width = Toolkit.getDefaultToolkit().getScreenSize().width;//获取屏幕宽度
     	int height = Toolkit.getDefaultToolkit().getScreenSize().height;//获取屏幕高度
    	int windowsWedth =360, windowsHeight = 700;//窗口大小
		setBounds((width - windowsWedth) / 2,(height - windowsHeight) / 2,windowsWedth,windowsHeight);//设置屏幕居中
		setLayout(new GridLayout(0,10));//网格布局,10列
		addWindowListener(new WindowAdapter() {//重写窗口关闭
			public void windowClosing(WindowEvent e) {
				pause = true;//暂停游戏
				Object[] options = {"继续游戏", "重新开始", "退出游戏"}; 
				Object selectedValue = JOptionPane.showOptionDialog(null, "是否退出游戏?", "游戏已暂停", 
		    			JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]);
				switch(selectedValue.toString()) {
					case "-1"://关闭弹窗
					case "0"://继续游戏
						pause = false;
						break;
					case "1"://重新开始
						dispose();//释放当前窗口
			    		new Tetris();//重新加载
						break;
					default:
						System.exit(0);//退出程序
						break;
				}
			}
		});
		for(int i=0; i<20; i++) {
			for(int j=0; j<10; j++) {
				grid[i][j] = new JButton("");//创建按钮
				getContentPane().add(grid[i][j]);//添加进网格布局
				grid[i][j].setBackground(darkColor);//设置为深色背景
				grid[i][j].setEnabled(false);//去除按钮可操作性,单纯作为背景(用按钮比图片或者面板更方便一些,时间差别不大)
			}
		}
		setVisible(true);//显示窗口
	}
	
	void systemTimer() {//系统控制方块定时下降
		Timer timer = new Timer(); //创建一个定时器
		timer.schedule(new TimerTask() {//为该定时器添加任务
			public void run() {
				if(!pause) {//游戏未暂停
					while(true) {
						if(!token) {//如果令牌忙碌,则等待令牌;如果令牌空闲,占据令牌。
							token = true;
							if(falling) {
								fall();//方块处于下落状态则调用下落方法,不处于则调用生成新方块方法
							}else {
								generate();
							}
							token = false;//释放令牌
							break;
						}
					}
				}
				if(gameOver) {//游戏结束
					timer.cancel();
					Object[] options = {"重新开始", "退出游戏"}; 
			    	int re = JOptionPane.showOptionDialog(null, "是否重新开始", "游戏结束", 
			    			JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]); 
			    	if(re == JOptionPane.YES_OPTION){ 
			    		dispose();
			    		new Tetris();
			    	}else{
			    		System.exit(0);
			    	}
				}
			}
		}, 0, time);
	}
	
	void play() {//玩家用键盘操作方块
		addKeyListener(new KeyAdapter(){//添加键盘事件
			public void keyPressed(KeyEvent e){
				if(e.getKeyCode() == KeyEvent.VK_ESCAPE) {//直接退出游戏
					System.exit(0);
				}else {
					if(!gameOver) {//首先判断游戏是否结束
						if(e.getKeyCode() == KeyEvent.VK_SPACE){//空格将游戏设置为暂停
							if(pause) {
								pause = false;
							}else {
								pause = true;
							}
						}else {
							if(!pause) {//暂停时不能使用方向键,只能使用Esc键和空格键
								while(falling && true) {//只有在方块下落过程中才可以申请令牌,响应键盘操作(方块落地一段时间还属于下落状态还能移动,
									if(!token) {        //但是如果方块组合的一部分和下面的一行消除掉,则不属于下落状态不能移动了)
										token = true;//占据令牌
										switch(e.getKeyCode()) {
											case KeyEvent.VK_LEFT:
												if(location[0][1]-1>-1 && location[1][1]-1>-1 && location[2][1]-1>-1 && location[3][1]-1>-1) {//判断左移后是否超过地图
													removable = true;//判断左移后是否和其他方块组合重叠
													for(int i=0; i<4; i++) {//考察方块组合的每个方块
														if(gridState[location[i][0]][location[i][1]-1]) {
															removable = false;
															break;
														}
													}
													if(removable) {//未重叠则允许左移
														realTimePlaceR--;//方块整体的基准点左移
														for(int i=0; i<4; i++) {
															grid[location[i][0]][location[i][1]].setBackground(darkColor);//原来方块位置恢复成背景
															location[i][1]--;//方块整体左移
														}
														for(int i=0; i<4; i++) {
															grid[location[i][0]][location[i][1]].setBackground(lightColor);//渲染方块
														}
													}
												}
												break;
											case KeyEvent.VK_RIGHT:
												if(location[0][1]+1<10 && location[1][1]+1<10 && location[2][1]+1<10 && location[3][1]+1<10) {//判断右移后是否超过地图
													removable = true;//判断右移后是否和其他方块组合重叠
													for(int i=0; i<4; i++) {//考察方块组合的每个方块
														if(gridState[location[i][0]][location[i][1]+1]) {
															removable = false;
															break;
														}
													}
													if(removable) {//未重叠则允许右移
														realTimePlaceR++;//方块整体的基准点右移
														for(int i=0; i<4; i++) {
															grid[location[i][0]][location[i][1]].setBackground(darkColor);//原来方块位置恢复成背景
															location[i][1]++;//方块整体右移
														}
														for(int i=0; i<4; i++) {
															grid[location[i][0]][location[i][1]].setBackground(lightColor);//渲染方块
														}
													}
												}
												break;
											case KeyEvent.VK_DOWN:
												fall();//下落方法
												break;
											case KeyEvent.VK_D:
												angle_ = (angle + 1) % angleNum;//将要逆时针旋转到的位置
											case KeyEvent.VK_F:
												if(angle_ == -1) {//如果不为-1说明点击的是D,为-1说明点击的是F(顺时针)
													angle_ = (angle + angleNum - 1) % angleNum;
												}
												if(angleNum != 1) {//旋转角度只有一个的话就不转了(正方形方块组合)
													transformative = true;//预先假设能变形
													for(int i=0; i<4; i++) {//考察方块组合的每个方块
														int L = relativePosition[shapeKind][angle_][i][0] + realTimePlaceL;//变形后的行坐标
														int R = relativePosition[shapeKind][angle_][i][1] + realTimePlaceR;//变形后的列坐标
														if(L>19 || R>9 || gridState[L][R]) {//超过地图或者该位置有其他方块则不能变形
															transformative = false;
															break;
														}
													}
													if(transformative) {//符合变形要求
														angle = angle_;//更新角度
														for(int i=0; i<4; i++) {
															grid[location[i][0]][location[i][1]].setBackground(darkColor);//原来方块位置恢复成背景
															location[i][0]++;
														}
														for(int i=0; i<4; i++) {
															location[i][0] = relativePosition[shapeKind][angle][i][0] + realTimePlaceL;//更新行位置
															location[i][1] = relativePosition[shapeKind][angle][i][1] + realTimePlaceR;//更新列位置
															grid[location[i][0]][location[i][1]].setBackground(lightColor);//渲染方块
														}
													}
												}
												angle_ = -1;//重新设置为-1
												break;
										}
										token = false;//释放令牌
										break;
									}
								}
							}
						}
					}
				}
			}
		});
	}
	
	void fall() {
		fallToTheGround = false;//是否落到地上或者其他方格上
		for(int i=0; i<4; i++) {
			if(location[i][0] == 19 || gridState[location[i][0]+1][location[i][1]]) {
				fallToTheGround = true;
				break;
			}
		}
		if(fallToTheGround) {
			falling = false;
			for(int i=0; i<4; i++) {
				gridState[location[i][0]][location[i][1]] = true;
			}
			removeLine(19);
			for(int j=0; j<9; j++) {
				if(gridState[0][j]) {
					gameOver = true;
					break;
				}
			}
		}else {
			realTimePlaceL++;
			for(int i=0; i<4; i++) {
				grid[location[i][0]][location[i][1]].setBackground(darkColor);
				location[i][0]++;
			}
			for(int i=0; i<4; i++) {
				grid[location[i][0]][location[i][1]].setBackground(lightColor);
			}
		}
	}
	
	void generate() {
		realTimePlaceL = 0;
		realTimePlaceR = 4;
		falling = true;
		shapeKind = (int) (Math.random()*7);
		switch(shapeKind) {
			case 5:
				angleNum = 1;
				angle = 0;
				break;
			case 0:
			case 4:
			case 6:
				angleNum = 2;
				angle = (int) (Math.random()*2);
				break;
			default:
				angleNum = 4;
				angle = (int) (Math.random()*4);
				break;
		}
		for(int i=0; i<4; i++) {
			location[i][0] = relativePosition[shapeKind][angle][i][0] + realTimePlaceL;
			location[i][1] = relativePosition[shapeKind][angle][i][1] + realTimePlaceR;
			grid[location[i][0]][location[i][1]].setBackground(lightColor);
		}
	}
	
	void removeLine(int line) {
		boolean removed = true, allBlank;
		int line_=line;
		for(int i=line; i>=0; i--) {
			removed = true;
			allBlank = true;
			for(int j=0; j<10; j++) {
				if(gridState[i][j]) {
					allBlank = false;
				}else {
					removed = false;
				}
				if(!removed && !allBlank) {
					break;
				}
			}
			if(removed || allBlank) {
				line_ = i;
				break;
			}
		}
		if(removed) {
			for(int i=line_; i>0; i--) {
				for(int j=0; j<10; j++) {
					if(gridState[i][j] ^ gridState[i-1][j]) {
						gridState[i][j] = gridState[i-1][j];
						if(gridState[i][j]) {
							grid[i][j].setBackground(lightColor);
						}else {
							grid[i][j].setBackground(darkColor);
						}
					}
				}
			}
			score += 10;
			if(score == 10000) {
				gameOver = true;
			}else {
				setTitle("Tetris    难度: " + difficulty + "  得分: " + String.valueOf(score));//设置窗口标题
				removeLine(line_);
			}
		}
	}

	public static void main(String[] args) {
		new Tetris();
	}

}
  • 9
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

点点 星光

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值