服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Android - Android实现俄罗斯方块

Android实现俄罗斯方块

2022-03-06 20:20傲子 Android

这篇文章主要为大家详细介绍了Android实现俄罗斯方块游戏 ,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

本文实例为大家分享了Android实现俄罗斯方块的具体代码,供大家参考,具体内容如下

思路:

  • 首先要画出游戏背景墙;
  • 其次,要有方块,以及方块单元;
  • 方块的不同形状,颜色随机产生;
  • 游戏的控制面板。

可能会出现的问题或者难点:

边界问题:

①处于边界的时候,方块不可以再左右移动;
②下降的时候,到达边界即底部,则不可继续下落,此时应该产生一个新的方块;

与其它方块接触问题:

①下落的时候,如果碰到其它的方块则停止下落;
②左右移动的时候,移动的过程中,如果接触到其他方快,则不可再继续左右移动;

方块的消除:

①调用方块消除方法的时间:当方块下落到底部的时候,判断是否有需要消除的行;
②消除某一行之后,应该把这一行上面的全部方块下移一行;

方块的旋转:

在当前项目中,我采用的是顺时针旋转。
①当旋转的时候,如果出现方块部分超出了边界,应该对方块进行平移,使其回到边界以内。(曾在网上看到有人做过,判断旋转之后是否会超出边界,如果会超出,则不进行旋转,我觉得不好,方块只要没有下落到底部,我觉得都可以进行旋转,除了没有空间让其旋转外);
②如果空间不足以旋转,也不可以旋转。空间不足以旋转的意思是:比如横向方向只有两个的空间,而方块旋转后会占用三个空间,此时也不可进行旋转;
③当无法继续下落或者下落到了底部也不可再进行旋转

控制面板:

①游戏开始、暂停、继续、结束,这些状态应该怎么去控制,以及游戏与控制台的事件关联。

未发现的问题:

因为本人能力,只做到这么多,如果有人发现问题,可以留言交流,欢迎挑问题。

游戏的运行界面如下所示,基本的功能以及操作很简单。

Android实现俄罗斯方块

下面直接看项目代码
项目文件结构

Android实现俄罗斯方块

下面分别介绍每个类的功能

TetrisViewAW.java游戏的主界面,背景墙以及方块都在此TetrisViewAW.Java里面,就是一个自定义的View ,(默认大家对于自定义View是熟悉的),在改类里面,有一个游戏主线程,用于控制游戏的开始,暂停,继续,停止,以及方块下落的速率。代码我加了很多注释,看不懂的可以留言。还有一点需要注意,当停止游戏时,要释放线程,养成好习惯

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
/**
 * 俄罗斯方块Game主界面
 *
 * @sign Created by wang.ao on 2017年1月12日
 */
@SuppressLint("DrawAllocation")
public class TetrisViewAW extends View {
 /** 网格开始坐标值,横纵坐标的开始值都是此值 */
 public static final int beginPoint = 10;
 /** 俄罗斯方块的最大坐标 */
 private static int max_x, max_y;
 /** 行数和列数 */
 private static int num_x = 0, num_y = 0;
 /** 背景墙画笔 */
 private static Paint paintWall = null;
 /** 俄罗斯方块的单元块画笔 */
 private static Paint paintBlock = null;
 private static final int BOUND_WIDTH_OF_WALL = 2;
 /** 当前正在下落的方块 */
 private List<BlockUnit> blockUnits = new ArrayList<BlockUnit>();
 /** 下一个要显示的方块 */
 private List<BlockUnit> blockUnitBufs = new ArrayList<BlockUnit>();
 /** 下一个要显示的方块 */
 private List<BlockUnit> routeBlockUnitBufs = new ArrayList<BlockUnit>();
 /** 全部的方块allBlockUnits */
 private List<BlockUnit> allBlockUnits = new ArrayList<BlockUnit>();
 /** 调用此对象的Activity对象 */
 private TetrisActivityAW father = null;
 private int[] map = new int[100]; // 保存每行网格中包含俄罗斯方块单元的个数
 /** 游戏的主线程 */
 private Thread mainThread = null;
 // 游戏的几种状态
 /** 标识游戏是开始还是停止 */
 private boolean gameStatus = false;
 /** 标识游戏是暂停还是运行 */
 private boolean runningStatus = false;
 /** 俄罗斯方块颜色数组 */
 private static final int color[] = { Color.parseColor("#FF6600"), Color.BLUE, Color.RED, Color.GREEN, Color.GRAY };
 /** 方块的中心方块单元的坐标, */
 private int xx, yy;
 /** 方块,用户随机获取各种形状的方块 */
 private TetrisBlock tetrisBlock;
 /** 分数 */
 private int score = 0;
 /** 当前方块的类型 */
 private int blockType = 0;
 
