java俄罗斯方块小游戏编写心得01

    首先,对游戏进行分析,俄罗斯方块由七种方块及游戏界面组成,再进行细分,七种方块可拆分成四个最基本小方块,而游戏界面可分为窗体上嵌入一块游戏面板。所以,首先对游戏的最基本元素最基本小方块进行构造,编写一个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<cells.length;i++) {
            cells[i].left();
        }
    }
    public void moveRight() {
        for(int i=0;i<cells.length;i++) {
            cells[i].right();;
        }
    }
    public void softDrop() {
        for(int i=0;i<cells.length;i++) {
            cells[i].drop();
        }
    }
}

七种方块可抽象为字母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);
        }
    }
}

现在运行代码,可以看到游戏界面已经完美,游戏的第一步已经完成

猜你喜欢

转载自blog.csdn.net/z774884795/article/details/80466761