目前学到Swing第二章,这两天没有学新知识,写了个俄罗斯方块。
写之前想不好怎么实现,找来别人的程序看了一下,再加上自己的想法,做了下面这个半成品,接下来可以加上各种菜单、按钮贴图等美化,都是些锦上添花的动作,繁琐但不难。
我觉得写俄罗斯方块,难点在于如何将方块的形状(七种)、状态(四种翻转)、动作(左、右、下落)等提炼成数组。 这里采用四维数组来表示每一种方块:【种类】【翻转】【X坐标】【Y坐标】,每种方块都画在一个4*4的大方块中,把这个大方块作为一个整体进行移动。再写了一个方法遍历大方块,判断该方块的位置是否合法。
有了合适的模型,再把每个步骤转化成代码,就不难了。
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.util.Arrays;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JCheckBox;
import javax.swing.Timer;
import javax.swing.JOptionPane;
public class KingTetris{
// 20行,10列
private final int ROW = 20;
private final int COL = 10;
// 每个方块边长,单位为像素
private final int LEN = 35;
// 游戏区域的左、上边距
private final int LEFT_MARGIN = LEN*2;
private final int UP_MARGIN = LEN;
// 面布大小,单位为像素
private final int AREA_WIDTH = LEN*22;
private final int AREA_HEIGHT = LEN*22;
// 是否需要网格
private boolean showGrid = true;
// 是否彩色,将来可以作为贴图控制
private boolean isColor = true;
// 得分
private int score = 0;
// 画布
private MyCanvas drawArea = new MyCanvas();
// 窗口
private JFrame f = new JFrame("俄罗斯方块");
// 画图用的image
private BufferedImage image = new BufferedImage(AREA_WIDTH, AREA_HEIGHT, BufferedImage.TYPE_INT_RGB);
private Graphics g = image.createGraphics();
// 数组,用于保存背景
private int[][] map = new int[COL][ROW];
// 数组,用于保存颜色
private Color[] color = new Color[]{Color.green, Color.red, Color.orange, Color.blue, Color.cyan, Color.yellow, Color.magenta, Color.gray};
//默认灰色
private final int DEFAULT = 7;
private Color[][] mapColor = new Color[COL][ROW];
//组件的横坐标
int wordX = LEN*14;// 组件的横坐标
int wordY = LEN*9; // 字的初始纵坐标
//shape的四个参数
private int type, state, x, y, nextType, nextState;
//如果刚开始游戏,由于无nextType,先给type等随机一个,下为首次开始游戏的标志
private boolean newBegin = true;
// 用数组来代表不同形状的下坠物,四维分别是类型Type、旋转状态State、横坐标X、纵坐标Y。画示意图即可得出坐标
// 方块共有7种,分别以S、Z、L、J、I、O、T这7个字母的形状来命名
private int[][][][] shape = new int[][][][]{
// S的四种翻转状态:
{ { {0,1,0,0}, {1,1,0,0}, {1,0,0,0}, {0,0,0,0} },
{ {0,0,0,0}, {1,1,0,0}, {0,1,1,0}, {0,0,0,0} },
{ {0,1,0,0}, {1,1,0,0}, {1,0,0,0}, {0,0,0,0} },
{ {0,0,0,0}, {1,1,0,0}, {0,1,1,0}, {0,0,0,0} } },
// Z:
{ { {1,0,0,0}, {1,1,0,0}, {0,1,0,0}, {0,0,0,0} },
{ {0,1,1,0}, {1,1,0,0}, {0,0,0,0}, {0,0,0,0} },
{ {1,0,0,0}, {1,1,0,0}, {0,1,0,0}, {0,0,0,0} },
{ {0,1,1,0}, {1,1,0,0}, {0,0,0,0}, {0,0,0,0} } },
// L:
{ { {0,0,0,0}, {1,1,1,0}, {0,0,1,0}, {0,0,0,0} },
{ {0,0,0,0}, {0,1,1,0}, {0,1,0,0}, {0,1,0,0} },
{ {0,0,0,0}, {0,1,0,0}, {0,1,1,1}, {0,0,0,0} },
{ {0,0,1,0}, {0,0,1,0}, {0,1,1,0}, {0,0,0,0} } },
// J:
{ { {0,0,0,0}, {0,0,1,0}, {1,1,1,0}, {0,0,0,0} },
{ {0,0,0,0}, {0,1,1,0}, {0,0,1,0}, {0,0,1,0} },
{ {0,0,0,0}, {0,1,1,1}, {0,1,0,0}, {0,0,0,0} },
{ {0,1,0,0}, {0,1,0,0}, {0,1,1,0}, {0,0,0,0} } },
// I:
{ { {0,1,0,0}, {0,1,0,0}, {0,1,0,0}, {0,1,0,0} },
{ {0,0,0,0}, {1,1,1,1}, {0,0,0,0}, {0,0,0,0} },
{ {0,1,0,0}, {0,1,0,0}, {0,1,0,0}, {0,1,0,0} },
{ {0,0,0,0}, {1,1,1,1}, {0,0,0,0}, {0,0,0,0} } },
// O:
{ { {0,0,0,0}, {0,1,1,0}, {0,1,1,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,1,1,0}, {0,1,1,0}, {0,0,0,0} },
{ {0,0,0,0}, {0,1,1,0}, {0,1,1,0}, {0,0,0,0} } },
// T:
{ { {0,1,0,0}, {1,1,0,0}, {0,1,0,0}, {0,0,0,0} },
{ {0,0,0,0}, {1,1,1,0}, {0,1,0,0}, {0,0,0,0} },
{ {0,1,0,0}, {0,1,1,0}, {0,1,0,0}, {0,0,0,0} },
{ {0,1,0,0}, {1,1,1,0}, {0,0,0,0}, {0,0,0,0} } },
};
/**
* 初始化界面
*/
private void init(){
drawArea.setPreferredSize(new Dimension(AREA_WIDTH, AREA_HEIGHT));
f.add(drawArea);
JCheckBox gridCB = new JCheckBox("显示网格",true);
JCheckBox colorCB = new JCheckBox("彩色方块", false);
gridCB.setBounds(wordX, wordY-LEN,LEN,LEN);
colorCB.setBounds(wordX, wordY-2*LEN,LEN,LEN);
// paintArea();
// 加键盘监听器
f.addKeyListener(new KeyAdapter(){
public void keyPressed(KeyEvent e){
switch (e.getKeyCode()) {
case KeyEvent.VK_UP:
turn();
break;
case KeyEvent.VK_LEFT:
left();
break;
case KeyEvent.VK_RIGHT:
right();
break;
case KeyEvent.VK_DOWN:
down();
break;
}
}
});
Timer timer = new Timer(1000, new timerListener());
newShape();
timer.start();
// 窗口显示在屏幕正中
// Toolkit是抽象类,只能用getDefaultToolkit()方法来获取实例。
// getScreenSize()方法返回的是一个Dimension对象,还须用getWidth()获取宽度
f.pack();
int screenSizeX = (int)Toolkit.getDefaultToolkit().getScreenSize().getWidth();
int screenSizeY = (int)Toolkit.getDefaultToolkit().getScreenSize().getHeight();
int fSizeX = (int)f.getSize().getWidth();
int fSizeY = (int)f.getSize().getHeight();
f.setResizable(false);// 禁止改变Frame大小
f.setBounds((screenSizeX-fSizeX)/2, (screenSizeY-fSizeY)/2, fSizeX,fSizeY );
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
/**
* 绘图
*/
private void paintArea(){
// 默认黑色,填充白色
g.setColor(Color.white);
g.fillRect(0, 0, AREA_WIDTH, AREA_HEIGHT);
// 方格线
// 先画最外围
g.setColor(Color.gray);
for (int offset = 0; offset <= 2; offset++){
g.drawRect(LEFT_MARGIN-offset, UP_MARGIN-offset, COL*LEN+offset*2, ROW*LEN+offset*2);
}
// 如果showGrid为true则显示网格
if(showGrid){
g.setColor(Color.gray);
// 9条竖线
for (int i = 1 ; i <= 9; i++){
g.drawLine(LEFT_MARGIN+LEN*i, UP_MARGIN, LEFT_MARGIN+LEN*i, UP_MARGIN+ROW*LEN);
}
// 19条横线
for(int i = 1; i <= 19; i++){
g.drawLine(LEFT_MARGIN, UP_MARGIN+LEN*i, LEFT_MARGIN+COL*LEN, UP_MARGIN+LEN*i);
}
}
// 右上角显示下一个shape
int offset2 = 3;// 边框粗细
int col = 4;// 右上角方框的列数
int row = 4;// 行数
g.setColor(Color.gray);
g.setFont(new Font("Microsoft YaHei Mono", Font.BOLD, 20));
g.drawString("下一个:", wordX, LEN*2);
int nextX = wordX;
int nextY = LEN*2;
//暂不画方框
// for (int offset = 0; offset <= 2; offset++){
// g.drawRect(nextX-offset+10, nextY+10-offset, col*LEN+offset*2, row*LEN+offset*2);
// }
//画下一次出现的下坠方块
g.setColor(isColor?color[nextType]:color[DEFAULT]);
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
if (shape[nextType][nextState][i][j]==1) {
g.fill3DRect(nextX+10+i*LEN, nextY+10+j*LEN, LEN, LEN,true);
}
}
}
g.setColor(Color.gray);
g.setFont(new Font("Times", Font.BOLD, 15));
g.drawString("玩法:", wordX, wordY+LEN*2);
g.drawString("上箭头:翻转", wordX, wordY+LEN*3);
g.drawString("左箭头:左移", wordX, wordY+LEN*4);
g.drawString("右箭头:右移", wordX, wordY+LEN*5);
g.drawString("下箭头:下落", wordX, wordY+LEN*6);
g.setFont(new Font("Times", Font.BOLD, 25));
g.drawString("得分:" + score, wordX, wordY+LEN*8);
//画下坠物shape
g.setColor(isColor?color[type]:color[DEFAULT]);
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
if (shape[type][state][i][j]==1) {
g.fill3DRect(LEFT_MARGIN+(x+i)*LEN, UP_MARGIN+(y+j)*LEN, LEN, LEN,true);
}
}
}
//画背景map
for(int i = 0; i < COL; i++){
for(int j = 0; j < ROW; j++){
if (map[i][j] == 1) {
g.setColor(mapColor[i][j]);
g.fill3DRect(LEFT_MARGIN+i*LEN, UP_MARGIN+j*LEN, LEN, LEN,true);
}
}
}
drawArea.repaint();
}
/**
* 自定义画布,重写paint()方法
*/
private class MyCanvas extends JPanel{
public void paint(Graphics g){
g.drawImage(image, 0, 0, null);
}
}
/**
* 判断位置是否合法
*/
private boolean check(int type, int state, int x, int y){
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
if ( (shape[type][state][i][j] == 1) && ( (x+i>=COL) || (x+i<0 ) || (y+j>=ROW) || (map[x+i][y+j]==1) ) ) {
return false;
}
}
}
return true;
}
/**
* 判断游戏是否结束
*/
private boolean isGameOver(int type, int state, int x, int y){
return !check(type, state, x, y);
}
/**
* 新建方块
*/
private void newShape(){
Random rand = new Random();
if(newBegin){
type = rand.nextInt(7);
state = rand.nextInt(4);
newBegin = false;
}
else{
type = nextType;
state = nextState;
}
nextType = rand.nextInt(7);
nextState = rand.nextInt(4);
x = 3;
y = 0;
// 如果游戏已结束,则重新开始
if(isGameOver(type, state, x, y)){
JOptionPane.showMessageDialog(f, "GAME OVER!");
newGame();
}
paintArea();
}
/**
* 新建游戏
*/
private void newGame(){
newMap();
score = 0;
newBegin = true;
}
/**
* 清空背景图
*/
private void newMap(){
for(int i = 0; i < COL; i++){
Arrays.fill(map[i],0);
}
}
/**
* 消行
*/
private void delLine(){
boolean flag = true;
int addScore = 0;
for(int j = 0; j < ROW; j++){
flag = true;
for( int i = 0; i < COL; i++){
if (map[i][j]==0){
flag = false;
break;
}
}
if(flag){
addScore += 10;
for(int t = j; t > 0; t--){
for(int i = 0; i =0)&&(map[x+i][y+j]==0)){
map[x+i][y+j]=shape[type][state][i][j];
mapColor[x+i][y+j]=color[isColor?type:DEFAULT];
}
}
}
}
/**
* 下面为四个方向键对应的方法
*/
private void turn(){
int tmpState = state;
state = (state + 1)%4;
if (!check(type,state, x, y )) {
state = tmpState; //不能转就什么都不做
}
paintArea();
}
private void left(){
if(check(type,state, x-1, y)){
--x;
}
paintArea();
}
private void right(){
if (check(type,state, x+1, y)) {
++x;
}
paintArea();
}
private void down(){
if (check(type,state, x, y+1)) {
++y;
}
//如果下不去则固定之
else{
add(type, state, x, y);
delLine();
newShape();
}
paintArea();
}
/**
* 主函数
*/
public static void main(String[] args){
new KingTetris().init();
}
}
版权声明:本文为博主原创文章,未经博主允许不得转载。