 public TetrisViewAW(Context context) {
 this(context, null);
 }
 
 public TetrisViewAW(Context context, AttributeSet attrs) {
 super(context, attrs);
 if (paintWall == null) {// 初始化化背景墙画笔
 paintWall = new Paint();
 paintWall.setColor(Color.LTGRAY);
 paintWall.setStyle(Paint.Style.STROKE);
 paintWall.setStrokeWidth(BOUND_WIDTH_OF_WALL + 1);
 }
 if (paintBlock == null) {// 初始化化背景墙画笔
 paintBlock = new Paint();
 paintBlock.setColor(Color.parseColor("#FF6600"));
 }
 tetrisBlock = new TetrisBlock();
 routeBlockUnitBufs = tetrisBlock.getUnits(beginPoint, beginPoint);
 Arrays.fill(map, 0); // 每行网格中包含俄罗斯方块单元的个数全部初始化为0
 // 绘制方块
 }
 
 /**
 * 设置当前游戏页面的父类activity
 *
 * @param tetrisActivityAW
 */
 public void setFather(TetrisActivityAW tetrisActivityAW) {
 father = tetrisActivityAW;
 }
 
 @Override
 protected void onDraw(Canvas canvas) {
 super.onDraw(canvas);
 max_x = getWidth();
 max_y = getHeight();
 RectF rel;
 // 绘制网格
 num_x = 0;
 num_y = 0;
 for (int i = beginPoint; i < max_x - BlockUnit.UNIT_SIZE; i += BlockUnit.UNIT_SIZE) {
 for (int j = beginPoint; j < max_y - BlockUnit.UNIT_SIZE; j += BlockUnit.UNIT_SIZE) {
 rel = new RectF(i, j, i + BlockUnit.UNIT_SIZE, j + BlockUnit.UNIT_SIZE);
 canvas.drawRoundRect(rel, 8, 8, paintWall);
 num_y++;
 }
 num_x++;
 }
 // 随机产生一个俄罗斯方块
 int len = blockUnits.size();
 // 绘制方块
 // Toast.makeText(context, "" + len, Toast.LENGTH_SHORT).show();
 for (int i = 0; i < len; i++) {
 int x = blockUnits.get(i).x;
 int y = blockUnits.get(i).y;
 // 设置当前方块的颜色
 paintBlock.setColor(color[blockUnits.get(i).color]);
 rel = new RectF(x + BOUND_WIDTH_OF_WALL, y + BOUND_WIDTH_OF_WALL,
  x + BlockUnit.UNIT_SIZE - BOUND_WIDTH_OF_WALL, y + BlockUnit.UNIT_SIZE - BOUND_WIDTH_OF_WALL);
 canvas.drawRoundRect(rel, 8, 8, paintBlock);
 }
 // 随机产生一个俄罗斯方块
 len = allBlockUnits.size();
 // 绘制方块
 // Toast.makeText(context, "" + len, Toast.LENGTH_SHORT).show();
 for (int i = 0; i < len; i++) {
 int x = allBlockUnits.get(i).x;
 int y = allBlockUnits.get(i).y;
 paintBlock.setColor(color[allBlockUnits.get(i).color]);
 rel = new RectF(x + BOUND_WIDTH_OF_WALL, y + BOUND_WIDTH_OF_WALL,
  x + BlockUnit.UNIT_SIZE - BOUND_WIDTH_OF_WALL, y + BlockUnit.UNIT_SIZE - BOUND_WIDTH_OF_WALL);
 canvas.drawRoundRect(rel, 8, 8, paintBlock);
 }
 }
 
 /**
 * 开始游戏
 */
 public void startGame() {
 gameStatus = true;
 runningStatus = true;
 if (mainThread == null || !mainThread.isAlive()) {
 getNewBlock();
 mainThread = new Thread(new MainThread());
 mainThread.start();
 }
 
 }
 
