俄罗斯方块操作简介
左右键控制方块左右移动,下键加快方块移动的速度
空白键让方块顺时针旋转
当分数超过10的倍数,则会相应的减少相应的倍数的时间,即会加快掉落的速度
该游戏只为练练手,写的比较简单,可能还有bug,望以后再改进
源代码简介(还没有学设计模式,所以整个设计可能还不那么令人如意):
Model.java:该文件中的类Model仅存储方块的形状(将每个方块分解为一个一个的小正方形,存储在数组里面,将其旋转的图形也存储在数组里面,这样简单迅速):每个方块由四个小正方形组成。
静态数组shapeBox:每四组大括号为一组表示一个方块,每五个方块是一组,总共有四组,即第一组为方块的初始形状,第二组为第一组方块顺时针旋转后的形状,第三组为第二组顺时针旋转后的形状,依次类推。将所有的形状放在一个数组里面方便随机选择方块。
方法drawModel仅仅是绘制方块的图像,由fIndex指定绘制那个方块(fIndex由Box.java传递过来)
Box.java:该文件中的类相当于一个中转,在Model.java与RussiaBoxClient.java中中转,其主要是判断方块是否超过边界,是否碰到其他方块,并绘制方块,与Model.java沟通,还有一个就是其属性index是随机产生,传递给fIndex(Model.java中drawModel的参数)
私有方法isBottom判断是否碰到边框的底部
私有方法addItem将方块的小正方形加入到RussiaBoxClient中的pDraw中(pDraw中存储的是框架里面的所有停止的没有消掉的小正方形)
私有方法overEdge判断方块旋转时是否超过了框架的左右边框
公共方法hit判断方块是否撞到了其他方块和底部
公共方法bKeyPressed监听按键事件,在监听的时候注意判断方块是否会越界,然后再决定是否响应键盘事件
DrawPoint.java:该文件里面的类DrawPoint主要是为了存储每个小正方形的位置与颜色,其实这个类可以不用单独放在一个文件里面的,完全可以放在RussiaBoxClient.java里面
公共方法draw是为了绘制小正方形
RussiaBoxClient.java:定义了主框架,并绘图,还有利用双缓冲更新框架画面
私有方法decBox是为了消解已经形成一行的方块
私有方法ThreadMonitor:调用repaint方法,并让线程睡眠一定时间,这个睡眠时间由分数的多少决定,分数越高时间越短
私有方法keyMonitor监听键盘事件,调用Box里面的监听事件的方法
内部类cmp是为了对pDraw进行排序
整个项目还是挺简单的,但是我的大部分时间是花在调试程序上了,1)、之前运行的时候在消解一行小正方形后上面掉下来的东西总是很乱,然后用各种方法调试并检查,发现自己在pDraw.remove()时弄错了,因为pDraw.remove()后整个List变小了,如果现在在将k加一的话,则删除的不是下一个小正方形,而是下下一个小正方形,2)、改后还是有错,继续检查调试,发现数组record用的有问题,然后改成先前用的num(之前的问题还一直以为num有问题,就改成了record去记录一行方块的个数),最后调试成功,3)、还有就是remove后整个pDraw里面的元素变少了,在继续查找是否还有一行的小正方形时,不可继续在前面的基础上处理,要先减去remove的元素个数才行。。。
Model.java
Model.java
1 import java.awt.*;2
3
4 public classModel {5
6 //本来想找规律的,但是觉得找规律可能比直接列出来更麻烦,效率会更低,就枚举了,以后再更改吧。。。7 //初始的方块
8 public static final int shapeBox[][] ={9 {0,0},{Box.UNIT_WIDTH,0},{2*Box.UNIT_WIDTH,0},{Box.UNIT_WIDTH,Box.UNIT_WIDTH},10 {0,0},{Box.UNIT_WIDTH,0},{2*Box.UNIT_WIDTH,0},{0,Box.UNIT_WIDTH},11 {0,0},{Box.UNIT_WIDTH,0},{2*Box.UNIT_WIDTH,0},{2*Box.UNIT_WIDTH,Box.UNIT_WIDTH},12 {0,0},{0,Box.UNIT_WIDTH},{0,2*Box.UNIT_WIDTH},{0,3*Box.UNIT_WIDTH},13 {0,0},{Box.UNIT_WIDTH,0},{0,Box.UNIT_WIDTH},{Box.UNIT_WIDTH,Box.UNIT_WIDTH},14 {0,Box.UNIT_WIDTH},{Box.UNIT_WIDTH,0},{Box.UNIT_WIDTH,Box.UNIT_WIDTH},{Box.UNIT_WIDTH,2*Box.UNIT_WIDTH},15 {0,0},{Box.UNIT_WIDTH,0},{Box.UNIT_WIDTH,Box.UNIT_WIDTH},{Box.UNIT_WIDTH,2*Box.UNIT_WIDTH},16 {Box.UNIT_WIDTH,0},{Box.UNIT_WIDTH,Box.UNIT_WIDTH},{0,2*Box.UNIT_WIDTH},{Box.UNIT_WIDTH,2*Box.UNIT_WIDTH},17 {0,0},{Box.UNIT_WIDTH,0},{2*Box.UNIT_WIDTH,0},{3*Box.UNIT_WIDTH,0},18 {0,0},{Box.UNIT_WIDTH,0},{0,Box.UNIT_WIDTH},{Box.UNIT_WIDTH,Box.UNIT_WIDTH},19 {Box.UNIT_WIDTH,0},{0,Box.UNIT_WIDTH},{Box.UNIT_WIDTH,Box.UNIT_WIDTH},{2*Box.UNIT_WIDTH,Box.UNIT_WIDTH},20 {2*Box.UNIT_WIDTH,0},{0,Box.UNIT_WIDTH},{Box.UNIT_WIDTH,Box.UNIT_WIDTH},{2*Box.UNIT_WIDTH,Box.UNIT_WIDTH},21 {0,0},{0,Box.UNIT_WIDTH},{Box.UNIT_WIDTH,Box.UNIT_WIDTH},{2*Box.UNIT_WIDTH,Box.UNIT_WIDTH},22 {0,0},{0,Box.UNIT_WIDTH},{0,2*Box.UNIT_WIDTH},{0,3*Box.UNIT_WIDTH},23 {0,0},{Box.UNIT_WIDTH,0},{0,Box.UNIT_WIDTH},{Box.UNIT_WIDTH,Box.UNIT_WIDTH},24 {0,0},{0,Box.UNIT_WIDTH},{Box.UNIT_WIDTH,Box.UNIT_WIDTH},{0,2*Box.UNIT_WIDTH},25 {0,0},{0,Box.UNIT_WIDTH},{0,2*Box.UNIT_WIDTH},{Box.UNIT_WIDTH,2*Box.UNIT_WIDTH},26 {0,0},{Box.UNIT_WIDTH,0},{0,Box.UNIT_WIDTH},{0,2*Box.UNIT_WIDTH},27 {0,0},{Box.UNIT_WIDTH,0},{2*Box.UNIT_WIDTH,0},{3*Box.UNIT_WIDTH,0},28 {0,0},{Box.UNIT_WIDTH,0},{0,Box.UNIT_WIDTH},{Box.UNIT_WIDTH,Box.UNIT_WIDTH}29 };30 /*
31 //旋转一次的方块(顺时针)32 public static final int oShapeBox[][] = {33 {0,Box.UNIT_WIDTH},{Box.UNIT_WIDTH,0},{Box.UNIT_WIDTH,Box.UNIT_WIDTH},{Box.UNIT_WIDTH,2*Box.UNIT_WIDTH},34 {0,0},{Box.UNIT_WIDTH,0},{Box.UNIT_WIDTH,Box.UNIT_WIDTH},{Box.UNIT_WIDTH,2*Box.UNIT_WIDTH},35 {Box.UNIT_WIDTH,0},{Box.UNIT_WIDTH,Box.UNIT_WIDTH},{0,2*Box.UNIT_WIDTH},{Box.UNIT_WIDTH,2*Box.UNIT_WIDTH},36 {0,0},{Box.UNIT_WIDTH,0},{2*Box.UNIT_WIDTH,0},{3*Box.UNIT_WIDTH,0},37 {0,0},{Box.UNIT_WIDTH,0},{0,Box.UNIT_WIDTH},{Box.UNIT_WIDTH,Box.UNIT_WIDTH}38 };39
40 //旋转两次后的方块(顺时针)41 public static final int tShapeBox[][] = {42 {Box.UNIT_WIDTH,0},{0,Box.UNIT_WIDTH},{Box.UNIT_WIDTH,Box.UNIT_WIDTH},{2*Box.UNIT_WIDTH,Box.UNIT_WIDTH},43 {2*Box.UNIT_WIDTH,0},{0,Box.UNIT_WIDTH},{Box.UNIT_WIDTH,Box.UNIT_WIDTH},{2*Box.UNIT_WIDTH,Box.UNIT_WIDTH},44 {0,0},{0,Box.UNIT_WIDTH},{Box.UNIT_WIDTH,Box.UNIT_WIDTH},{2*Box.UNIT_WIDTH,Box.UNIT_WIDTH},45 {0,0},{0,Box.UNIT_WIDTH},{0,2*Box.UNIT_WIDTH},{0,3*Box.UNIT_WIDTH},46 {0,0},{Box.UNIT_WIDTH,0},{0,Box.UNIT_WIDTH},{Box.UNIT_WIDTH,Box.UNIT_WIDTH}47 };48
49 //旋转三次后的方块(顺时针)50 public static final int thShapeBox[][] = {51 {0,0},{0,Box.UNIT_WIDTH},{Box.UNIT_WIDTH,Box.UNIT_WIDTH},{0,2*Box.UNIT_WIDTH},52 {0,0},{0,Box.UNIT_WIDTH},{0,2*Box.UNIT_WIDTH},{Box.UNIT_WIDTH,2*Box.UNIT_WIDTH},53 {0,0},{Box.UNIT_WIDTH,0},{0,Box.UNIT_WIDTH},{0,2*Box.UNIT_WIDTH},54 {0,0},{Box.UNIT_WIDTH,0},{2*Box.UNIT_WIDTH,0},{3*Box.UNIT_WIDTH,0},55 {0,0},{Box.UNIT_WIDTH,0},{0,Box.UNIT_WIDTH},{Box.UNIT_WIDTH,Box.UNIT_WIDTH}56 };57 */
58
59 /*
60 * drawModel方法是为了画出方块模型61 * fIndex的范围是[0,20),其中[0,5)表示初始的方块(总共有五个方块),[5,10)表示第一次旋转后的方块。。。62 * sIndex是每个方块组的方块的编号63 * index是每个组的方块的起始编号,由于是每四个为一个方块的位置描述,所以要用sIndex*464 */
65 public void drawModel(Graphics g,int fIndex,int x,inty){66 Color c =g.getColor();67 int sIndex = fIndex % 5;68
69 if(fIndex >= 20){70 try{71 throw new Exception("wrong index,there is no this box...the surround of index is 0 - 19");72 } catch(Exception e) {73 e.printStackTrace();74 }75 }76 switch(sIndex){77 case 0:78 g.setColor(Color.BLUE);79 break;80 case 1:81 g.setColor(Color.GREEN);82 break;83 case 2:84 g.setColor(Color.MAGENTA);85 break;86 case 3:87 g.setColor(Color.ORANGE);88 break;89 case 4:90 g.setColor(Color.RED);91 break;92 }93
94 int index = fIndex * 4;95 g.fillRect(x + shapeBox[index][0], y + shapeBox[index][1], Box.UNIT_WIDTH, Box.UNIT_WIDTH);96 g.fillRect(x + shapeBox[index+1][0], y + shapeBox[index+1][1], Box.UNIT_WIDTH, Box.UNIT_WIDTH);97 g.fillRect(x + shapeBox[index+2][0], y + shapeBox[index+2][1], Box.UNIT_WIDTH, Box.UNIT_WIDTH);98 g.fillRect(x + shapeBox[index+3][0], y + shapeBox[index+3][1], Box.UNIT_WIDTH, Box.UNIT_WIDTH);99
100 g.setColor(c);101
102 }103
104 }
Box.java
Box.java
1 import java.awt.*;2 import java.awt.event.*;3 importjava.util.Random;4
5 public classBox {6 public static final int BOX_NUM = 15;7 public static final int UNIT_WIDTH = RussiaBoxClient.GAME_WIDTH /BOX_NUM;8 //public static final int UNIT_HEIGHT = RussiaBoxClient.GAME_HEIGHT / 15;
9 private intx,y;10 RussiaBoxClient rbc ;11 //Color cBox;
12 private static Random r = newRandom();13 Model boxModel = newModel();14 int index;//选择方块的形状
15 boolean edge = false;//检测方块是否碰到了其他东西16 //int eBottom[][] = new int[3][2];
17
18 publicBox(RussiaBoxClient rbC) {19 this.x = BOX_NUM / 2 *UNIT_WIDTH;20 this.y = 0;21 this.index = r.nextInt(20);22 this.rbc =rbC;23 }24
25 public voiddraw(Graphics g){26 if (!edge) {27 //y += 20;
28 y +=UNIT_WIDTH;29 }30 boxModel.drawModel(g, index, x, y);31 }32
33 //private boolean hitBox(){34 //for(int i = 0;i < BOX_NUM;i ++){35 //for(int j = 0;j < 3;j ++){36 //if(eBottom[j][0] == rbc.edgeBottom[i][0] && eBottom[j][1] == rbc.edgeBottom[i][1]){37 //edge = true;38 //rbc.boxes.add(this);39 //for(int k = j;k < 3;k ++)40 //{41 //rbc.edgeBottom[i + k - j][1] -= (rbc.edgeBottom[i + k - j][1] - y);42 //}43 //for(int k = 0;k < j;k ++){44 //rbc.edgeBottom[i + k - j][1] -= (rbc.edgeBottom[i + k - j][1] - y);45 //}46 //return true;47 //}48 //}49 //}50 //return false;51 //}52 //判断是否碰到边框的底部
53 private boolean isBottom(intsIndex){54 for(int i = 0;i < 4;i ++){55 if(Model.shapeBox[sIndex+i][1]+y >= RussiaBoxClient.GAME_HEIGHT -UNIT_WIDTH)56 return true;57 }58 return false;59 }60
61 //将方块里面的一个一个得小正方形加入到RussiaBoxClient中的pDraw中去
62 private void addItem(intsIndex){63 edge = true;64
65 int ii = index % 5;66 Color cc;67 if(ii == 0){68 cc =Color.BLUE;69 }else if(ii == 1)70 {71 cc =Color.GREEN;72 }else if(ii == 2)73 {74 cc =Color.MAGENTA;75 }else if(ii == 3)76 {77 cc =Color.ORANGE;78 }else
79 {80 cc =Color.RED;81 }82
83 rbc.pDraw.add(new DrawPoint(Model.shapeBox[sIndex][0] + x,Model.shapeBox[sIndex][1] +y,cc));84 rbc.pDraw.add(new DrawPoint(Model.shapeBox[sIndex + 1][0] + x,Model.shapeBox[sIndex+1][1] +y,cc));85 rbc.pDraw.add(new DrawPoint(Model.shapeBox[sIndex + 2][0] + x,Model.shapeBox[sIndex+2][1] +y,cc));86 rbc.pDraw.add(new DrawPoint(Model.shapeBox[sIndex + 3][0] + x,Model.shapeBox[sIndex+3][1] +y,cc));87 }88
89 //判断是否撞到边界和其他小方块
90 public booleanhit(){91 if(edge)92 return true;93
94 if(isBottom(4*index))95 {96 addItem(4*index);97 return true;98 }99
100 for(int i = 0;i < 4;i ++)101 {102 int sIndex = index * 4;103
104 //System.out.println(Model.shapeBox[sIndex+i][1]);
105
106 Rectangle r1 = new Rectangle(Model.shapeBox[sIndex + i][0] + x,Model.shapeBox[sIndex+i][1] + y +UNIT_WIDTH,UNIT_WIDTH,UNIT_WIDTH);107
108 for(int j = 0;j < rbc.pDraw.size();j ++){109 Rectangle r2 = newRectangle(rbc.pDraw.get(j).x,rbc.pDraw.get(j).y,UNIT_WIDTH,UNIT_WIDTH);110
111 if(r1.intersects(r2))112 {113 addItem(sIndex);114 return true;115 }116 }117 }118
119
120 return false;121 }122
123 //方块旋转时是否超过了框架的左右边框
124 private boolean overEdge(intsIndex){125 for(int i = 0;i < 4;i ++){126 if(Model.shapeBox[sIndex + i][0] + x >= RussiaBoxClient.GAME_WIDTH - UNIT_WIDTH || Model.shapeBox[sIndex + i][0] + x <= 0){127 return true;128 }129 }130 return false;131 }132
133 //监听按键事件
134 public voidbKeyPressed(KeyEvent e){135 int key =e.getKeyCode();136
137 if (!edge) {138 switch(key) {139 caseKeyEvent.VK_DOWN:140 if(!hit()){141 y +=UNIT_WIDTH;142 }143 break;144 caseKeyEvent.VK_RIGHT:145 if(!overEdge(4*index)){146 x +=UNIT_WIDTH;147 }148 break;149 caseKeyEvent.VK_LEFT:150 if(!overEdge(4*index)){151 x -=UNIT_WIDTH;152 }153 break;154 caseKeyEvent.VK_SPACE:155 index += 5;156 if (index >= 20)157 index %= 20;158
159 break;160 }161 }162
163 }164 }
DrawPoint.java
DrawPoint.java
1 import java.awt.*;2
3
4 public classDrawPoint {5 intx,y;6 privateColor cBox;7
8 public DrawPoint(int x, inty, Color cBox) {9 this.x =x;10 this.y =y;11 this.cBox =cBox;12 }13
14 public voiddraw(Graphics g){15 Color c =g.getColor();16 g.setColor(cBox);17
18 g.fillRect(x, y, Box.UNIT_WIDTH, Box.UNIT_WIDTH);19
20 g.setColor(c);21 }22
23 }
RussiaBoxClient.java
RussiaBoxClient.java
1 import java.awt.*;2 import java.awt.event.*;3 importjava.util.Collections;4 parator;5 importjava.util.List;6 importjava.util.ArrayList;7
8 public class RussiaBoxClient extendsFrame{9 /**
10 *11 */
12 private static final long serialVersionUID = 1L;13
14 public static final int GAME_HEIGHT = 400;15 public static final int GAME_WIDTH = 300;16
17 Image offScreen = null;18 Box box = null;19 intscores;20 inttimes;21 //public int edgeBottom[][] = new int[Box.BOX_NUM][2];22
23 //List boxes = new ArrayList();
24 List pDraw = new ArrayList();25 Cmp cmp = newCmp();26
27 public static voidmain(String[] args) {28 newRussiaBoxClient().launchFrame();29 }30
31 //private void setEdgeBottom(){32 //for(int i = 0;i < Box.BOX_NUM;i ++){33 //edgeBottom[i][0] = i * Box.UNIT_WIDTH;34 //edgeBottom[i][1] = GAME_HEIGHT;35 //}36 //}
37
38 private voidlaunchFrame(){39 this.setLocation(500, 150);40 setSize(GAME_WIDTH,GAME_HEIGHT);41 this.setBackground(Color.cyan);42
43 pDraw.clear();44 scores = 0;45 times = 700;46 //setEdgeBottom();
47
48 this.addWindowListener(newWindowAdapter(){49 public voidwindowClosing(WindowEvent e){50 System.exit(0);51 }52 });53 this.addKeyListener(newkeyMonitor());54
55 this.setResizable(false);56 this.setTitle("俄罗斯方块");57 this.setVisible(true);58
59 new Thread(newThreadMonitor()).start();60 }61
62 //绘图
63 public voidpaint(Graphics g) {64 g.drawString("your scores are: "+scores, 10, 10);65 if(box == null)66 {67 box = new Box(this);68 }69
70 for(int i = 0;i < pDraw.size();i ++){71 pDraw.get(i).draw(g);72 }73
74 if (!box.edge) {75 if(box.hit())76 {77 Collections.sort(pDraw,cmp);78
79 decBox();80 }81 box.draw(g);82 }else
83 {84 box = null;85 }86 }87
88 //利用双缓冲更新主框架界面
89 public voidupdate(Graphics g) {90 if(offScreen == null){91 offScreen = this.createImage(GAME_WIDTH,GAME_HEIGHT);92 }93 Graphics gOffScreen =offScreen.getGraphics();94 Color c =gOffScreen.getColor();95 gOffScreen.setColor(Color.cyan);96 gOffScreen.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);97 gOffScreen.setColor(c);98
99 paint(gOffScreen);100 g.drawImage(offScreen, 0, 0, null);101
102 }103
104 /*private boolean dealRecord(int i,boolean record[]){105 if(i == 1){106 for(int j = 0;j < Box.BOX_NUM;j ++){107 record[j] = false;108 }109 return true;110 }else if(i == 2){111 for(int j = 0;j < Box.BOX_NUM;j ++){112 if(!record[j])113 return false;114 }115 return true;116 }117 return true;118 }119 */
120 //消解方块
121 private voiddecBox(){122 //boolean record[] = new boolean[Box.BOX_NUM];123 //dealRecord(1,record);
124 int num = 0;125 for(int i = 0;i < pDraw.size() - 1;i ++){126 if(pDraw.get(i).y == pDraw.get(i+1).y){127 //record[pDraw.get(i).x / Box.UNIT_WIDTH] = true;128 //record[pDraw.get(i + 1).x / Box.UNIT_WIDTH] = true;
129 num ++;130 }else
131 {132 //record[pDraw.get(i).x / Box.UNIT_WIDTH] = false;133 //record[pDraw.get(i + 1).x / Box.UNIT_WIDTH] = false;
134 num = 0;135 }136 if( num == Box.BOX_NUM - 1){137 scores ++;138 /*for(int j = 0;j < pDraw.size();j ++){139 System.out.println(pDraw.get(j).x + " " + pDraw.get(j).y);140 }141 */
142 if(i + 2
145 pDraw.get(j).y +=Box.UNIT_WIDTH;146 }147 }148 for(int j = 0;j < Box.BOX_NUM;j ++){149 int k = i + 2 -Box.BOX_NUM;150 //System.out.println("delete "+k + ":" + pDraw.get(k).x +" " + pDraw.get(k).y);151 //if(k >= 0 && k < pDraw.size())
152 pDraw.remove(k);153 }154 //for(int j = 0;j < pDraw.size();j ++){155 //System.out.println("Second:");156 //System.out.println(pDraw.get(j).x + " " + pDraw.get(j).y);157 //}158 //dealRecord(1,record);
159 num = 0;160 i -=num;161 }162 }163 }164 //重绘线程
165 private class ThreadMonitor implementsRunnable{166
167 public voidrun() {168 while(true){169
170 repaint();171
172 try{173 Thread.sleep(times);174 times -= (scores/10 *50);175 } catch(InterruptedException e) {176 e.printStackTrace();177 }178 }179 }180
181 }182
183 //主框架监听键盘事件
184 private class keyMonitor extendsKeyAdapter{185
186 public voidkeyPressed(KeyEvent e) {187 box.bKeyPressed(e);188 }189
190 }191
192 //写比较器类排序pDraw
193 class Cmp implements Comparator{194 public intcompare(DrawPoint d1,DrawPoint d2){195 if(d1.y d2.x){200 return 1;201 }else if(d1.x ==d2.x){202 return 0;203 }else
204 {205 return -1;206 }207 }else
208 return -1;209 }210 }211 }