public static BufferedImage T; //T形状的方块 以下以此类推
public static BufferedImage I;
static {
try {
T=ImageIO.read(Tetris.class.getResource("T.png")); //其他以此类推
background=ImageIO.read(Tetris.class.getResource("tetris.png"));
}catch(Exception e)
{
e.printStackTrace();
}
}
if(cell==null) {
g.drawRect(x, y, CELL_SIZE, CELL_SIZE);} //x表示横坐标
else {
g.drawImage(cell.getImage(),x,y,null);}
Tetris类的属性有正在下落、即将下落的四格方块组成的图形(有七种)和墙(方块在墙上移动,墙为20*10的方格)。
private Tetromino currentOne=Tetromino.randomOne();
private Tetromino nextOne=Tetromino.randomOne();
private Cell[][] wall=new Cell[20][10]; //设置墙为20*10的方格组合 private static final int CELL_SIZE=26; //宽度为26
接下来是玩游戏的方法,也是游戏的主要逻辑。
定义start方法,在其中创建一个键盘监听器对象,接受键盘的下左右事件,并重新绘制新位置的图形(repaint方法即可实现,repaint()通过调用线程再由线程去调用update()方法清除当前显示并再调用paint()方法进行绘制下一个需要显示的内容.这样就起到了一种图片的交替显示从而在视角上形成了动画,update()即用来清除当前显示并调用paint()方法)。
为了使程序的运行速度看起来慢一些(即为了防止块下落的过快而人来不及反应),设置了程序的休眠时间为300毫秒。
也需要判断是否能继续下落,即下一行的格有没有方块或者有没有到达底部,如果没有则继续下落,如果有则停止下落,在墙上的相应位置设置有方块(即非null),并更新下一个图形。再调用repaint函数,使键盘没有任何操作时,图形仍能继续下落。
在键盘有操作时,需要判断是按了哪个键,这件事键盘监听的对象即可做到。不同的键选择不同的操作。在判断没有左右出界和即将移动的地方没有其他方块后,调用Tetromino中的移动函数,移动整个图形,如果不再能改变位置,则在墙上表明位置然后绘制这个图形。(在此处需要在键盘产生时间后立刻改变墙的位置,在下次画图时能直接画到移动到的位置,如果按很多次下而不实时改变墙中的位置,则会影响游戏体验,方块并没有因为按动的频率加快而速度加快,不符合游戏人的需要)。
在移动时,先向该移动的方向移动,如果出界或重合,则再向反方向移动一次。判断左右出界时,如果列小于0或者大于9则出界,判断即将移动的位置有无方块判断墙的对应位置是否为空即可。判断这个时不能先判断有无重合,因为在这种操作中可能存在列号为-1时,判断重合函数会报错,如果把判断出界放在前面,即会捕捉这个错误,或语句也不会继续执行。
上述即是目前简单实现方块移动的过程,之后还会实现图形的旋转~
附全部代码:
Cell类:
import java.awt.image.BufferedImage;
/**
* 俄罗斯方块中的最小单位-方格
* 属性:row col image
* 行为(方法):left() right() down()
*/
public class Cell {
private int row;//行
private int col;//列
private BufferedImage image;
public Cell(int row, int col, BufferedImage image) {
super();
this.row = row;
this.col = col;
this.image = image;
}
public Cell() {
super();
}
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++;
}
}
/**
* 四格方块
* 属性:
* --cells ---四个方块
* 行为;
* moveLeft()
* moveRight()
* softDrop()
*
*/
public class Tetromino {
protected Cell[] cells=new Cell[4];
public void moveLeft()
{
for(Cell c:cells)
c.left();
}
public void moveRight()
{
for(int i=0;i<4;i++)
cells[i].right();
}
public void softDrop()
{
for(int i=0;i<4;i++)
cells[i].drop();
}
/*
* 随机生成一个四格方块
*/
public static Tetromino randomOne() {
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;
}
}
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
/*
* 俄罗斯方块的主类
* 加载静态资源
* 前提:必须是面板JPanel,可以嵌入窗口
* 面板上自带一个画笔,有一个功能:自动绘制
* 其实是调用了JPanel里的paint()
*/
public class Tetris extends JPanel{
/*
* 属性:正在下落的四格方块
* 将要下落的四格方块
*/
private Tetromino currentOne=Tetromino.randomOne();
private Tetromino nextOne=Tetromino.randomOne();
/*
* 属性:墙 20*10的方格 宽度为26
*/
private Cell[][] wall=new Cell[20][10];
private static final int CELL_SIZE=26;
public static BufferedImage T; //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();
}
}
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);
}
}
}
}
public void paintCurrentOne(Graphics g)
{
Cell[] cells=currentOne.cells;
for(int i=0;i9)
return true;
}
return false;
}
public void moveRightAction() {
//没出界或者右面的方块重合
currentOne.moveRight();
//不可以改变这两个函数的位置 因为coincide里的数组不允许参数为-1的时候
if(outOfBounds()||coincide())
{
currentOne.moveLeft();
}
}
/*
* 判断是否下落
*/
public boolean canDrop()
{
Cell[] cells=currentOne.cells;
for(Cell cell:cells) {
/*
* 获取每个元素的行号和列号
* 判断
* 只要有一个元素的下一行有方块
* 或者只要有一个元素到达最后一行
* 就不能再下落了
*/
int row=cell.getRow();
int col=cell.getCol();
if(row==19) {
return false;
}
if(wall[row+1][col]!=null) {
return false;
}
}
return true;
}
/*
* 当不能再下落时,需要将四格方块,嵌入到墙中
* 也就是存储在二维数组中相应位置中
*/
public void LandToWall() {
Cell[] cells=currentOne.cells;
for(Cell cell:cells) {
int row=cell.getRow();
int col=cell.getCol();
wall[row][col]=cell;
}
}
//启动游戏的入口
public static void main(String[] args) {
//1、创建一个窗口对象
JFrame frame=new JFrame("俄罗斯方块");
//创建游戏界面即面板
Tetris panel=new Tetris();
//将面板嵌入窗口
frame.add(panel);
//2、设置为可见
frame.setVisible(true);
//3、设置窗口大小尺寸
frame.setSize(535, 600);
//4、设置窗口居中
frame.setLocationRelativeTo(null);
//5、设置窗口关闭,即程序终止
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel.setBackground(Color.yellow);
panel.start();
}
}