 /**
 * 暂停游戏
 */
 public void pauseGame() {
 runningStatus = false;
 }
 
 /**
 * 继续游戏
 */
 public void continueGame() {
 runningStatus = true;
 }
 
 /**
 * 停止游戏
 */
 public void stopGame() {
 // 停止游戏,释放游戏主线程
 runningStatus = false;
 gameStatus = false;
 mainThread.interrupt();
 blockUnits.clear();
 allBlockUnits.clear();
 score = 0;
 invalidate();
 }
 
 /**
 * 向左滑动
 */
 public void toLeft() {
 if (BlockUnit.toLeft(blockUnits, max_x, allBlockUnits)) {
 xx = xx - BlockUnit.UNIT_SIZE;
 }
 invalidate();
 }
 
 /**
 * 向右滑动
 */
 public void toRight() {
 if (BlockUnit.toRight(blockUnits, max_x, allBlockUnits)) {
 xx = xx + BlockUnit.UNIT_SIZE;
 }
 invalidate();
 }
 
 /**
 * 按顺时针旋转
 */
 public void route() {
 if (blockType == 3) {// 如果当前正在下落的方块为正方形,则不进行旋转
 return;
 }
 if (routeBlockUnitBufs.size() != blockUnits.size()) {
 routeBlockUnitBufs = tetrisBlock.getUnits(xx, yy);
 }
 for (int i = 0; i < blockUnits.size(); i++) {
 routeBlockUnitBufs.get(i).x = blockUnits.get(i).x;
 routeBlockUnitBufs.get(i).y = blockUnits.get(i).y;
 }
 for (BlockUnit blockUnit : routeBlockUnitBufs) {
 int tx = blockUnit.x;
 int ty = blockUnit.y;
 blockUnit.x = -(ty - yy) + xx;
 blockUnit.y = tx - xx + yy;
 }
 routeTran(routeBlockUnitBufs);
 if (!BlockUnit.canRoute(routeBlockUnitBufs, allBlockUnits)) {
 // Toast.makeText(father, "不可旋转", Toast.LENGTH_SHORT).show();
 return;
 }
 for (BlockUnit blockUnit : blockUnits) {
 int tx = blockUnit.x;
 int ty = blockUnit.y;
 blockUnit.x = -(ty - yy) + xx;
 blockUnit.y = tx - xx + yy;
 }
 routeTran(blockUnits);
 invalidate();
 }
 
 /**
 * 如果方块处于边缘,则翻转过后,会出现方块部分处于边缘之外的情况, 因此,通过递归判断是否有超出边缘的部分,
 * 如果有,则进行左右平移,把处于边缘外的方块移动到边缘内
 */
 public void routeTran(List<BlockUnit> blockUnitsBuf) {
 boolean needLeftTran = false;
 boolean needRightTran = false;
 for (BlockUnit u : blockUnitsBuf) {
 if (u.x < beginPoint) {
 needLeftTran = true;
 }
 if (u.x > max_x - BlockUnit.UNIT_SIZE) {
 needRightTran = true;
 }
 }
 if (needLeftTran || needRightTran) {
 for (BlockUnit u : blockUnitsBuf) {
 if (needLeftTran) {
  u.x = u.x + BlockUnit.UNIT_SIZE;
 } else if (needRightTran) {
  u.x = u.x - BlockUnit.UNIT_SIZE;
 }
 }
 routeTran(blockUnitsBuf);
 } else {
 return;
 }
 
 }
 
 /**
 * 获取一个新的方块
 */
 private void getNewBlock() {
 // 新的方块的坐标,x坐标位于x轴的中间,y 位于起始位置
 this.xx = beginPoint + (num_x / 2) * BlockUnit.UNIT_SIZE;
 this.yy = beginPoint;
 if (blockUnitBufs.size() == 0) {
 // 当游戏第一次开始的时候,先初始化一个方块
 blockUnitBufs = tetrisBlock.getUnits(xx, yy);
 }
 blockUnits = blockUnitBufs;
 blockType = tetrisBlock.blockType;
 blockUnitBufs = tetrisBlock.getUnits(xx, yy);
 if (father != null) {// 显示出下一个要出现的方块
 father.setNextBlockView(blockUnitBufs, (num_x / 2) * BlockUnit.UNIT_SIZE);
 }
 }
 
