俄罗斯方块
俄罗斯方块
我们通过四个部分来完成这个项目
一、初始化图形界面
首先创建一个GameStart类来实现图形界面并串接起我们所有的部件
public class GameStart extends JFrame {
public GameStart() {
init();
}
public static void main(String[] args) { //主方法执行
new GameStart();
}
主要部分如下:
private void init() {
setTitle("俄罗斯方块");
RussiaGame rus = new RussiaGame();
JMenuBar menu = new JMenuBar();
setJMenuBar(menu);
JMenu help = new JMenu("帮助");
help.setActionCommand("help");
JMenuItem about = help.add("关于");
about.setActionCommand("about");
about.addActionListener(new ActionListener() {
//初始“ 关于框中的内容 ”
public void actionPerformed(ActionEvent e) {
StringBuilder sbd = new StringBuilder(); //对字符串的修改比string buffer 更快
sbd.append("作者:刘昊鑫,陈东升");
if (e.getActionCommand().equals("about")) //当点击关于的子菜单时
JOptionPane.showMessageDialog(null, sbd.toString()); //调用了j option pane 类中的方法生成消息对话框 ,null为正前方位置显示
}
});
add(rus, java.awt.BorderLayout.CENTER); //大框架中添加组件
addKeyListener(rus); //键盘监听
menu.add(help);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setUndecorated(false); //不隐藏边框
setSize(500, 500);
setLocation(300,200);
setVisible(true);
setResizable(false);
}
}
二、构建方块形状
方块组合形状通过三维数组表示,1表示该处有方块,0表示没有
创建shapes类实现
代码如下:
public class Shapes {
/**
* 初始化形状
*/
public static final int [][][]SHAPES=
{
/*************T******************/
{{0,0,0,0,0},
{0,0,0,0,0} ,
{0,1,1,1,0},
{0,0,1,0,0},
{0,0,0,0,0}
},
/***********反L****/
{{0,0,0,0,0},
{0,0,1,0,0},
{0,0,1,0,0},
{0,1,1,0,0},
{0,0,0,0,0}
},
/************口***************add*/
{{0,0,0,0,0},
{0,0,0,0,0} ,
{0,1,1,0,0},
{0,1,1,0,0},
{0,0,0,0,0},
},
/************L****************/
{{0,0,0,0,0},
{0,1,0,0,0},
{0,1,0,0,0},
{0,1,1,0,0},
{0,0,0,0,0}
},
/*************Z****************/
{{0,0,0,0,0},
{0,0,0,0,0},
{0,1,1,0,0},
{0,0,1,1,0},
{0,0,0,0,0}
},
/*************1****************/
{{0,1,0,0,0},
{0,1,0,0,0} ,
{0,1,0,0,0},
{0,1,0,0,0},
{0,0,0,0,0}
},
/*************反Z****************/
{{0,0,0,0,0},
{0,0,0,0,0},
{0,0,1,1,0},
{0,1,1,0,0},
{0,0,0,0,0}
},
{{0,0,0,0,0},
{0,0,0,0,0} ,
{0,0,0,0,0},
{1,1,1,1,0},
{0,0,0,0,0}
}
};
三、方块的存储与下落
1、存方块
/**
* 存方块
* 方块数组
* @param shapeMap
*/
public static final void wirte(int [][] shapeMap,int row,int column){
try {
File fl=new File("shape.txt");
if(!fl.exists()){
return ;
}
BufferedWriter bft=new BufferedWriter(new FileWriter(fl));
//用字节缓冲流写入构建游戏时的方块边界
for(int i=0;i<row;i++){
for(int j=0;j<column;j++){
bft.append(shapeMap[i][j]+"");
}
bft.append("\n");
}
bft.flush(); //刷新缓存
bft.close();
} catch (Exception e) {
}
}
2、写方块
/**
* 写方块
* @return
* 方块数组
*/
public static final int [][]read(int row,int column) {
int [][]shapeMap=new int[row][column];
try {
File fl=new File("shape.txt");
if(!fl.exists()){
fl.mkdir();
}
BufferedReader bfd=new BufferedReader(new FileReader(fl));
String read=null;
int index=0;
while((read=bfd.readLine())!=null){
char[]ary= read.toCharArray(); //将fl中的数据传到字符数组中储存
for(int i=0,size=ary.length;i<size;i++){
char ch=ary[i];
shapeMap[index][i]=Integer.parseInt(ch+""); //将string解析为整数存入shape map数组
}
index++;
}
bfd.close();
}
catch (Exception e) {
}
return shapeMap;
}
注意这里的shape.text就是游戏中方块活动的边界
同下
四、游戏组件
(1) 核心算法的实现
1、首先创建一个RussiaGame类,定义我们需要的量
public class RussiaGame extends JPanel implements Serializable, KeyListener {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* 数组的行数和列数 初始话界面的大小为 22行 12列
*/
private static final int ROW_SIZE = 22, COLUMN_SIZE = 12;
/**
* 界面宽度
*/
private static final int WIDTH_SIZE = 344;
/**
* 界面高度
*/
private static final int HEIGHT_SIZE = 375;
/**
* 储存格子的数组
*/
private static int[][] shapeMap = new int[ROW_SIZE][COLUMN_SIZE];
int[][] random;// 用来装载随机形状的二维数组
/**
* 用来记载移动的坐标
*/
private int move_x = 5, move_y = -2;
/**
* 方块大小
*/
private static final int SQUARE_SIZE = 20;
/**
* 方块矩阵的大小
*/
private static final int MATRIX_SIZE = 5;
/**
* 分数
*/
private int scores = 0;
/**
* 判断游戏是否结束的标志
*/
private boolean canPlay = true;
/**
* 休眠时间
*/
private int SLEEP_TIME = 500;
/**
* 暂停游戏
*/
private boolean SLEEPGAME = true;
/**
* ENTER 建的值用来标记暂停事件
*/
static final int VK_ENTER = 10;
/**
* 下一个方块
*
*/
static int[][] next;
/**
* 用来装载当期方块和下一个方块
*/
private LinkedList<int[][]> shapeList = new LinkedList<int[][]>();
2、获取墙壁大小
/**传入墙壁大小
*
*
*/
private void initShape() {
shapeMap = FileHelpers.read(ROW_SIZE, COLUMN_SIZE);
}
3、判断方块能否旋转
/**
* 判断是否能旋转 如果下一个图形所在的坐标没有越界和没有
*
* @return true转换 false 不转换
*/
private boolean canTurnChange(int[][] newRandom) {
boolean canTurn = true;
if (move_y > 0)
for (int i = MATRIX_SIZE - 1; i >= 0; i--) {
for (int j = 0; j < MATRIX_SIZE; j++) {
if (newRandom[i][j] == 1) {
if (shapeMap[move_y + i][move_x + j] == 1
|| shapeMap[move_y + i][move_x + j] == 2) {
canTurn = false;
break;
}
}
}
}
if (move_x == -1 || move_y == 18 || move_x == 8) {
canTurn = false;
}
if (random == Shapes.SHAPES[5] || random == Shapes.SHAPES[7]) {
if (move_y >= -1 && shapeMap[move_y + 1][move_x + 3] == 1) {
canTurn = false;
}
if (move_x == 0 || move_x == 3 || move_y == 3) {
canTurn = false;
}
}
return canTurn;
}
4、消行
/**
* 消行
*
* @return
*/
private int[][] delteLine() {
int count = 0;
for (int i = ROW_SIZE - 2; i >= 1; i--) {
if (canDelete(shapeMap[i])) {
scores += 10;
for (int j = 1; j <= 10; j++) {
shapeMap[i][j] = 0;
count++;
}
}
if (count >= 1) {
int[] temp = shapeMap[i];
shapeMap[i] = shapeMap[i - 1];
shapeMap[i - 1] = temp;
}
}
return shapeMap;
}
protected boolean canDelete(int[] line) {
for (int i = 1, size = 10; i <= size; i++) {
if (line[i] == 0) {
return false;
}
}
return true;
}
5、重新开始的实现
/**
* 重新开始游戏
*/
private void restart() {
for (int i = 0; i <= 20; i++) {
for (int j = 1; j <= 10; j++) {
shapeMap[i][j] = 0;
}
}
canPlay = true;
}
6、判断游戏是否结束
/**
* 判断游戏是否结束
*
* @return
*/
private boolean gameOver() {
int[] top = shapeMap[1];// 获得顶部
for (int i = 1; i < COLUMN_SIZE - 1; i++) {
if (top[i] == 1) {
return true;
}
}
return false;
}
7、方块是否能下落
/**
* 方块能是否能下落
*
* @return
*/
private boolean canDown() {
boolean canDown = true;
int x = move_x == -1 ? move_x + 2 : move_x + 1;
int len = shapeLength();
if (move_y >= 0 && move_x < 12)
for (int i = MATRIX_SIZE - 1; i >= 0; i--) {
for (int j = 0; j < MATRIX_SIZE; j++) {
if (random[i][j] == 1) {
if (shapeMap[move_y + i + 1][move_x + j] == 1) {
canDown = false;
break;
}
}
}
}
if (move_y >= 0 && x < 12 && shapeMap[move_y + len][x] == 2) {
canDown = false;
}
return canDown;
}
8、获取方块长度
/**
* 获得方块的长度
*/
private int shapeLength() {
for (int leng = 5; leng >= 1; leng--) {
int[] number = random[leng - 1];
for (int index = 0; index < 5; index++) {
if (number[index] == 1) {
return leng;
}
}
}
return -1;
}
9、画方块及组件
/**
* 画当前方块
*
* @param gs
*/
private void paintCurr(java.awt.Graphics gs, int[][] randoms) {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (randoms[i][j] == 1) {
gs.setColor(Color.yellow);
gs.fill3DRect(j * SQUARE_SIZE + move_x * SQUARE_SIZE, i
* SQUARE_SIZE + move_y * SQUARE_SIZE, SQUARE_SIZE,
SQUARE_SIZE, true);
}
}
}
}
private void paintNext(java.awt.Graphics gs) {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (next[i][j] == 1) {
gs.setColor(Color.YELLOW);
gs.fill3DRect(j * SQUARE_SIZE + 250, i * SQUARE_SIZE,
SQUARE_SIZE, SQUARE_SIZE,true);
}
}
}
}
@Override
// 画组件的方法
protected void paintComponent(java.awt.Graphics g) {
super.paintComponent(g);
Image bc = new ImageIcon("222.jpg").getImage();
g.drawImage(bc, 0, 0, getWidth(), getHeight(), this);
g.setColor(Color.red);
g.drawString("分数" + scores, 250, 150);
g.drawString("F5 清屏重新开始 ", 250, 190);
g.drawString("ENTER 暂停\\开始游戏", 250, 230);
g.drawString("ESC 退出并保存", 250, 270);
Color green = new Color(140, 140, 140);
g.setColor(green);
paintNext(g);
// 绘制当前方块
paintCurr(g, random);
// 画已经落下的方块
for (int j = 0; j < ROW_SIZE; j++) {
for (int i = 0; i < COLUMN_SIZE; i++) {
if (shapeMap[j][i] == 1) {
g.setColor(green);
g.fill3DRect(i * SQUARE_SIZE, j * SQUARE_SIZE, SQUARE_SIZE,
SQUARE_SIZE, true);
}
if (shapeMap[j][i] == 2) {
g.setColor(Color.black);
g.drawRect(i * SQUARE_SIZE, j * SQUARE_SIZE, SQUARE_SIZE,
SQUARE_SIZE);
}
}
}
};
10、画墙壁
/**
* 画墙壁的方法
*/
private void clearMap() {
shapeMap = new int[ROW_SIZE][COLUMN_SIZE];
for (int i = 0; i < ROW_SIZE; i++) {
shapeMap[i][0] = 2;
shapeMap[i][COLUMN_SIZE - 1] = 2;
}
// 画底部的格子
for (int j = 0; j < COLUMN_SIZE; j++) {
shapeMap[ROW_SIZE - 1][j] = 2;
}
}
private void turnLef() {
System.out.println(SQUARE_SIZE*(move_x+3));
if (move_y>=0&&canLeft()) {
move_x--;
shapeMap = delteLine();
repaint();
}
}
11、生成随机形状
/**
* 生成随机形状
*
* @return
*/
private void randomShape() {
Random ran = new Random();
if (shapeList.size() == 2) {
shapeList.removeFirst();
shapeList.addLast(Shapes.SHAPES[ran.nextInt(8)]);
} else {
shapeList.addFirst(Shapes.SHAPES[ran.nextInt(8)]);
shapeList.addLast(Shapes.SHAPES[ran.nextInt(8)]);
}
random = shapeList.getFirst();
next = shapeList.getLast();
}
12、判断左右移动
/**
* 判断是否能左移
*
* @return
*/
private boolean canLeft() {
boolean canLeft = true;
if(move_y>=0)
for (int i = MATRIX_SIZE - 1; i >= 0; i--) {
for (int j = 0; j < MATRIX_SIZE; j++) {
if (random[i][j] == 1) {
if (shapeMap[move_y + i][move_x + j - 1] == 1
|| shapeMap[move_y + i][move_x + j - 1] == 2) {
canLeft = false;
break;
}
}
}
}
return canLeft;
}
/**
* 判断是否能右移
*
* @return
*/
private boolean canRight() {
boolean canRight = true;
if(move_y>=0)
for (int i = MATRIX_SIZE - 1; i >= 0; i--) {
for (int j = 0; j < MATRIX_SIZE; j++) {
if (random[i][j] == 1) {
if (shapeMap[move_y + i][move_x + j + 1] == 1
|| shapeMap[move_y + i][move_x + j + 1] == 2) {
canRight = false;
break;
}
}
}
}
return canRight;
}
private void turnRight() {
if (move_y>=0&&canRight()) {
move_x++;
shapeMap = delteLine();
repaint();
}
}
private void turnUp() {
int[][] newRandom = xuanzhuan();
if (canTurnChange(newRandom)) {
if (random == Shapes.SHAPES[5]) {
newRandom = Shapes.SHAPES[7];
} else if (random == Shapes.SHAPES[7]) {
newRandom = Shapes.SHAPES[5];
}
random = newRandom;
}
shapeMap = delteLine();
repaint();
}
private void turnDown() {
if (canDown())
move_y++;
shapeMap = delteLine();
repaint();
}
14、方块的旋转
/**
* 旋转矩阵
*
* @return
*/
public int[][] xuanzhuan() {
int[][] newRandom = new int[random.length][random[0].length];
if (random == Shapes.SHAPES[2]) {
return random;
}
for (int i = 0; i < random.length; i++) {
for (int j = 0; j < random[0].length; j++) {
newRandom[j][random[0].length - i - 1] = random[i][j];
}
}
return newRandom;
}
(2)事件的监听和实现
为便于常量的使用,我们在其内部再定义一个GameLitsner类
class GameLitsner implements ActionListener {
public void actionPerformed(ActionEvent e) {
if (SLEEPGAME)
if (gameOver() == false) {
if (canDown()) {
move_y++;
shapeMap = delteLine();
repaint();
} else {
shapeMap = delteLine();
addPoint();
move_x = 5;
move_y = -5;
randomShape();
repaint();
}
}
else
canPlay = false;
}
}
/**
* 得分
*/
private void addPoint() {
int len = shapeLength();
int index = len == 3 ? 3 : 4;
for (int i = 0; i < 5; i++, index--) {
for (int j = 0; j < 5; j++) {
if (random[i][j] == 1 && move_y + len - index >= 0) {
shapeMap[move_y + len - index][move_x + j] = 1;
}
}
}
}
/**
*
*/
public void keyPressed(KeyEvent key) {
if (canPlay && SLEEPGAME)
switch (key.getKeyCode()) {
// 改变形状
case KeyEvent.VK_UP:
case KeyEvent.VK_W:
turnUp();
break;
// 下落
case KeyEvent.VK_DOWN:
case KeyEvent.VK_S:
turnDown();
break;
// 左移
case KeyEvent.VK_LEFT:
case KeyEvent.VK_A:
turnLef();
break;
// 右移
case KeyEvent.VK_RIGHT:
case KeyEvent.VK_D:
turnRight();
break;
case KeyEvent.VK_ESCAPE:
int isSave = JOptionPane.showConfirmDialog(null, "是否保存游戏");
if (isSave == 0) {
FileHelpers.wirte(shapeMap, ROW_SIZE, COLUMN_SIZE);
} else {
clearMap();
FileHelpers.wirte(shapeMap, ROW_SIZE, COLUMN_SIZE);
}
System.exit(1000);
break;
}
if (key.getKeyCode() == KeyEvent.VK_F5) {
restart();
}
if (key.getKeyCode() == VK_ENTER) {
SLEEPGAME = SLEEPGAME == true ? false : true;
} else if (canPlay == false) {
JOptionPane.showMessageDialog(null, "Game Over!");
scores = 0;
}
}
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
}
效果如下: