package main;
import utils.SourceUtil;
import javax.swing.*;
/**
* @author woyin
* @since 2023/7/10 17:17
*/
public class Test {
public static void main(String[] args) {
//创建俄罗斯方块界面
JFrame jFrame = new JFrame("俄罗斯方块");
//规划俄罗斯方块界面设计
Bockpanel bockpanel = new Bockpanel();
//添加设计好的界面
jFrame.add(bockpanel);
//为界面添加Logo
jFrame.setIconImage(SourceUtil.tetrisLogoImage);
//设置界面属性,大小
jFrame.setSize(535, 595);//页面大小
jFrame.setDefaultCloseOperation(3);//关闭窗口
jFrame.setLocationRelativeTo(null);//居中
jFrame.setResizable(false);//固定页面,不可伸缩
jFrame.setVisible(true);//可视化
//游戏开始
bockpanel.start();
}
}
package main;
import domain.Blockmodel;
import domain.Cell;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.function.Consumer;
/**
* @author woyin
* @since 2023/7/11 9:40
*/
//自定义监听,实现自由下落
public class KeyActionListener extends KeyAdapter implements ActionListener {
//创建Bockpanel属性
public Bockpanel bockpanel;
//关卡级别
protected int level = 1;
//所得分数
protected int score = 0;
//下落时间
protected int delay = 1000;
protected Timer timer = new Timer(delay, this);
//游戏暂定标识符
private boolean flag = true;
//游戏暂停时间计算
protected long preGameTime = 0;
//记录游戏开始时间
protected Date startTime = new Date();
//计数器,计数消的行数
int lines = 0;
//设置Bockpanel属性值
public KeyActionListener(Bockpanel bockpanel) {
this.bockpanel = bockpanel;
}
//对键盘进行监听
@Override
public void keyPressed(KeyEvent e) {
int t = e.getKeyCode();
switch (t) {
//左移
case KeyEvent.VK_LEFT:
if (flag) {
moveLeftAction();
//刷新屏幕
bockpanel.repaint();
}
break;
//右移
case KeyEvent.VK_RIGHT:
if (flag) {
moveRinghtAction();
//刷新屏幕
bockpanel.repaint();
}
break;
//下移
case KeyEvent.VK_DOWN:
if (flag) {
moveDownAction();
//刷新屏幕
bockpanel.repaint();
}
break;
case KeyEvent.VK_UP:
if (flag) {
//判断方块是否在第一行
if (canStartRote()) {
//左旋
bockpanel.currentOne.moveLeftRote();
//判断是否能够左旋
if (canRote()) {
//右旋
bockpanel.currentOne.moveRightRote();
}
//刷新屏幕
bockpanel.repaint();
}
}
break;
//暂停游戏
case KeyEvent.VK_ENTER:
if (flag) {
//游戏停止
timer.stop();
//获取停止时间
Date stopTime = new Date();
//计算游戏开始到暂停的时间
preGameTime += stopTime.getTime() - startTime.getTime();
//改变状态
flag = false;
} else {
//游戏开始
timer.start();
//记录游戏开始时间
startTime = new Date();
//改变状态
flag = true;
}
break;
}
}
//判断是否能够左旋
private boolean canRote() {
for (Cell cell : bockpanel.currentOne.cell) {
//判断左旋后是否越界
if (cell.getRow() < 0 || cell.getRow() > 19 || cell.getCol() < 0 || cell.getCol() > 9) {
return true;
}
//判断左旋后所在单元格是否有俄罗斯方块
if (bockpanel.wall[cell.getRow()][cell.getCol()] != null) {
return true;
}
}
return false;
}
//判断方块是否在第一行,第一行不能旋转
private boolean canStartRote() {
for (Cell cell : bockpanel.currentOne.cell) {
//判断方格
if (cell.getRow() <= 0) {
return false;
}
}
return true;
}
//下移
private void moveDownAction() {
//判断是否能够下移
if (ifcandown()) {
bockpanel.currentOne.movedown();
}
}
//右移
private void moveRinghtAction() {
//判断能否右移
if (canRight()) {
bockpanel.currentOne.moveright();
}
}
//能否右移判断
private boolean canRight() {
for (Cell cell : bockpanel.currentOne.cell) {
//判断获取当前俄罗斯方块行数是否已经到边界
if (cell.getCol() == 9) {
return false;
//判断获取当前俄罗斯方块行数右方是否有俄罗斯方块
} else if (bockpanel.wall[cell.getRow()][cell.getCol() + 1] != null) {
return false;
}
}
return true;
}
//能否左移
private void moveLeftAction() {
//判断能否左移
if (canLefy()) {
bockpanel.currentOne.moveleft();
}
}
//能否左移判断
private boolean canLefy() {
for (Cell cell : bockpanel.currentOne.cell) {
//判断获取当前俄罗斯方块行数是否已经到边界
if (cell.getCol() == 0) {
return false;
//判断获取当前俄罗斯方块行数左方是否有俄罗斯方块
} else if (bockpanel.wall[cell.getRow()][cell.getCol() - 1] != null) {
return false;
}
}
return true;
}
//俄罗斯方块下落逻辑
@Override
public void actionPerformed(ActionEvent e) {
//判断是否能够下落
if (ifcandown()) {
//俄罗斯方块下落一格
bockpanel.currentOne.movedown();
} else {
//判断游戏是否结束
if (ifGameOver()) {
//游戏结束
timer.stop();
//设置再来一次窗口
int op = JOptionPane.showConfirmDialog(null, "Game Over! 再来一局");
//判断是否再次游戏
if (op == JOptionPane.YES_OPTION) {
//清空棋盘
bockpanel.wall = new Cell[20][10];
//重新开始
timer.restart();
//清空时间
preGameTime = 0;
startTime = new Date();
} else if (op == JOptionPane.NO_OPTION) {
//退出系统
System.exit(0);
}
} else {
//将棋子位子录入棋盘
lanToWall();
//消除棋子堆满的一行
destroyLine();
//将当前俄罗斯方块替换成下一个俄罗斯方块
bockpanel.currentOne = bockpanel.NextOne;
//重新获取下一个俄罗斯方块
bockpanel.NextOne = Blockmodel.randomOne();
}
}
//刷新页面
bockpanel.repaint();
}
//将棋子位子录入棋盘
private void lanToWall() {
//遍历棋子
for (Cell cell : bockpanel.currentOne.cell) {
//棋子信息录入棋盘
bockpanel.wall[cell.getRow()][cell.getCol()] = cell;
}
}
//消除棋子堆满的一行
private void destroyLine() {
//创建数组,用来储存要消除的行
ArrayList<Integer> rowList = new ArrayList<>();
//遍历当前俄罗斯方块
for (Cell cell : bockpanel.currentOne.cell) {
//判断一行是否堆满
if (isFullLine(cell.getRow())) {
rowList.add(cell.getRow());
}
}
//对集合进行排序
Collections.sort(rowList);
rowList.forEach(integer -> {
//消除的行数加一
lines++;
//将消除的一行信息清空
bockpanel.wall[integer] = new Cell[10];
//将消除行的上面行移下来
int i = integer;
for (; i > 0; i--) {
//将上一行的信息移到下一行
System.arraycopy(bockpanel.wall[i - 1], 0, bockpanel.wall[i], 0, 10);
}
//补充第一行的方格
bockpanel.wall[0] = new Cell[10];
score = 10 * lines;
if (score == 10 * level) {
level++;
delay -= 10;
if (delay >= 0) {
timer.setDelay(delay);
}
}
});
}
//判断一行是否堆满
private boolean isFullLine(int row) {
//获取当前俄罗斯方块行数的所有列数
Cell[] wall = bockpanel.wall[row];
for (Cell cell : wall) {
//判断这一行是否堆满
if (cell == null) {
return false;
}
}
return true;
}
//判断游戏是否结束
private boolean ifGameOver() {
for (Cell cell : bockpanel.currentOne.cell) {
//获取当前俄罗斯方块行数是否出界
if (cell.getRow() <= 0) {
return true;
}
}
return false;
}
//判断游戏是否结束
private boolean ifcandown() {
for (Cell cell : bockpanel.currentOne.cell) {
//判断获取当前俄罗斯方块行数是否已经到底
if (cell.getRow() == 19) {
return false;
//判断获取当前俄罗斯方块行数下方是否有俄罗斯方块
} else if (bockpanel.wall[cell.getRow() + 1][cell.getCol()] != null) {
return false;
}
}
return true;
}
}
package main;
import domain.Blockmodel;
import domain.Cell;
import utils.SourceUtil;
import javax.swing.*;
import java.awt.*;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @author woyin
* @since 2023/7/10 17:22
*/
//创建单独画布类,满足业务的需求
public class Bockpanel extends JPanel {
//设计20*10的网状格子
public Cell[][] wall = new Cell[20][10];
//每个网状格子的长宽为26
private static final int CELL_SIZE = 26;
//创建当前方块
public Blockmodel currentOne = Blockmodel.randomOne();
//创建下一个方块
public Blockmodel NextOne = Blockmodel.randomOne();
//实例化KeyActionListener
KeyActionListener keyActionListener = new KeyActionListener(this);
//规划游戏界面
@Override
public void paint(Graphics g) {
//为界面添加背景图片
g.drawImage(SourceUtil.backgroundImage, 0, 0, null);
//画好游戏进行的网状格子
paintWall(g);
//补充右侧信息
paintGameInfo(g);
//展示当前要移动的方块
paintCurrentOne(g);
//展示下一个要移动的方块
paintNextOne(g);
}
private void paintGameInfo(Graphics g) {
//设置字体属性
Font font = new Font("楷体", Font.CENTER_BASELINE, 22);
g.setFont(font);
g.setColor(Color.magenta);
//显示设置的字体
g.drawString("关 卡:" + keyActionListener.level, 300, 175);
g.drawString("分 数:" + keyActionListener.score, 300, 235);
//创建游戏结束时间
Date now = new Date();
//整个游戏时间
long l = now.getTime() - keyActionListener.startTime.getTime() + keyActionListener.preGameTime;
//格式化时间
Date date = new Date(l);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("mm分钟ss秒");
String gameTime = simpleDateFormat.format(date);
//显示游戏时间
g.drawString("时 间:" + gameTime, 300, 290);
}
//画出游戏进行格子
private void paintWall(Graphics g) {
for (int i = 0; i < 20; i++) {
for (int j = 0; j < 10; j++) {
Cell cell = wall[i][j];
//判断当前单元格是否为俄罗斯方块
if (cell == null) {
g.drawRect(j * CELL_SIZE + 15, i * CELL_SIZE + 15, CELL_SIZE, CELL_SIZE);
} else {
g.drawImage(cell.getImage(), j * CELL_SIZE + 15, i * CELL_SIZE + 15, null);
}
}
}
}
//画出当前要移动的方块的位置
private void paintNextOne(Graphics g) {
Cell[] cells = NextOne.cell;
for (Cell cell : cells) {
g.drawImage(cell.getImage(), cell.getCol() * 26 + 280, cell.getRow() * 26 + 40, null);
}
}
//画出下一个要移动的方块的位置
private void paintCurrentOne(Graphics g) {
Cell[] cells = currentOne.cell;
for (Cell cell : cells) {
g.drawImage(cell.getImage(), cell.getCol() * 26 + 15, cell.getRow() * 26 + 15, null);
}
}
public void start() {
//为Bockpanel添加键盘监听
this.addKeyListener(keyActionListener);
//为KeyActionListener类设置bockpanel值
//打开界面集中鼠标
this.requestFocus();
//下落时间
Timer timer = keyActionListener.timer;
//启动游戏
timer.start();
}
}
这一段代码放在main包下,一共有三个类
package utils;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
/**
* @author woyin
* @since 2023/7/10 14:57
*/
public class SourceUtil {
public static BufferedImage iImage;
public static BufferedImage jImage;
public static BufferedImage lImage;
public static BufferedImage oImage;
public static BufferedImage sImage;
public static BufferedImage tImage;
public static BufferedImage zImage;
public static BufferedImage backgroundImage;
public static BufferedImage tetrisLogoImage;
/**
* 静态代码块,加载一次静态资源
*/
static {
/**
* ImageIO.read()方法可以读取项目下的静态资源文件
*/
try {
iImage = ImageIO.read(SourceUtil.class.getResource("../image/I.png"));
jImage = ImageIO.read(SourceUtil.class.getResource("../image/J.png"));
lImage = ImageIO.read(SourceUtil.class.getResource("../image/L.png"));
oImage = ImageIO.read(SourceUtil.class.getResource("../image/O.png"));
sImage = ImageIO.read(SourceUtil.class.getResource("../image/S.png"));
tImage = ImageIO.read(SourceUtil.class.getResource("../image/T.png"));
zImage = ImageIO.read(SourceUtil.class.getResource("../image/Z.png"));
backgroundImage = ImageIO.read(SourceUtil.class.getResource("../image/background.png"));
tetrisLogoImage = ImageIO.read(SourceUtil.class.getResource("../image/tetrisLogo.png"));
} catch (IOException e) {
e.printStackTrace();
}
}
}
这一段代码放在utils包下,只有一个类
package domain;
import java.util.Random;
/**
* @author woyin
* @since 2023/7/10 14:41
*/
//方块的抽象父类
public class Blockmodel {
//创建方块数组,用来存储方块的七种形状的单元格
public Cell[] cell = new Cell[4];
//变形数组
public State[] states;
//左移
public void moveleft() {
for (Cell cel : cell) {
cel.left();
}
}
//右移
public void moveright() {
for (Cell cel : cell) {
cel.right();
}
}
//下移
public void movedown() {
for (Cell cel : cell) {
cel.down();
}
}
public int count;
//左旋转
public void moveLeftRote() {
if (this instanceof O) {
return;
}
if (count == 0) {
count = 4;
}
count--;
State state = states[count % states.length];
cell[1].setRow(cell[0].getRow() + state.row1);
cell[1].setCol(cell[0].getCol() + state.col1);
cell[2].setRow(cell[0].getRow() + state.row2);
cell[2].setCol(cell[0].getCol() + state.col2);
cell[3].setRow(cell[0].getRow() + state.row3);
cell[3].setCol(cell[0].getCol() + state.col3);
}
//右旋转
public void moveRightRote() {
if (this instanceof O) {
return;
}
if (count == 4) {
count = 0;
}
count++;
State state = states[count % states.length];
cell[1].setRow(cell[0].getRow() + state.row1);
cell[1].setCol(cell[0].getCol() + state.col1);
cell[2].setRow(cell[0].getRow() + state.row2);
cell[2].setCol(cell[0].getCol() + state.col2);
cell[3].setRow(cell[0].getRow() + state.row3);
cell[3].setCol(cell[0].getCol() + state.col3);
}
//随机生成七种方块之一
public static Blockmodel randomOne() {
Blockmodel b = new Blockmodel();
Random random = new Random();
//每一个字对应一种方块形状
int t = random.nextInt(6);
switch (t) {
case 0:
b = new I();
break;
case 1:
b = new J();
break;
case 2:
b = new Z();
break;
case 3:
b = new O();
break;
case 4:
b = new S();
break;
case 5:
b = new T();
break;
}
return b;
}
}
package domain;
import java.awt.image.BufferedImage;
/**
* @author woyin
* @since 2023/7/10 14:33
*/
//单元格类,组成俄罗斯方块的基本单位
public class Cell {
//行
private int row;
//列
private int col;
//单元格所存存储的颜色
private BufferedImage image;
public Cell() {
}
public Cell(int col, int row, BufferedImage image) {
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 down() {
row++;
}
}
package domain;
import utils.SourceUtil;
/**
* @author woyin
* @since 2023/7/10 15:07
*/
public class I extends Blockmodel {
//对I方块进行初始化
public I() {
cell[0] = new Cell(4, 0, SourceUtil.iImage);
cell[1] = new Cell(3, 0, SourceUtil.iImage);
cell[2] = new Cell(5, 0, SourceUtil.iImage);
cell[3] = new Cell(6, 0, SourceUtil.iImage);
//储存方块其它形状
super.states = new State[2];
states[0] = new State(0, -1, 0, 1, 0, 2);
states[1] = new State(1, 0, -1, 0, -2, 0);
}
}
package domain;
import utils.SourceUtil;
/**
* @author woyin
* @since 2023/7/10 15:19
*/
public class J extends Blockmodel {
//对J方块进行初始化
public J() {
cell[0] = new Cell(4, 0, SourceUtil.jImage);
cell[1] = new Cell(3, 0, SourceUtil.jImage);
cell[2] = new Cell(5, 0, SourceUtil.jImage);
cell[3] = new Cell(3, 1, SourceUtil.jImage);
//储存方块其它形状
super.states = new State[4];
states[0] = new State(0, -1, 0, 1, 1, 1);
states[1] = new State(-1, 0, 1, 0, 1, -1);
states[2] = new State(0, 1, 0, -1, -1, -1);
states[3] = new State(1, 0, -1, 0, -1, 1);
}
}
package domain;
import utils.SourceUtil;
/**
* @author woyin
* @since 2023/7/10 15:28
*/
public class O extends Blockmodel {
//对O方块进行初始化
public O() {
cell[0] = new Cell(4, 0, SourceUtil.oImage);
cell[1] = new Cell(5, 0, SourceUtil.oImage);
cell[2] = new Cell(4, 1, SourceUtil.oImage);
cell[3] = new Cell(5, 1, SourceUtil.oImage);
}
}
package domain;
import utils.SourceUtil;
/**
* @author woyin
* @since 2023/7/10 15:25
*/
public class S extends Blockmodel {
//对S方块进行初始化
public S() {
cell[0] = new Cell(4, 1, SourceUtil.sImage);
cell[1] = new Cell(3, 0, SourceUtil.sImage);
cell[2] = new Cell(4, 0, SourceUtil.sImage);
cell[3] = new Cell(5, 1, SourceUtil.sImage);
//储存方块其它形状
super.states = new State[4];
states[0] = new State(0, -1, 0, 1, 1, 1);
states[1] = new State(-1, 0, 1, 0, 1, -1);
states[2] = new State(0, 1, 0, -1, -1, -1);
states[3] = new State(1, 0, -1, 0, -1, 1);
}
}
package domain;
/**
* @author woyin
* @since 2023/7/13 10:57
*/
public class State {
int row1, col1;
int row2, col2;
int row3, col3;
public State() {
}
public State(int row1, int col1, int row2, int col2, int row3, int col3) {
this.row1 = row1;
this.col1 = col1;
this.row2 = row2;
this.col2 = col2;
this.row3 = row3;
this.col3 = col3;
}
}
package domain;
import utils.SourceUtil;
/**
* @author woyin
* @since 2023/7/10 15:17
*/
public class T extends Blockmodel {
//对T方块进行初始化
public T() {
cell[0] = new Cell(4, 0, SourceUtil.tImage);
cell[1] = new Cell(3, 0, SourceUtil.tImage);
cell[2] = new Cell(5, 0, SourceUtil.tImage);
cell[3] = new Cell(4, 1, SourceUtil.tImage);
//储存方块其它形状
super.states = new State[4];
states[0] = new State(0, -1, 0, 1, 1, 1);
states[1] = new State(-1, 0, 1, 0, 1, -1);
states[2] = new State(0, 1, 0, -1, -1, -1);
states[3] = new State(1, 0, -1, 0, -1, 1);
}
}
package domain;
import utils.SourceUtil;
/**
* @author woyin
* @since 2023/7/10 15:22
*/
public class Z extends Blockmodel {
//对Z方块进行初始化
public Z() {
cell[0] = new Cell(4, 0, SourceUtil.zImage);
cell[1] = new Cell(5, 0, SourceUtil.zImage);
cell[2] = new Cell(3, 1, SourceUtil.zImage);
cell[3] = new Cell(4, 1, SourceUtil.zImage);
//储存方块其它形状
super.states = new State[4];
states[0] = new State(0, -1, 0, 1, 1, 1);
states[1] = new State(-1, 0, 1, 0, 1, -1);
states[2] = new State(0, 1, 0, -1, -1, -1);
states[3] = new State(1, 0, -1, 0, -1, 1);
}
}
这一段代码放在domain,y一共有9各类