 /**
 * 游戏的主线程
 *
 * @sign Created by wang.ao on 2017年1月16日
 */
 private class MainThread implements Runnable {
 
 @Override
 public void run() {
 while (gameStatus) {
 while (runningStatus) {
  if (BlockUnit.canMoveToDown(blockUnits, max_y, allBlockUnits)) {
  // 判断是否可以继续下落,如果可以下落,则下落
  BlockUnit.toDown(blockUnits, max_y, allBlockUnits);
  yy = yy + BlockUnit.UNIT_SIZE;
  } else {
  /**
  * 当不可以继续下落的时候,把当前的方块添加到allBlockUnits中,
  * 并且判断是否有需要消除的方块,然后再产生一个新的方块
  */
  for (BlockUnit blockUnit : blockUnits) {
  blockUnit.y = blockUnit.y + BlockUnit.UNIT_SIZE;
  allBlockUnits.add(blockUnit);
  }
  for (BlockUnit u : blockUnits) {
  // 更新map,即更新每行网格中静止俄罗斯方块单元的个数
  int index = (int) ((u.y - beginPoint) / 50); // 计算所在行数
  map[index]++;
  }
  // 每行最大个数
  int end = (int) ((max_y - 50 - beginPoint) / BlockUnit.UNIT_SIZE);
  int full = (int) ((max_x - 50 - beginPoint) / BlockUnit.UNIT_SIZE) + 1;
  try {
  Thread.sleep(GameConfig.SPEED);
  } catch (InterruptedException e) {
  e.printStackTrace();
  }
  for (int i = 0; i <= end; i++) {
  /***
  * 消除需要消除的方块(触发条件,某一行中被塞满了方块,没有空白)
  * 注意顺序,先消除某一行,再移动这一行上边的方块
  */
  if (map[i] >= full) {
  BlockUnit.remove(allBlockUnits, i);
  score += 100;
  map[i] = 0;
  for (int j = i; j > 0; j--)
   map[j] = map[j - 1];
  map[0] = 0;
  for (BlockUnit blockUnit : allBlockUnits) {
   if (blockUnit.y < (i * BlockUnit.UNIT_SIZE + beginPoint)) {
   blockUnit.y = blockUnit.y + BlockUnit.UNIT_SIZE;
   }
  }
  }
  }
  father.runOnUiThread(new Runnable() {
  @Override
  public void run() {
  /**
   * 刷新分数
   */
  father.score.setText("" + score);
  invalidate();
  }
  });
  try {
  Thread.sleep(GameConfig.SPEED * 3);
  } catch (InterruptedException e) {
  e.printStackTrace();
  }
  father.runOnUiThread(new Runnable() {
  @Override
  public void run() {
  getNewBlock();
  score += 10;
  father.score.setText("" + score);
  }
  });
  }
  father.runOnUiThread(new Runnable() {
  @Override
  public void run() {
  invalidate();
  }
  });
  try {
  Thread.sleep(GameConfig.SPEED);
  } catch (InterruptedException e) {
  e.printStackTrace();
  }
 
 }
 }
 
 }
 
 }
 
}

BlockUnit.java方块的单元块,大家都玩过俄罗斯方块,每一个方块由四个单元块组成。单元快应该有以下属性:①大小:单元块的大小决定了主界面的容量(容纳单元块的数量);②颜色:每个单元块都有一个颜色,美化游戏界面(可无);③坐标:包括X轴坐标、Y轴坐标,在绘制方块的时候,以单元块的坐标为起点绘制,即:单元块的坐标值应该为单元块在界面上的左上角的坐标。

此类的主要功能有:方块的下落,左右移动,判断是否可以旋转等功能都在此类中,算是核心类。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
/**
 * 俄罗斯方块的单元快
 *
 * @sign Created by wang.ao on 2017年1月13日
 */
public class BlockUnit {
 public static final int UNIT_SIZE = 50;
 public static final int BEGIN = 10;
 public int color;
 // 单元块 的坐标
 public int x, y;
 
 public BlockUnit() {
 }
 
 public BlockUnit(int x, int y, int color) {
 /*
 * @param 单元块横纵坐标 构造函数
 */
 this.x = x;
 this.y = y;
 this.color = color;
 }
 
