备注,程序采用了 substance.jar ,若缺少可以去掉main方法前面的代码。
一、方块类
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
public class BlockGrid16 {
/*所有方块网格类型*/
public static final int[][] MODELS = {
{0x0063,0x0264,0x0063,0x0264}, //Z
{0x006c,0x0462,0x006c,0x0462}, //反Z
{0x0446,0x002e,0x0622,0x00e8}, //L
{0x0226,0x0047,0x0644,0x0071}, //反L
{0x0027,0x0464,0x0072,0x0131}, //T
{0x0066,0x0066,0x0066,0x0066}, //田
{0x000f,0x4444,0x000f,0x4444}, //l
};
/*随机数生成器*/
public static final Random random = new Random();
/*每一维大小*/
public static final int dimension = 4;
/*方块类型*/
public int type;
/*方块状态*/
public int status;
/*方块速度等级*/
public int level;
/*网格模型*/
public boolean[][] model;
/*当前所在画布*/
public Canvas canvas;
/*网格模型左上角在画布坐标系中的行位置*/
public int row;
/*网格模型左上角在画布坐标系中的列位置*/
public int column;
/*计时器*/
public Timer t;
/*构造方法*/
public BlockGrid16(Canvas canvas,int level) {
this.type = random.nextInt(MODELS.length);
this.status = random.nextInt(MODELS[0].length);
this.level = level;
this.canvas = canvas;
this.row = -999;
this.column = 4;
buildModel(MODELS[type][status]);
}
/*带参数构造方法*/
public BlockGrid16(int type,int status,int level,Canvas canvas,int row,int column) {
this(canvas,level);
this.type = type;
this.status = status;
this.row = row;
this.column = column;
}
public void init(){
row = -3;
column = 4;
/*判断游戏是否结束*/
if (isTouchBottom(row, column)) {
canvas.isEnd = true;
}
}
/*更新方块模型在画布上的位置*/
public void update(){
for (int i=0;i<dimension;i++) {
for (int j=0;j<dimension;j++) {
if ( canvas.isValidPlace(row + i, column + j) && model[i][j]) {
canvas.map[row+i][column+j] = true;
}
}
}
}
/*方块移动*/
public void moveTo(int r,int c) {
/* 判断移动的可能性 */
if (!moveable(r,c))
return;
/*擦除方块模型移动前的位置*/
erase();
/*调整方块模型位置*/
row = r;
column = c;
/*更新*/
update();
/*如果已经到达底部,延时1000-level*50 毫秒更换当前方块模型*/
if (isTouchBottom(row,column)) {
canvas.haveFullLine();
t = new Timer();
t.schedule(new TimerTask(){
public void run() {
if(t != null){
canvas.currBlock = canvas.nextBlock;
canvas.currBlock.init();
canvas.nextBlock = new BlockGrid16(canvas,level);
t = null;
canvas.repaint();
}
}
}, 1000-level*50);
}
}
/*向左移动*/
public void moveLeft() {
moveTo(row,column-1);
}
/*向右移动*/
public void moveRight() {
moveTo(row, column+1);
}
/*向下移动*/
public void moveDown() {
if (t != null && isTouchBottom(row,column)) {
canvas.haveFullLine();
canvas.currBlock = canvas.nextBlock;
canvas.currBlock.init();
canvas.nextBlock = new BlockGrid16(canvas,level);
canvas.repaint();
t = null;
}
moveTo(row+1, column);
}
/*移动到底部*/
public void moveBottom() {
for (int i=-3;i<Canvas.ROW;i++) {
if (moveable(i, column) && isTouchBottom(i, column)) {
t = new Timer();
moveTo(i, column);
canvas.haveFullLine();
canvas.currBlock = canvas.nextBlock;
canvas.currBlock.init();
canvas.nextBlock = new BlockGrid16(canvas,level);
canvas.repaint();
t = null;
break;
}
}
}
/*是否可以移动到指定位置*/
public boolean moveable(int row,int column) {
/*擦除当前要移动方块模型所占位置,以免影响判断*/
erase();
/*判断方块模型每一个单元格将要移动到的位置是否已经被占有*/
for (int i=0;i<dimension;i++) {
for (int j=0;j<dimension;j++) {
/*只需判断方块模型中被占有的单元格*/
if (model[i][j]) {
/*判断合法性*/
if (i + row >= Canvas.ROW || j + column <0 || j + column >= Canvas.COLUMN){
update();
return false;
}
/*判断将要移动的位置上是否已经被占用*/
if (canvas.isValidPlace(i+row, j+column) && canvas.map[i+row][j+column]) {
update();
return false;
}
}
}
}
/*恢复方块模型*/
update();
return true;
}
/*判断方块模型是否到底部*/
public boolean isTouchBottom(int row,int column) {
erase();
for (int i=0;i<dimension;i++) {
for (int j=0;j<dimension;j++) {
if (model[i][j]) {
int nextR = i + 1 + row;
int nextC = j + column;
/*若已经到底部*/
if (nextR == Canvas.ROW) {
update();
return true;
}
/*判断当前单元格下方的单元格是否已经被占用*/
if (canvas.isValidPlace(nextR, nextC)) {
if (canvas.map[nextR][nextC]) {
update();
return true;
}
}
}
}
}
update();
return false;
}
/*变形*/
public void rorate() {
int newStatus = (status + 1)%MODELS[type].length;
if (!rorateable(MODELS[type][newStatus])) {
return;
}else {
status ++;
}
/*擦除原先*/
erase();
/*旋转*/
buildModel(MODELS[type][newStatus]);
/*更新*/
update();
}
//TODO 检查是否可以变形
public boolean rorateable(int keyCode) {
erase();
/*初始化临时模型*/
boolean[][] tempModel = new boolean[dimension][dimension];
int bitKey = 0x8000;
for (int i = 0;i<dimension;i++) {
for (int j = 0;j<dimension;j++) {
tempModel[i][j] = !((bitKey & keyCode) == 0);
bitKey = bitKey >> 1;
}
}
for (int i=0;i<dimension;i++) {
for (int j=0;j<dimension;j++) {
if (tempModel[i][j]) {
if ( row + i >= Canvas.ROW || column+j < 0 || column +j >=Canvas.COLUMN ) {
update();
return false;
}
}
}
}
update();
return true;
}
/*擦除方块*/
public void erase() {
for (int i=0;i<dimension;i++) {
for (int j=0;j<dimension;j++) {
if ( canvas.isValidPlace(row + i, column + j) && model[i][j]) {
canvas.map[row+i][column+j] = false;
}
}
}
}
/*构建网格模型*/
public void buildModel(int modelCode) {
int bitKey = 0x8000;
model = new boolean[dimension][dimension];
for (int i = 0;i<dimension;i++) {
for (int j = 0;j<dimension;j++) {
model[i][j] = !((bitKey & modelCode) == 0);
bitKey = bitKey >> 1;
}
}
}
}
二、画布类
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class Canvas extends JPanel implements KeyListener{
private static final long serialVersionUID = -1354251777507926593L;
/*游戏参数*/
public static final int COLUMN = 10;
public static final int ROW = 18;
public static final int SIZE = 24;
public static final Color backgroundColor = new Color(0xf1,0xf1,0xf1);
public static final Color blockColor = Color.YELLOW;
public static final Color cblockColor = Color.ORANGE;
public static final int[] PADDING = {10,20,10,20};
public static final int WIDTH = PADDING[1] + PADDING[3] + SIZE*COLUMN;
public static final int HEIGHT= PADDING[0] + PADDING[2] + SIZE*ROW;
public final int borderWidth = 1;
public int oneLineScore = 100;
public int level; //游戏当前等级
public int score; //游戏当前分数
/*游戏对象*/
public BlockGrid16 currBlock; //当前操作的方块网格
public BlockGrid16 nextBlock; //下一个将要操作的方块网格
public boolean[][] map; //游戏图 18X10 网格
public Timer downTimer; //下落计数器
/*游戏标志*/
public boolean isEnd = true; /*游戏是否结束*/
public boolean isPause = false; /*游戏是否暂停*/
/*主窗口对象*/
public GameApplication app;
/*线程*/
public ChangeBlockThread cbThread; /*切换方块线程*/
public Canvas(GameApplication app) {
this.app = app;
setPreferredSize(null); /*设置面板大小*/
map = new boolean[ROW][COLUMN]; /*初始化地图数组*/
setBackground(backgroundColor); /*设置背景颜色*/
addKeyListener(this); /*注册键盘监听*/
cbThread = new ChangeBlockThread();
}
public void gameInit(int level) {
/*标志初始化*/
isEnd = false;
isPause = false;
/*地图初始化*/
map = new boolean[ROW][COLUMN];
/*当前操作方块初始化*/
currBlock = new BlockGrid16(this,level);
currBlock.init();
/*下一个操作方块初始化*/
nextBlock = new BlockGrid16(this,level);
/*等级*/
this.level = level;
/*分数*/
this.score = 0;
/*重绘*/
repaint();
}
/*判断指定位置是否为合法位置*/
public boolean isValidPlace(int row,int column) {
if ( row < 0 || row >= ROW || column < 0 || column >= COLUMN)
return false;
return true;
}
/*检查是否底部为满行*/
public void haveFullLine() {
for (int i=0;i<ROW;i++) {
boolean isFullLine = true;
for (int j=0;j<COLUMN;j++) {
if (!map[i][j]) {
isFullLine = false;
break;
}
}
if (isFullLine) {
clearLine(i);
i -- ;
}
}
}
/*删除指定行*/
public void clearLine(int row) {
for (int i=row;i>0;i--) {
for (int j=0;j<COLUMN;j++) {
map[i][j] = map[i-1][j];
}
}
score += oneLineScore;
}
@Override
public void setPreferredSize(Dimension arg0) {
super.setPreferredSize(new Dimension(WIDTH,HEIGHT));
}
@Override
public void paint(Graphics g) {
super.paint(g);
/*画游戏边界*/
g.drawLine(PADDING[3], PADDING[0], PADDING[3]+SIZE*COLUMN, PADDING[0]);
g.drawLine(PADDING[3], PADDING[0] + SIZE*ROW, PADDING[3]+SIZE*COLUMN, PADDING[0]+ SIZE*ROW);
g.drawLine(PADDING[3], PADDING[0], PADDING[3], PADDING[0] + ROW*SIZE);
g.drawLine(PADDING[3]+SIZE*COLUMN, PADDING[0], PADDING[3]+SIZE*COLUMN, PADDING[0] + ROW*SIZE);
/*更新当前操作方块位置*/
if(currBlock != null)
currBlock.update();
/*画方块*/
if(map != null)
for ( int i=0;i<ROW;i++) {
for ( int j=0;j<COLUMN;j++){
if (map[i][j]) {
/*画矩形*/
g.drawRect(PADDING[3] + j*SIZE, PADDING[0] + i*SIZE, SIZE, SIZE);
/*选择颜色变填充矩形*/
Color oldColor = g.getColor();
g.setColor(Color.PINK);
g.fillRect(PADDING[3] + j*SIZE + borderWidth, PADDING[0] + i*SIZE + borderWidth, SIZE -borderWidth , SIZE -borderWidth);
/*还原颜色*/
g.setColor(oldColor);
}
}
}
}
/*键盘监听*/
@Override
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if ( (key >=37 && key <= 40 || key == 32) && !isEnd) {
if (key == 37) {
currBlock.moveLeft();
} else if (key == 39) {
currBlock.moveRight();
} else if (key == 40) {
currBlock.moveDown();
} else if (key == 32) {
currBlock.moveBottom();
} else if (key == 38) {
currBlock.rorate();
}
System.out.println(score);
repaint();
}else if ( key == 80) { //P
}else if ( key == 78) { //N
if (downTimer != null) {
downTimer.cancel();
downTimer = null;
}
if (isEnd != true)
if( JOptionPane.showConfirmDialog(this, "您确定要重新开始吗?", "新的游戏",
JOptionPane.YES_NO_OPTION) == 0) {
gameInit(app.gameLevel);
downTimer = new Timer();
downTimer.schedule(new TimerTask(){
public void run() {
currBlock.moveDown();
repaint();
}
}, 0, 1000-level*50);
return;
}
gameInit(app.gameLevel);
downTimer = new Timer();
downTimer.schedule(new TimerTask(){
public void run() {
currBlock.moveDown();
repaint();
}
}, 0, 1000-level*50);
}else if ( key == 82) { //R
}else if ( key == 79) { //O
}
//上:38
//下:40
//左:37
//右:39
//P:80
//N:78
//R:82
//O:79
}
@Override
public void keyReleased(KeyEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void keyTyped(KeyEvent arg0) {
// TODO Auto-generated method stub
}
/*方块下落控制的线程*/
class ChangeBlockThread extends Thread{
public long delay = 1000;
@Override
public void run() {
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
三、主界面类
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.JToolBar;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class GameApplication extends JFrame implements ActionListener{
private static final long serialVersionUID = -1653174441012031192L;
private Canvas canvas;
public int gameLevel = 10;
/*工具栏*/
private JToolBar toolbar;
/*构造方法*/
public GameApplication() {
init();
}
/*界面初始化函数*/
private void init() {
setTitle("俄罗斯方块 by:luiyikeke");
add(createOrGetCanvas());
add(createOrGetToolBar(),BorderLayout.NORTH);
pack();
setLocationRelativeTo(null);
canvas.requestFocus();
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
/*创建画布面板,位于分割面板的左部*/
private Canvas createOrGetCanvas(){
if(canvas == null){
canvas = new Canvas(this);
}
return canvas;
}
/*创建工具栏*/
private JToolBar createOrGetToolBar() {
if ( toolbar == null) {
toolbar = new JToolBar();
toolbar.add(createButtonWithAction("重新开始(N)"));
toolbar.add(createButtonWithAction("暂停(P)"));
toolbar.add(createButtonWithAction("排行榜(R)"));
toolbar.add(createButtonWithAction("设置(O)"));
}
return toolbar;
}
/*创建按钮,并且带有事件监听*/
private JButton createButtonWithAction(String btnString) {
JButton btn = new JButton(btnString);
btn.addActionListener(this);
return btn;
}
// ------------------------------------
// 事件监听
// ------------------------------------
@Override
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
if("重新开始(N)".equals(command)) {
canvas.gameInit(gameLevel);
}else if("暂停(P)".equals(command)) {
}else if("排行榜(R)".equals(command)) {
}else if("设置(O)".equals(command)) {
}
canvas.requestFocus();
}
public static void main(String[] args) {
try {
// 设置程序将采用的界面风格
UIManager
.setLookAndFeel("org.jvnet.substance.skin.SubstanceOfficeSilver2007LookAndFeel");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (UnsupportedLookAndFeelException e) {
e.printStackTrace();
}
new GameApplication().setVisible(true);
}
}