首先,对游戏进行分析,俄罗斯方块由七种方块及游戏界面组成,再进行细分,七种方块可拆分成四个最基本小方块,而游戏界面可分为窗体上嵌入一块游戏面板。所以,首先对游戏的最基本元素最基本小方块进行构造,编写一个Cell类
对Cell类进行分析,最基本小方块所具有的属性为行row,列col,图片Image;所具有的行为为左移,右移,下落(七种方块的左移、右移、下落实质上为最基本小方块的左移、右移、下落)。
注:在编写代码时,需要遵守JavaBean规范,即程序员在定义类时,默认遵守的一种规范。
代码如下:
package com.tetris;
import java.awt.image.BufferedImage;
public class Cell {
private int row;
private int col;
private BufferedImage image;
public Cell() {}
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;
}
@Override
public String toString() {
return "Cell [row=" + row + ", col=" + col + ", image=" + image + "]";
}
//向左移动
public void left() {
col--;
}
//向右移动
public void right() {
col++;
}
public void drop() {
row++;
}
}
游戏最基本小方块类已经写完,接下来的第二步,就是需要用最基本小方块组合出来七种方块,而七种方块统一是由四个最基本小方块组成,所以我们可以先编写一个四格方块类,即取出四个最基本小方块放入二维数组中,构成一个四格方块,但是此时方块还未组成七种基本图形,只是在定义其行为及属性,为接下来的七种图形的构造提供方便。最终的七种方块的形状,由各个类继承本类来进行构造。对该类进行分析,四格方块所具有的属性为四个最基本小方块,方法同最基本小方块方法相同,即左移、右移、下落
代码如下:
package com.tetris;
public class Tetromino {
protected Cell[] cells=new Cell[4];
public void moveLefr() {
for(int i=0;i
}
}
public void moveRight() {
for(int i=0;i
}
}
public void softDrop() {
for(int i=0;i
}
}
}
七种方块可抽象为字母I,S,T,Z,L,O,J,如图所示,若想将最基本小方块组合成七种方块,需要将最基本方块放在一个二维数组中,行列号图中已经给出,所以可编写出I,S,T,Z,L,O,J类,分别代表七种方块。
七种小方块图片为:I: J: L: O: S: T: Z: ,在使用图片时,需要将图片放入到同一包下。
代码如下:
I:
package com.tetris;
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);
}
}
J:
package com.tetris;
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);
}
}
L:
package com.tetris;
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);
}
}
O:
package com.tetris;
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);
}
}
S:
package com.tetris;
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);
}
}
T:
package com.tetris;
public class T extends Tetromino{
//提供构造器,进行初始化T形的四格方块的位置
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);
}
}
Z:
package com.tetris;
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);
}
}
代码到了这,七种方块已经构造完成,但是代码到了这会出错,因为我们在使用图片时只是将图片引到了同一包下,并没有将图片读进来,如果我们在这七种方块类中加入图片读取,那么就会出现一个问题,在最终写完后,图片会是使用一次加载一次,会造成不必要的浪费,所以我们现在需要一个主类,来将图片在程序运行最初就将图片加载进来,在以后的使用中只对其进行调用即可,不需要重新加载。
代码如下:
package com.tetris;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
public class Tetris{
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 {
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();
}
}
}
代码到了这里,根据之前的分析,七种方块的构造已经完成,还需要一个游戏面板,而游戏面板是一进游戏首先出现的就是这个面板,所以也可以说它是游戏的入口,也就是整个程序的开始的地方,所以需要写在主类的main方法中,因为主类即游戏界面,所以可以使主类继承JPanel,那么在创建主类对象时,就是创建了一个游戏面板。
代码如下:
package com.tetris;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Tetris extends JPanel{
public static void main(String[] args) {
//创建一个窗口对象
JFrame jf=new JFrame("火拼俄罗斯");
//创建游戏界面,即面板
Tetris panle=new Tetris();
//将面板嵌入窗口
jf.add(panle);
//设置窗口可见
jf.setVisible(true);
//设置窗口的尺寸
jf.setSize(535, 595);
//设置窗口位置
jf.setLocationRelativeTo(null);
//设置窗口关闭,即程序终止
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
此时如果运行的话只会出现一个游戏界面,而游戏界面上什么都没有,因为并没有在面板上绘制任何东西,接下来我们需要分析一下面板上都要绘制什么,首先,有游戏背景,其次,在游戏开始时我们会有一个正在下落的方块,在右边还会有一个位置显示下一个将要下落的方块,这是面板上可见的元素,再分析,如果想要让方块显示在游戏界面上,那便需要给方块一个初始位置,而位置如何表示,我们可以衍生出一个抽象的墙的概念,即方块是在墙上的,这便是面板上可见与不可见的元素。我们需要将这些属性封装在JPanel的自动调用方法paint()方法中,但是我们需要对paint()方法进行重写。
代码如下:
package com.tetris;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Tetris extends JPanel{
public void paint(Graphics g) {
//绘制背景
g.drawImage(background, 0, 0, null);
//绘制墙
paintWall(g);
//绘制正在下落的四格方块
paintCurrentOne(g);
//绘制下一个将要下落的四格方块
paintNextOne(g);
}
}
此时我们需要对paintWall()、paintCurrentOne()、paintNextOne()方法进行定义,对paintWall()(绘制墙)方法进行分析,如果想要使方块显示在墙上,那就需要给方块位置,位置用什么来表示,我们可以将墙定义为一个10列20行的二维数组,而墙的二维数组内的元素为与最基本小方块长宽相同的小方格,如上图。
代码如下:
package com.tetris;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Tetris extends JPanel{
public void paintWall(Graphics g) {
//外层循环控制行数
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) {
//增加难度本句可加注释
g.drawRect(x, y, CELL_SIZE, CELL_SIZE);
}else {
g.drawImage(cell.getImage(), x, y, null);
}
}
}
}
}
然后分析即将下落方块与正在下落方块,之前已经定义过七种图形方块,可以在主类中首先定义两个Tetromino类型变量currentOne和nextOne,然后给两个变量赋值,因为在游戏中方块都是随机出现,所以需要一个随机生成方块的方法,在Tetromino类中定义此方法
代码如下:
package com.tetris;
public class Tetromino {
protected Cell[] cells=new Cell[4];
public static Tetromino randomeOne() {
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 I();break;
case 3:t=new J();break;
case 4:t=new L();break;
case 5:t=new S();break;
case 6:t=new Z();break;
}
return t;
}
}
有了随机生成方法后,就可以给currentOne与nextOne赋值,接下来就可以重新定义方法paintCurrentOne()方法
代码如下:
package com.tetris;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Tetris extends JPanel{
public void paintCurrentOne(Graphics g){
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);
}
}
}
接下来继续分析nextOne,即将下落方块因为要显示在右上角,所以他的横坐标至少大于260像素,纵坐标至少大于26.。
代码如下:
package com.tetris;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Tetris extends JPanel{
public void paintNextOne(Graphics g) {
//获取nextOne对象的四个元素
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);
}
}
}
现在运行代码,可以看到游戏界面已经完美,游戏的第一步已经完成