 /**
 * 判断方块是否可以向左移动,1是否在边缘,2是否会与其他方块重合
 * @param blockUnits 当前正在下落的方块
 * @param max_x 游戏主界面X轴的最大值 ,下同
 * @param allBlockUnits 所有的方块
 * @return 能移动true;不能移动false
 */
 public static boolean canMoveToLeft(List<BlockUnit> blockUnits, int max_x, List<BlockUnit> allBlockUnits) {
 for (BlockUnit blockUnit : blockUnits) {
 int x = blockUnit.x;
 if (x - UNIT_SIZE < BEGIN) {
 return false;
 }
 int y = blockUnit.y;
 if (isSameUnit(x - UNIT_SIZE, y, allBlockUnits)) {
 return false;
 }
 }
 return true;
 }
 
 /**
 * 判断方块是否可以向右移动,1是否在边缘,2是否会与其他方块重合
 * @param blockUnits 当前正在下落的方块
 * @param max_x 游戏主界面X轴的最大值 ,下同
 * @param allBlockUnits 所有的方块
 * @return 能移动true;不能移动false
 */
 public static boolean canMoveToRight(List<BlockUnit> blockUnits, int max_x, List<BlockUnit> allBlockUnits) {
 for (BlockUnit blockUnit : blockUnits) {
 int x = blockUnit.x;
 if (x + UNIT_SIZE > max_x - UNIT_SIZE) {
 return false;
 }
 int y = blockUnit.y;
 if (isSameUnit(x + UNIT_SIZE, y, allBlockUnits)) {
 return false;
 }
 }
 return true;
 }
 /**
 * 判断方块是否可以向下移动,1是否在边缘,2是否会与其他方块重合
 * @param blockUnits 当前正在下落的方块
 * @param max_x 游戏主界面X轴的最大值 ,下同
 * @param allBlockUnits 所有的方块
 * @return 能移动true;不能移动false
 */
 public static boolean canMoveToDown(List<BlockUnit> blockUnits, int max_y, List<BlockUnit> allBlockUnits) {
 for (BlockUnit blockUnit : blockUnits) {
 int x = blockUnit.x;
 int y = blockUnit.y + UNIT_SIZE * 2;
 if (y > max_y - UNIT_SIZE) {
 return false;
 }
 if (isSameUnit(x, y, allBlockUnits)) {
 return false;
 }
 }
 return true;
 }
 public static boolean canRoute(List<BlockUnit> blockUnits, List<BlockUnit> allBlockUnits){
 for (BlockUnit blockUnit: blockUnits) {
 if(isSameUnit(blockUnit.x, blockUnit.y, allBlockUnits)){
 return false;
 }
 }
 return true;
 }
 
 /**
 * 把当前方块向左移动一格
 * @param blockUnits
 * @param max_x
 * @param allBlockUnits
 * @return 是否成功移动一格,是:true,否:false
 */
 public static boolean toLeft(List<BlockUnit> blockUnits, int max_x, List<BlockUnit> allBlockUnits) {
 if (canMoveToLeft(blockUnits, max_x, allBlockUnits)) {
 for (BlockUnit blockUnit : blockUnits) {
 blockUnit.x = blockUnit.x - UNIT_SIZE;
 }
 return true;
 }
 return false;
 }
 /**
 * 把当前方块向右移动一格
 * @param blockUnits
 * @param max_x
 * @param allBlockUnits
 * @return 是否成功移动一格,是:true,否:false
 */
 public static boolean toRight(List<BlockUnit> blockUnits, int max_x, List<BlockUnit> allBlockUnits) {
 if (canMoveToRight(blockUnits, max_x, allBlockUnits)) {
 for (BlockUnit blockUnit : blockUnits) {
 blockUnit.x = blockUnit.x + UNIT_SIZE;
 }
 return true;
 }
 return false;
 }
 
 /**
 * 把当前方块下落一格
 * @param blockUnits
 * @param allBlockUnits
 * @return 是否成功移动一格,是:true,否:false
 */
 public static void toDown(List<BlockUnit> blockUnits, int max_Y, List<BlockUnit> allBlockUnits) {
 for (BlockUnit blockUnit : blockUnits) {
 blockUnit.y = blockUnit.y + BlockUnit.UNIT_SIZE;
 }
 }
 
