(1)Main
(2.1)Ter
(2.2)I
(2.3)J
(2.4)L
(2.5)O
(2.6)S
(2.7)T
(2.8)Z
(3)MyPanel
(4.1)ConstantPool
(4.2)Cell
(4.3)State
package xyz.mstar;
import xyz.mstar.teris.ui.MyPanel;
import javax.swing.*;
/**
* 主类
* @author IceFery
*/
public class Main {
public static void main(String[] args) {
// 1.创建一个窗口对象
JFrame frame = new JFrame("教主的俄罗斯方块");
MyPanel panel = new MyPanel(); // 用方块类型创建面板, 即界面
frame.add(panel); // 将面板嵌入窗口
// 2.设置为可见
frame.setVisible(true);
// 3.设置窗口的大小
frame.setSize(580, 580);
// 4.设置窗口居中
frame.setLocationRelativeTo(null);
// 5.设置窗口关闭方式
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 6.运行游戏
panel.game();
}
}
package xyz.mstar.teris.ter;
import xyz.mstar.teris.util.Cell;
import xyz.mstar.teris.util.State;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.util.Arrays;
/**
* 7种不同方块的父类,由小方块Cell组成,由7种形状的方块实现
* @author IceFery
*/
public abstract class Ter {
// 申明一大堆图片的引用
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;
// 让这些引用指向图片
static {
try {
T = ImageIO.read(Ter.class.getResource("T.jpg"));
O = ImageIO.read(Ter.class.getResource("O.jpg"));
I = ImageIO.read(Ter.class.getResource("I.jpg"));
J = ImageIO.read(Ter.class.getResource("J.jpg"));
L = ImageIO.read(Ter.class.getResource("L.jpg"));
S = ImageIO.read(Ter.class.getResource("S.jpg"));
Z = ImageIO.read(Ter.class.getResource("Z.jpg"));
} catch (Exception e) {
e.printStackTrace();
}
}
public Cell[] cells = new Cell[4]; // 所有的方块都是由最小的4个方块组成的
public State[] states; // 1~4种
public int count = 10000;
/**
* 随机生成一个小方块
* @return
*/
public static Ter randomOne() {
Ter t = null;
int num = (int) (Math.random() * 7); // 随机生成一个0~6的随机数来表示7种方块的序号
switch (num) { // 让t指向随机生成的一个方块
case 0:
t = new T();
break;
case 1:
t = new Z();
break;
case 2:
t = new O();
break;
case 3:
t = new I();
break;
case 4:
t = new J();
break;
case 5:
t = new L();
break;
case 6:
t = new S();
break;
}
return t;
}
/**
* 方块行为:向左移动
*/
public void moveLeft() {
// foreach语句 类型名 迭代变量:数组的引用名
for (Cell c : cells) {
c.left();// 每个小方块都向左移动
}
}
/**
* 方块行为:向右移动
*/
public void moveRight() {
for (Cell c : cells) {
c.right();
}
}
/**
* 方块行为:向下移动
*/
public void softDrop() {
for (Cell c : cells) {
c.drop();
}
}
/**
* 方块行为:逆时针转
*/
public void rotateRight() {
count++;
State s = states[count % states.length]; //对len求余, 会得到 0~len 之间的数
Cell c = cells[0];
int row = c.getRow(); //得到第一个小方块的行、列坐标
int col = c.getCol();
// 以此为基础,以s的属性作为偏移量, 重设每一个小方块的行、列坐标
for (int i = 1; i < 4; i++) {
cells[i].setRow(row + s.rows[i]);
cells[i].setCol(col + s.cols[i]);
}
}
/**
* 方块行为:顺时针转
*/
public void rotateLeft() {
count--;
State s = states[count % states.length];
Cell c = cells[0];
int row = c.getRow();
int col = c.getCol();
for (int i = 1; i < 4; i++) {
cells[i].setRow(row + s.rows[i]);
cells[i].setCol(col + s.cols[i]);
}
}
@Override
public String toString() {// 将cells按字符串形式返回
return "[" + Arrays.toString(cells) + "]";
}
}
package xyz.mstar.teris.ter;
import xyz.mstar.teris.util.Cell;
import xyz.mstar.teris.util.State;
/**
* 只有2种形态
* @author IceFery
*/
public class I extends Ter {
public I() {
// 左边空3格
cells[0] = new Cell(I, 0, 3);// 第(图片I,第0+1行, 第3+1个)个格子
cells[1] = new Cell(I, 0, 4);
cells[2] = new Cell(I, 0, 5);
cells[3] = new Cell(I, 0, 6);
//用数组存下每种形态的位置
states = new State[]{
new State(new int[][]{
{0, 0}, {0, -1}, {0, 1}, {0, 2}}),
new State(new int[][]{
{0, 0}, {-1, 0}, {1, 0}, {2, 0}})
};
}
}
package xyz.mstar.teris.ter;
import xyz.mstar.teris.util.Cell;
import xyz.mstar.teris.util.State;
/**
* 4种形态
* @author IceFery
*/
public class J extends Ter {
public J() {
cells[0] = new Cell(J, 0, 4);
cells[1] = new Cell(J, 0, 3);
cells[2] = new Cell(J, 0, 5);
cells[3] = new Cell(J, 1, 5);
states = new State[]{
new State(new int[][]{
{0, 0}, {0, 1}, {0, -1}, {-1, -1}}),
new State(new int[][]{
{0, 0}, {1, 0}, {-1, 0}, {-1, 1}}),
new State(new int[][]{
{0, 0}, {0, -1}, {0, 1}, {1, 1}}),
new State(new int[][]{
{0, 0}, {-1, 0}, {1, 0}, {1, -1}})
};
}
}
package xyz.mstar.teris.ter;
import xyz.mstar.teris.util.Cell;
import xyz.mstar.teris.util.State;
/**
* 4种形态
* @author IceFery
*/
public class L extends Ter {
public L() {
cells[0] = new Cell(L, 0, 4);
cells[1] = new Cell(L, 0, 3);
cells[2] = new Cell(L, 0, 5);
cells[3] = new Cell(L, 1, 3);
states = new State[]{
new State(new int[][]{
{0, 0}, {0, 1}, {0, -1}, {-1, 1}}),
new State(new int[][]{
{0, 0}, {1, 0}, {-1, 0}, {1, 1}}),
new State(new int[][]{
{0, 0}, {0, -1}, {0, 1}, {1, -1}}),
new State(new int[][]{
{0, 0}, {-1, 0}, {1, 0}, {-1, -1}})
};
}
}
package xyz.mstar.teris.ter;
import xyz.mstar.teris.util.Cell;
import xyz.mstar.teris.util.State;
/**
* 只有1种形态
* @author IceFery
*/
public class O extends Ter {
public O() {
cells[0] = new Cell(O, 0, 4);
cells[1] = new Cell(O, 0, 5);
cells[2] = new Cell(O, 1, 4);
cells[3] = new Cell(O, 1, 5);
states = new State[]{
new State(new int[][]{
{0, 0}, {0, 1}, {1, 0}, {1, 1}})};
}
}
package xyz.mstar.teris.ter;
import xyz.mstar.teris.util.Cell;
import xyz.mstar.teris.util.State;
/**
* 2种形态
* @author IceFery
*/
public class S extends Ter {
public S() {
cells[0] = new Cell(S, 0, 4);
cells[1] = new Cell(S, 0, 5);
cells[2] = new Cell(S, 1, 3);
cells[3] = new Cell(S, 1, 4);
states = new State[]{
new State(new int[][]{
{0, 0}, {0, 1}, {1, -1}, {1, 0}}),
new State(new int[][]
{{0, 0}, {-1, 0}, {1, 1}, {0, 1}})
};
}
}
package xyz.mstar.teris.ter;
import xyz.mstar.teris.util.Cell;
import xyz.mstar.teris.util.State;
/**
* 4种形态
* @author IceFery
*/
public class T extends Ter {
public T() {
cells[0] = new Cell(T, 0, 4);
cells[1] = new Cell(T, 0, 3);
cells[2] = new Cell(T, 0, 5);
cells[3] = new Cell(T, 1, 4);
states = new State[]{
new State(new int[][]{
{0, 0}, {0, -1}, {0, 1}, {1, 0}}),
new State(new int[][]{
{0, 0}, {-1, 0}, {1, 0}, {0, -1}}),
new State(new int[][]{
{0, 0}, {0, 1}, {0, -1}, {-1, 0}}),
new State(new int[][]{
{0, 0}, {1, 0}, {-1, 0}, {0, 1}})
};
}
}
package xyz.mstar.teris.ter;
import xyz.mstar.teris.util.Cell;
import xyz.mstar.teris.util.State;
/**
* 2种形态
* @author IceFery
*/
public class Z extends Ter {
public Z() {
cells[0] = new Cell(Z, 1, 4);
cells[1] = new Cell(Z, 0, 3);
cells[2] = new Cell(Z, 0, 4);
cells[3] = new Cell(Z, 1, 5);
states = new State[]{
new State(new int[][]{
{0, 0}, {-1, -1}, {-1, 0}, {0, 1}}),
new State(new int[][]{
{0, 0}, {-1, 1}, {0, 1}, {1, 0}})
};
}
}
package xyz.mstar.teris.ui;
import xyz.mstar.teris.ter.Ter;
import xyz.mstar.teris.util.Cell;
import xyz.mstar.teris.util.ConstantPool;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
/**
* 面板类
* @author IceFery
*/
public class MyPanel extends JPanel implements ConstantPool {
// 申明一大堆图片的引用
public static BufferedImage background;
public static BufferedImage gameover;
// 下降速度
protected static int[] speeds = new int[]{1000, 500, 250, 125};
protected static int speedIndex = 0;
// 让这些引用指向图片
static {
try {
background = ImageIO.read(MyPanel.class.getResource("background.jpg"));
gameover = ImageIO.read(MyPanel.class.getResource("game_over.png"));
} catch (Exception e) {
e.printStackTrace();
}
}
String[] showSpeed = {"UP【简单】", "UP【一般】", "UP【困难】", "UP【起飞】"};
// 分数池
int[] scores_pool = {0, 1, 2, 5, 10};
String[] showState = {"P 【暂停】", "P 【继续】"};
// 生成方块
private Ter currentOne = Ter.randomOne();
private Ter nextOne = Ter.randomOne();
private Cell[][] wall = new Cell[CELL_NUM_IN_HEIGHT][CELL_NUM_IN_WEIGHT];// 面板的方格
private int totalScore = 0;
private int totalLine = 0;
// 游戏的状态
private int gameState;
/**
* 游戏操作逻辑
*/
public void game() {
gameState = PLAYING;
// 实现一个监听器的接口
KeyListener listener = new KeyAdapter() {
public void keyPressed(KeyEvent e) {
int code = e.getKeyCode();
switch (code) {
case KeyEvent.VK_P: // 暂停
if (gameState == PLAYING) {
gameState = PAUSE;
} else if (gameState == PAUSE) {
gameState = PLAYING;
}
break;
case KeyEvent.VK_R: // 重新开始
gameState = PLAYING;
wall = new Cell[20][10];
currentOne = Ter.randomOne();
nextOne = Ter.randomOne();
totalScore = 0;
totalLine = 0;
break;
case KeyEvent.VK_ESCAPE: // 退出游戏
System.exit(0);
case KeyEvent.VK_UP: // 调整难度
if (speedIndex == FLY) { // 处在最高难度
speedIndex = 0;
break;
}
if (speedIndex >= EASY && speedIndex <= FLY) {
speedIndex++;
break;
}
case KeyEvent.VK_W: // 旋转
rotateRightAction();
break;
case KeyEvent.VK_S: // 加速下降
softDropAction();
break;
case KeyEvent.VK_A: // 左移
moveLeftAction();
break;
case KeyEvent.VK_D: // 右移
moveRightAction();
break;
case KeyEvent.VK_SPACE: // 速降
quickDropAction();
break;
}
repaint();
}
};
this.addKeyListener(listener); // 添加指定的按键侦听器,以接收发自此组件的按键事件
this.requestFocus(); // 把输入焦点放在调用这个方法的控件上。
while (true) {
try {
Thread.sleep(speeds[speedIndex]); // 使线程挂起 若干毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
if (gameState == PLAYING) { // 如果正在游戏
if (canDrop()) {
currentOne.softDrop();
} else {
landToWall();
destroyLine();
if (isGameOver()) { // 如果游戏结束
gameState = GAMEOVER;
} else {
currentOne = nextOne;
nextOne = Ter.randomOne();
}
}
repaint(); // 刷新
}
}
}
/**
* 满行消除逻辑
*/
public void destroyLine() {
int lines = 0; // 统计销毁行的行数
Cell[] cells = currentOne.cells; // 让cells指向正在掉落的方块
for (Cell c : cells) {
int row = c.getRow(); // 得到正在掉落的方块的行号
while (row < 20) {
if (isFullLine(row)) { // 如果满行
lines++; // 销毁的行数+1
wall[row] = new Cell[10]; // 第row行用新的小方块组填充
for (int i = row; i > 0; i--) {
// 将清空后的行的上一行的空慢状态赋值给清空的这一行
System.arraycopy(wall[i - 1], 0, wall[i], 0, 10);// 向下平移一行
}
wall[0] = new Cell[10];
}
row++;
}
}
// 从分数池中取出分数, 加入总分数
totalScore += scores_pool[lines];
totalLine += lines;
}
/**
* 界面绘制逻辑
* 调用私有方法paint(), paintCurrentOne(), paintNextOne(), paintScore(), paintState()
*/
public void paint(Graphics g) {
// 1.绘制背景
g.drawImage(background, 0, 0, null);
// 2.确定坐标原点的位置
g.translate(0, 0);
paintWall(g); // 绘制墙
paintCurrentOne(g); // 绘制正在下落的四个方块
paintNextOne(g); // 绘制下一个方块
paintScore(g); // 绘制分数
paintState(g); // 绘制状态
}
/**
* 绘制组:绘制墙
*/
private void paintWall(Graphics g) {
for (int i = 0; i < CELL_NUM_IN_HEIGHT; i++) {
for (int j = 0; j < CELL_NUM_IN_WEIGHT; j++) {
int x = j * CELL_SIZE;
int y = i * CELL_SIZE;
Cell cell = wall[i][j];// 让cell指向面板格子的第 [i][j]个格子
if (cell == null) {
g.drawRect(x, y, CELL_SIZE, CELL_SIZE);// 在(x, y)位置绘制一个 CELL_SIZE*CELL_SIZE大小的矩形方格
} else {
g.drawImage(cell.getImage(), x, y, null);// 在(x, y)位置绘制指定图像中当前可用的图像
}
}
}
}
/**
* 绘制组:绘制正在下落的方块
* @param g
*/
private void paintCurrentOne(Graphics g) {
// 获取currentOne对象的四个元素
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);
}
}
/**
* 绘制组:绘制将要下落的下一个方块
* @param g
*/
private void paintNextOne(Graphics g) {
// 绘制边框
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
g.drawRect(i * CELL_SIZE + 365, j * CELL_SIZE, CELL_SIZE, CELL_SIZE);
}
}
Cell[] cells = nextOne.cells;
for (Cell c : cells) {
int x = c.getCol() * CELL_SIZE + 285;// 获取横坐标和列坐标
int y = c.getRow() * CELL_SIZE + 26;
g.drawImage(c.getImage(), x, y, null);
}
}
/**
* 绘制组:绘制分数
* @param g
*/
private void paintScore(Graphics g) {
g.setFont(new Font("楷体", Font.BOLD, 20));// 设置字体大小为20号
g.setColor(Color.YELLOW);
g.drawString("预览:", 285, 20);
g.drawRect(280, 125, 275, 60);
g.drawString("分数: " + totalScore, 285, 150);
g.drawString("消行: " + totalLine, 285, 180);
g.setColor(Color.red);
g.drawString("R [新游戏]", 360, 270);
g.drawString("ESC [退出游戏]", 360, 300);
g.setColor(Color.BLUE);
g.drawString("SPACE[速降]", 360, 330);
g.drawString("W[翻转]", 415, 370);
g.drawRect(360, 380, 125, 55);
g.drawString("A", 370, 400);
g.drawString("S", 415, 430);
g.drawString("D", 465, 400);
}
/**
* 绘制组:绘制状态
* @param g
*/
private void paintState(Graphics g) {
// 简单
if (speedIndex == EASY) {
g.drawString(showSpeed[EASY], 360, 210);
}
// 一般
if (speedIndex == COMMON) {
g.drawString(showSpeed[COMMON], 360, 210);
}
// 困难
if (speedIndex == TOUGH) {
g.drawString(showSpeed[TOUGH], 360, 210);
}
if (speedIndex == FLY) {
g.drawString(showSpeed[FLY], 360, 210);
}
// 正在进行
if (gameState == PLAYING) {
g.drawString(showState[PLAYING], 360, 240);
}
// 游戏暂停
if (gameState == PAUSE) {
g.drawString(showState[PAUSE], 360, 240);
}
// 游戏结束
if (gameState == GAMEOVER) {
g.drawImage(gameover, 0, 0, null);
}
}
/**
* 监听事件组:向下
*/
public void softDropAction() {
// 判断能否继续下落,调用下落方法,每次按键调用后都要重新绘制,以保证画面流畅
if (canDrop()) { // 可以下落
currentOne.softDrop();
} else { // 不可以下落
landToWall();
destroyLine(); // 消除
currentOne = nextOne; // 将当前的掉落对象的引用指向即将掉落的对象
nextOne = Ter.randomOne(); // 随机生成下一个
}
}
/**
* 监听事件组:快速下落
*/
public void quickDropAction() {
//多次无休眠调用方块下移
for (; ; ) {
if (canDrop()) {
currentOne.softDrop();
} else {
break;
}
}
landToWall();
destroyLine();
if (isGameOver()) { // 游戏结束
gameState = GAMEOVER;
} else {
currentOne = nextOne;
nextOne = Ter.randomOne();
}
}
/**
* 监听事件组:速降到墙
*/
public void landToWall() {
Cell[] cells = currentOne.cells;
for (Cell c : cells) {
int row = c.getRow();
int col = c.getCol();
wall[row][col] = c;// 让面板填充为这个方块的小方块
}
}
/**
* 监听事件组:向左
*/
public void moveLeftAction() {
// 如果大方块不越界,或不予其他方块重合, 就可向左移动
currentOne.moveLeft();
if (outOfBounds() || coincide()) { // 如果数组下标越界,则向右移回来
currentOne.moveRight();
}
}
/**
* 监听事件组: 向右
*/
public void moveRightAction() {
currentOne.moveRight();
if (outOfBounds() || coincide()) {
currentOne.moveLeft();
}
}
/**
* 监听事件组: 顺时针旋转
*/
public void rotateRightAction() {
currentOne.rotateRight();
if (outOfBounds() || coincide()) {
currentOne.rotateLeft(); // 向左转回来
}
}
/**
* 判断组: 是否越界
*/
public boolean outOfBounds() {
Cell[] cells = currentOne.cells;
for (Cell c : cells) {
int row = c.getRow();
int col = c.getCol();
if (col < 0 || col > 9 || row < 0 || row > 19) { // 越界
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 boolean canDrop() {
Cell[] cells = currentOne.cells;
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 boolean isFullLine(int row) {
Cell[] lines = wall[row];
for (Cell c : lines) {
if (c == null) {
return false;
}
}
return true;
}
/**
* 判断组:游戏是否失败
*/
public boolean isGameOver() {
Cell[] cells = nextOne.cells;
for (Cell c : cells) {
int row = c.getRow();
int col = c.getCol();
if (wall[row][col] != null) {
return true;
}
}
return false;
}
}
package xyz.mstar.teris.util;
/**
* 常量池
* @author IceFery
*/
public interface ConstantPool {
//难度常量
int EASY = 0;
int COMMON = 1;
int TOUGH = 2;
int FLY = 3;
//游戏状态常量
int PLAYING = 0;
int PAUSE = 1;
int GAMEOVER = 2;
//面板大小常量
int CELL_SIZE = 27;// 一个格子的大小
int CELL_NUM_IN_HEIGHT = 20;
int CELL_NUM_IN_WEIGHT = 10;
}
package xyz.mstar.teris.util;
import java.awt.image.BufferedImage;
/**
* 背景小格子
* @author IceFery
*/
public class Cell {
/**
* 俄罗斯方块中的最小单位的基本属性
*/
private BufferedImage image; // 图片
private int row; // 行号
private int col; // 列号
/**
* 构造方法
* @param image
* @param row
* @param col
*/
public Cell(BufferedImage image, int row, int col) {
this.image = image;
this.row = row;
this.col = col;
}
/**
* 小方块向左移动
*/
public void left() {
col--;
}
/**
* 小方块向右移动
*/
public void right() {
col++;
}
/**
* 小方块向下移动
*/
public void drop() {
row++;
}
public BufferedImage getImage() {
return image;
}
public void setImage(BufferedImage image) {
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;
}
}
package xyz.mstar.teris.util;
/**
* 形态类
* @author IceFery
*/
public class State {
/**
* 设置八个属性
*/
public int[] rows = new int[4];
public int[] cols = new int[4];
/**
* 构造方法
*/
public State(int[][] rowAndCol) {
super();
for (int i = 0; i < 4; i++) {
this.rows[i] = rowAndCol[i][0];
this.cols[i] = rowAndCol[i][1];
}
}
}