 /**
 * 判断 方块单元是否和所有方块有重合
 * @param x
 * @param y
 * @param allBlockUnits
 * @return
 */
 public static boolean isSameUnit(int x, int y, List<BlockUnit> allBlockUnits) {
 for (BlockUnit blockUnit : allBlockUnits) {
 if (Math.abs(x - blockUnit.x) < UNIT_SIZE && Math.abs(y - blockUnit.y) < UNIT_SIZE) {
 return true;
 }
 }
 return false;
 }
 
 /**
 * 删除在第j行上的方块单元
 * @param allBlockUnits
 * @param j 需删除行标
 */
 public static void remove(List<BlockUnit> allBlockUnits, int j) {
 for (int i = allBlockUnits.size() - 1; i >= 0; i--) {
 /*
 * ①逆向遍历 ②根据y坐标计算单元所在行,若为j行则从units中删除
 */
 if ((int) ((allBlockUnits.get(i).y - BEGIN) / 50) == j)
 allBlockUnits.remove(i);
 }
 }
}

TetrisBlock.java用于产生不同形状的方块,共有其中类型。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
/**
 * 方块
 *
 * @sign Created by wang.ao on 2017年1月13日
 */
public class TetrisBlock {
 private static final int TYPE_SUM = 7;
 public int blockType, blockDirection; // 方块种类,方块朝向
 
 private int color; // 方块颜色
 
 private int x, y; // 方块坐标
 
 public TetrisBlock() {
 
 }
 
 public TetrisBlock(int x, int y) {
 this.x = x;
 this.y = y;
 }
 
 public List<BlockUnit> getUnits(int x, int y) {
 this.x = x;
 this.y = y;
 return returnUnit();
 }
 
 /**
 * 随机产生一种方块
 * @return
 */
 public List<BlockUnit> returnUnit() {
 List<BlockUnit> units = new ArrayList<BlockUnit>(); // 方块组成部分
 blockType = (int) (Math.random() * TYPE_SUM) + 1; // 随机生成一个种类
 blockDirection = 1; // 默认初始方向
 color = (int) (Math.random() * 4) + 1; // 随机生成一个颜色
 units.clear();
 switch (blockType) {
 case 1:// 横线
 for (int i = 0; i < 4; i++) {
 units.add(new BlockUnit(x + (-2 + i) * BlockUnit.UNIT_SIZE, y, color));
 }
 break;
 case 2:
 units.add(new BlockUnit(x + (-1 + 1) * BlockUnit.UNIT_SIZE, y - BlockUnit.UNIT_SIZE, color));
 for (int i = 0; i < 3; i++) {
 units.add(new BlockUnit(x + (-1 + i) * BlockUnit.UNIT_SIZE, y, color));
 }
 break;
 case 3:
 for (int i = 0; i < 2; i++) {
 units.add(new BlockUnit(x + (i - 1) * BlockUnit.UNIT_SIZE, y - BlockUnit.UNIT_SIZE, color));
 units.add(new BlockUnit(x + (i - 1) * BlockUnit.UNIT_SIZE, y, color));
 }
 break;
 case 4:
 units.add(new BlockUnit(x + (-1 + 0) * BlockUnit.UNIT_SIZE, y - BlockUnit.UNIT_SIZE, color));
 for (int i = 0; i < 3; i++) {
 units.add(new BlockUnit(x + (-1 + i) * BlockUnit.UNIT_SIZE, y, color));
 }
 break;
 case 5:
 units.add(new BlockUnit(x + (-1 + 2) * BlockUnit.UNIT_SIZE, y - BlockUnit.UNIT_SIZE, color));
 for (int i = 0; i < 3; i++) {
 units.add(new BlockUnit(x + (-1 + i) * BlockUnit.UNIT_SIZE, y, color));
 }
 break;
 case 6:
 for (int i = 0; i < 2; i++) {
 units.add(new BlockUnit(x + (-1 + i) * BlockUnit.UNIT_SIZE, y - BlockUnit.UNIT_SIZE, color));
 units.add(new BlockUnit(x + i * BlockUnit.UNIT_SIZE, y, color));
 }
 break;
 case 7:
 for (int i = 0; i < 2; i++) {
 units.add(new BlockUnit(x + i * BlockUnit.UNIT_SIZE, y - BlockUnit.UNIT_SIZE, color));
 units.add(new BlockUnit(x + (-1 + i) * BlockUnit.UNIT_SIZE, y, color));
 }
 break;
 }
 return units;
 }

NextBlockView.java其实就是游戏主界面的一个缩减版,用于显示下一个要出现的方块的,玩家可以明确的知道下一个方块的形状和颜色。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/**
 * 下一个要展示的方块
 *
 * @sign Created by wang.ao on 2017年1月13日
 */
@SuppressLint("DrawAllocation")
public class NextBlockView extends View {
 /** 网格开始坐标值,横纵坐标的开始值都是此值 */
 public static final int beginPoint = 10;
 /** 俄罗斯方块的最大坐标 */
 private static int max_x, max_y;
 private List<BlockUnit> blockUnits = new ArrayList<BlockUnit>();
 /** 背景墙画笔 */
 private static Paint paintWall = null;
 private static final int BOUND_WIDTH_OF_WALL = 2;
 private static Paint paintBlock = null;
 private int div_x = 0;
 // 俄罗斯方块颜色数组
 private static final int color[] ={ Color.parseColor("#FF6600"), Color.BLUE, Color.RED, Color.GREEN, Color.GRAY };
 
 public NextBlockView(Context context) {
 this(context, null);
 }
 
 public NextBlockView(Context context, AttributeSet attrs) {
 super(context, attrs);
 if (paintWall == null) {// 初始化化背景墙画笔
 paintWall = new Paint();
 paintWall.setColor(Color.LTGRAY);
 paintWall.setStyle(Paint.Style.STROKE);
 paintWall.setStrokeWidth(BOUND_WIDTH_OF_WALL + 1);
 }
 if (paintBlock == null) {// 初始化化背景墙画笔
 paintBlock = new Paint();
 paintBlock.setColor(Color.parseColor("#FF6600"));
 }
 }
 
 public void setBlockUnits(List<BlockUnit> blockUnits, int div_x) {
 this.blockUnits = blockUnits;
 this.div_x = div_x;
 invalidate();
 }
 
 @Override
 protected void onDraw(Canvas canvas) {
 super.onDraw(canvas);
 max_x = getWidth();
 max_y = getHeight();
 RectF rel;
 // 绘制网格
 int len = blockUnits.size();
 // 绘制方块
 // Toast.makeText(context, "" + len, Toast.LENGTH_SHORT).show();
 for (int i = 0; i < len; i++) {
 paintBlock.setColor(color[blockUnits.get(i).color]);
 int x = blockUnits.get(i).x - div_x + BlockUnit.UNIT_SIZE * 2;
 int y = blockUnits.get(i).y + BlockUnit.UNIT_SIZE * 2;
 rel = new RectF(x + BOUND_WIDTH_OF_WALL, y + BOUND_WIDTH_OF_WALL,
  x + BlockUnit.UNIT_SIZE - BOUND_WIDTH_OF_WALL, y + BlockUnit.UNIT_SIZE - BOUND_WIDTH_OF_WALL);
 canvas.drawRoundRect(rel, 8, 8, paintBlock);
 rel = new RectF(x, y, x + BlockUnit.UNIT_SIZE, y + BlockUnit.UNIT_SIZE);
 canvas.drawRoundRect(rel, 8, 8, paintWall);
 }
 }
 
}

GameConfig.java用于配置方块的下落速度

?
1
2
3
4
public class GameConfig {
 /**方块下落的速度*/
 public static final int SPEED = 300;
}

TetrisActivityAW.java主界面,包括游戏主界面和控制台,很简单,直接贴代码。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
public class TetrisActivityAW extends Activity {
 private NextBlockView nextBlockView;
 private TetrisViewAW tetrisViewAW;
 private TextView gameStatusTip;
 public TextView score;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_tetris_activity_aw);
 nextBlockView = (NextBlockView) findViewById(R.id.nextBlockView1);
 tetrisViewAW = (TetrisViewAW) findViewById(R.id.tetrisViewAW1);
 tetrisViewAW.setFather(this);
 gameStatusTip = (TextView) findViewById(R.id.game_staus_tip);
 score = (TextView) findViewById(R.id.score);
 }
 
 public void setNextBlockView(List<BlockUnit> blockUnits, int div_x) {
 nextBlockView.setBlockUnits(blockUnits, div_x);
 }
 
 /**
 * 开始游戏
 *
 * @param view
 */
 public void startGame(View view) {
 tetrisViewAW.startGame();
 gameStatusTip.setText("游戏运行中");
 }
 
 /**
 * 暂停游戏
 */
 public void pauseGame(View view) {
 tetrisViewAW.pauseGame();
 gameStatusTip.setText("游戏已暂停");
 }
 
 /**
 * 继续游戏
 */
 public void continueGame(View view) {
 tetrisViewAW.continueGame();
 gameStatusTip.setText("游戏运行中");
 }
 
 /**
 * 停止游戏
 */
 public void stopGame(View view) {
 tetrisViewAW.stopGame();
 score.setText(""+0);
 gameStatusTip.setText("游戏已停止");
 }
 
 /**
 * 向左滑动
 */
 public void toLeft(View view) {
 tetrisViewAW.toLeft();
 }
 
 /**
 * 向右滑动
 */
 public void toRight(View view) {
 tetrisViewAW.toRight();
 }
 /**
 * 向右滑动
 */
 public void toRoute(View view) {
 tetrisViewAW.route();
 }
}

TetrisActivityAW activity的xml文件

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 tools:context="${relativePackage}.${activityClass}" >
 
 <com.awang.media.minetetris.TetrisViewAW
 android:id="@+id/tetrisViewAW1"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:layout_marginBottom="200dp"
 android:layout_marginRight="120dp" />
 
 <com.awang.media.minetetris.NextBlockView
 android:id="@+id/nextBlockView1"
 android:layout_width="120dp"
 android:layout_height="120dp"
 android:layout_alignParentRight="true" />
 
 <LinearLayout
 android:layout_width="110dp"
 android:layout_height="wrap_content"
 android:layout_alignParentRight="true"
 android:layout_below="@+id/nextBlockView1"
 android:layout_marginTop="20dp"
 android:orientation="vertical" >
 
 <LinearLayout
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:orientation="horizontal" >
 
 <TextView
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="分数" />
 
 <TextView
 android:id="@+id/score"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_marginLeft="10dp"
 android:text="1" />
 </LinearLayout>
 
 <LinearLayout
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:layout_marginTop="20dp"
 android:orientation="horizontal" >
 
 <TextView
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="等级" />
 
 <TextView
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_marginLeft="10dp"
 android:text="1" />
 </LinearLayout>
 
 <LinearLayout
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:layout_marginTop="20dp"
 android:orientation="horizontal" >
 
 <TextView
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="速度" />
 
 <TextView
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_marginLeft="10dp"
 android:text="0" />
 </LinearLayout>
 
 <LinearLayout
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:layout_marginTop="20dp"
 android:orientation="horizontal" >
 
 <TextView
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="最高分" />
 
 <TextView
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_marginLeft="10dp"
 android:text="0" />
 </LinearLayout>
 
 <Button
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_marginTop="10dp"
 android:onClick="startGame"
 android:text="开始" />
 
 <Button
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:onClick="pauseGame"
 android:text="暂停" />
 
 <Button
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:onClick="continueGame"
 android:text="继续" />
 
 <Button
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:onClick="stopGame"
 android:text="结束" />
 </LinearLayout>
 
 <RelativeLayout
 android:layout_width="match_parent"
 android:layout_height="200dp"
 android:layout_alignParentBottom="true" >
 
 <Button
 android:layout_width="100dp"
 android:layout_height="100dp"
 android:onClick="toLeft"
 android:text="左" />
 
 <TextView
 android:id="@+id/game_staus_tip"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_centerHorizontal="true"
 android:layout_marginTop="5dp"
 android:text="点击开始运行游戏"
 android:textSize="20sp" />
 <Button
 android:layout_width="100dp"
 android:layout_height="100dp"
 android:layout_centerInParent="true"
 android:onClick="toRoute"
 android:text="旋转" />
 <Button
 android:layout_width="100dp"
 android:layout_height="100dp"
 android:layout_alignParentRight="true"
 android:onClick="toRight"
 android:text="右" />
 </RelativeLayout>
 
</RelativeLayout>

整个项目就是这些,代码已经全部贴出来了。

整个项目写的时候,以为很简单,但是却遇到了很多问题,不过都已解决。欢迎来找bug,大家共同进步。

源码下载地址:Android 俄罗斯方块与贪吃蛇源码下载

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://blog.csdn.net/waa_0618/article/details/54581457

延伸 · 阅读

精彩推荐