// 更新 |
237 | public void update() { |
238 | // 如果是处于运行状态 |
239 | if (mMode == RUNNING) { |
240 |
241 | long now = System.currentTimeMillis(); |
242 |
243 | // 如果当前时间距离最后一次移动的时间超过了延迟时间 |
244 | if (now - mLastMoveTime > mMoveDelay) { |
245 | // |
246 | clearTiles(); |
247 | updateWalls(); |
248 | updateSnake(); |
249 | updateApples(); |
250 | mLastMoveTime = now; |
251 | } |
252 | //会话进程睡一个延迟时间单位 |
253 | mRedrawHandler.sleep(mMoveDelay); |
254 | } |
255 |
256 | } |
257 |
258 | // 更新墙 |
259 | private void updateWalls() { |
260 | Log.i(tag, "updateWalls" ); |
261 | for ( int x = 0 ; x < mXTileCount; x++) { |
262 | // 给上边线的每个贴片位置设置一个绿色索引标识 |
263 | setTile(GREEN_STAR, x, 0 ); |
264 | // 给下边线的每个贴片位置设置一个绿色索引标识 |
265 | setTile(GREEN_STAR, x, mYTileCount - 1 ); |
266 | } |
267 | for ( int y = 1 ; y < mYTileCount - 1 ; y++) { |
268 | // 给左边线的每个贴片位置设置一个绿色索引标识 |
269 | setTile(GREEN_STAR, 0 , y); |
270 | // 给右边线的每个贴片位置设置一个绿色索引标识 |
271 | setTile(GREEN_STAR, mXTileCount - 1 , y); |
272 | } |
273 | } |
274 |
275 | // 更新苹果 |
276 | private void updateApples() { |
277 | Log.i(tag, "updateApples" ); |
278 |
279 | for (Coordinate c : mAppleList) { |
280 | setTile(YELLOW_STAR, c.x, c.y); |
281 | } |
282 | } |
283 |
284 | // 更新蛇 |
285 | private void updateSnake() { |
286 | // 生长标志 |
287 | boolean growSnake = false ; |
288 |
289 | // 得到蛇头坐标 |
290 | Coordinate head = mSnakeTrail.get( 0 ); |
291 | // 初始化一个新的蛇头坐标 |
292 | Coordinate newHead = new Coordinate( 1 , 1 ); |
293 |
294 | // 当前方向改成新的方向 |
295 | mDirection = mNextDirection; |
296 |
297 | // 根据方向确定蛇头新坐标 |
298 | switch (mDirection) { |
299 | // 如果方向向东(右),那么X加1 |
300 | case EAST: { |
301 | newHead = new Coordinate(head.x + 1 , head.y); |
302 | break ; |
303 | } |
304 | // 如果方向向西(左),那么X减1 |
305 | case WEST: { |
306 | newHead = new Coordinate(head.x - 1 , head.y); |
307 | break ; |
308 | } |
309 | // 如果方向向北(上),那么Y减1 |
310 | case NORTH: { |
311 | newHead = new Coordinate(head.x, head.y - 1 ); |
312 | break ; |
313 | } |
314 | // 如果方向向南(下),那么Y加1 |
315 | case SOUTH: { |
316 | newHead = new Coordinate(head.x, head.y + 1 ); |
317 | break ; |
318 | } |
319 | } |
320 |
321 | // 冲突检测 新蛇头是否四面墙重叠,那么游戏结束 |
322 | if ((newHead.x < 1 ) || (newHead.y < 1 ) || (newHead.x > mXTileCount - 2 ) |
323 | || (newHead.y > mYTileCount - 2 )) { |
324 | // 设置游戏状态为Lose |
325 | setMode(LOSE); |
326 | // 返回 |
327 | return ; |
328 |
329 | } |
330 |
331 | // 冲突检测 新蛇头是否和自身坐标重叠,重叠的话游戏也结束 |
332 | int snakelength = mSnakeTrail.size(); |
333 |
334 | for ( int snakeindex = 0 ; snakeindex < snakelength; snakeindex++) { |
335 | Coordinate c = mSnakeTrail.get(snakeindex); |
336 | if (c.equals(newHead)) { |
337 | // 设置游戏状态为Lose |
338 | setMode(LOSE); |
339 | // 返回 |
340 | return ; |
341 | } |
342 | } |
343 |
344 | // 看新蛇头和苹果们是否重叠 |
345 | int applecount = mAppleList.size(); |
346 | for ( int appleindex = 0 ; appleindex < applecount; appleindex++) { |
347 | Coordinate c = mAppleList.get(appleindex); |
348 | if (c.equals(newHead)) { |
349 | // 如果重叠,苹果坐标从苹果列表中移除 |
350 | mAppleList.remove(c); |
351 | // 再立刻增加一个新苹果 |
352 | addRandomApple(); |
353 | // 得分加一 |
354 | mScore++; |
355 | // 延迟是以前的90% |
356 | mMoveDelay *= 0.9 ; |
357 | // 蛇增长标志改为真 |
358 | growSnake = true ; |
359 | } |
360 | } |
361 |
362 | // 在蛇头的位置增加一个新坐标 |
363 | mSnakeTrail.add( 0 , newHead); |
364 | // 如果没有增长 |
365 | if (!growSnake) { |
366 | // 如果蛇头没增长则删去最后一个坐标,也就相当于蛇向前走了一步^_^ |
367 | mSnakeTrail.remove(mSnakeTrail.size() - 1 ); |
368 | } |
369 |
370 | int index = 0 ; |
371 | // 重新设置一下颜色,蛇头式黄色的,蛇身是红色的 |
372 | for (Coordinate c : mSnakeTrail) { |
373 | if (index == 0 ) { |
374 | setTile(YELLOW_STAR, c.x, c.y); |
375 | } else { |
376 | setTile(RED_STAR, c.x, c.y); |
377 | } |
378 | index++; |
379 | } |
380 |
381 | } |
382 |
383 | // 定义坐标内部类 |
384 | private class Coordinate { |
385 |
386 | public int x; |
387 | public int y; |
388 |
389 | // 构造函数 |
390 | public Coordinate( int newX, int newY) { |
391 | x = newX; |
392 | y = newY; |
393 | } |
394 |
395 | // 重写equals |
396 | public boolean equals(Coordinate other) { |
397 | if (x == other.x && y == other.y) { |
398 | return true ; |
399 | } |
400 | return false ; |
401 | } |
402 |
403 | // 重写toString |
404 | @Override |
405 | public String toString() { |
406 | return "Coordinate: [" + x + "," + y + "]" ; |
407 | } |
408 | } |
409 |
410 | // 添加苹果 |
411 | private void addRandomApple() { |
412 | // 新的坐标 |
413 | Coordinate newCoord = null ; |
414 | // |
415 | boolean flag = true ; |
416 | // 为真的话在循环体内一直循环 |
417 | while (flag) { |
418 | // 为苹果再找一个坐标,先随机一个X值 |
419 | int newX = 1 + RNG.nextInt(mXTileCount - 2 ); |
420 | // 再随机一个Y值 |
421 | int newY = 1 + RNG.nextInt(mYTileCount - 2 ); |
422 | // 新坐标 |
423 | newCoord = new Coordinate(newX, newY); |
424 |
425 | // 下面确保新苹果不在蛇身下,先假设没发生冲突 |
426 | boolean collision = false ; |
427 |
428 | int snakelength = mSnakeTrail.size(); |
429 | // 和蛇占据的所有坐标比较 |
430 | for ( int index = 0 ; index < snakelength; index++) { |
431 | // 只要和蛇占据的任何一个坐标相同,即认为发生冲突了 |
432 | if (mSnakeTrail.get(index).equals(newCoord)) { |
433 | collision = true ; |
434 | } |
435 | } |
436 | // 如果有冲突就继续循环,如果没冲突flag的值就是false,那么自然会退出循环,新坐标也就诞生了 |
437 | flag = collision; |
438 | } |
439 |
440 | if (newCoord == null ) { |
441 | Log.e(tag, "Somehow ended up with a null newCoord!" ); |
442 | } |
443 | // 生成一个新苹果放在苹果列表中(两个苹果可能会重合吧) |
444 | mAppleList.add(newCoord); |
445 | } |
446 |
447 | // 初始化游戏 |
448 | private void initNewGame() { |
449 |
450 | Log.e(tag, "initNewGame!" ); |
451 |
452 | // 清空ArrayList列表 |
453 | mSnakeTrail.clear(); |
454 | mAppleList.clear(); |
455 |
456 | // 创建蛇身 |
457 | mSnakeTrail.add( new Coordinate( 7 , 7 )); |
458 | mSnakeTrail.add( new Coordinate( 6 , 7 )); |
459 | mSnakeTrail.add( new Coordinate( 5 , 7 )); |
460 | mSnakeTrail.add( new Coordinate( 4 , 7 )); |
461 | mSnakeTrail.add( new Coordinate( 3 , 7 )); |
462 | mSnakeTrail.add( new Coordinate( 2 , 7 )); |
463 |
464 | // 新的方向 :北方 |
465 | mNextDirection = NORTH; |
466 |
467 | // 开始都时候有2个苹果 |
468 | addRandomApple(); |
469 | addRandomApple(); |
470 |
471 | // 设置移动延迟 |
472 | mMoveDelay = 600 ; |
473 | // 得分0 |
474 | mScore = 0 ; |
475 | } |
476 |
477 | // 保存状态 |
478 | public Bundle saveState() { |
479 |
480 | Bundle bundle = new Bundle(); |
481 |
482 | bundle.putIntArray( "mAppleList" , coordArrayListToArray(mAppleList)); |
483 | bundle.putIntArray( "mSnakeTrail" , coordArrayListToArray(mSnakeTrail)); |
484 |
485 | bundle.putInt( "mDirection" , Integer.valueOf(mDirection)); |
486 | bundle.putInt( "mNextDirection" , Integer.valueOf(mNextDirection)); |
487 |
488 | bundle.putLong( "mMoveDelay" , Long.valueOf(mMoveDelay)); |
489 | bundle.putLong( "mScore" , Long.valueOf(mScore)); |
490 |
491 | return bundle; |
492 | } |
493 |
494 | // 恢复状态 |
495 | public void restoreState(Bundle icicle) { |
496 |
497 | setMode(PAUSE); |
498 |
499 | mAppleList = coordArrayToArrayList(icicle.getIntArray( "mAppleList" )); |
500 | mDirection = icicle.getInt( "mDirection" ); |
501 | mNextDirection = icicle.getInt( "mNextDirection" ); |
502 | mMoveDelay = icicle.getLong( "mMoveDelay" ); |
503 | mScore = icicle.getLong( "mScore" ); |
504 | mSnakeTrail = coordArrayToArrayList(icicle.getIntArray( "mSnakeTrail" )); |
505 | } |
506 |
507 | // 整数数组转坐标数组 |
508 | private ArrayList coordArrayToArrayList( int [] rawArray) { |
509 | ArrayList coordArrayList = new ArrayList(); |
510 |
511 | int coordCount = rawArray.length; |
512 | for ( int index = 0 ; index < coordCount; index += 2 ) { |
513 | Coordinate c = new Coordinate(rawArray[index], rawArray[index + 1 ]); |
514 | coordArrayList.add(c); |
515 | } |
516 | return coordArrayList; |
517 | } |
518 |
519 | // 坐标数组转整数数组 |
520 | private int [] coordArrayListToArray(ArrayList cvec) { |
521 | int count = cvec.size(); |
522 | int [] rawArray = new int [count * 2 ]; |
523 | for ( int index = 0 ; index < count; index++) { |
524 | Coordinate c = cvec.get(index); |
525 | rawArray[ 2 * index] = c.x; |
526 | rawArray[ 2 * index + 1 ] = c.y; |
527 | } |
528 | return rawArray; |
529 | } |
530 |
531 | } |
6、MainActivity
01 | package android.basic.lesson48; |
02 |
03 | import android.app.Activity; |
04 | import android.os.Bundle; |
05 | import android.widget.TextView; |
06 |
07 | public class MainActivity extends Activity { |
08 |
09 | private SnakeView mSnakeView; |
10 |
11 | private static String ICICLE_KEY = "snake-view" ; |
12 |
13 | /** Called when the activity is first created. */ |
14 | @Override |
15 | public void onCreate(Bundle savedInstanceState) { |
16 |
17 | super .onCreate(savedInstanceState); |
18 | setContentView(R.layout.main); |
19 |
20 | mSnakeView = (SnakeView) findViewById(R.id.snake); |
21 | TextView tv = (TextView) findViewById(R.id.text); |
22 |
23 | mSnakeView.setStatusTextView(tv); |
24 |
25 | //检查存贮状态以确定是重新开始还是恢复状态 |
26 | if (savedInstanceState == null ) { |
27 | //存储状态为空,说明刚启动可以切换到准备状态 |
28 | mSnakeView.setMode(SnakeView.READY); |
29 | } else { |
30 | //已经保存过,那么就去恢复原有状态 |
31 | Bundle bundle = savedInstanceState.getBundle(ICICLE_KEY); |
32 | if (bundle != null ) { |
33 | //恢复状态 |
34 | mSnakeView.restoreState(bundle); |
35 | } else { |
36 | //设置状态为暂停 |
37 | mSnakeView.setMode(SnakeView.PAUSE); |
38 | } |
39 | } |
40 | } |
41 |
42 | //暂停事件被触发时 |
43 | @Override |
44 | protected void onPause() { |
45 | super .onPause(); |
46 | // Pause the game along with the activity |
47 | mSnakeView.setMode(SnakeView.PAUSE); |
48 | } |
49 |
50 | //状态保存 |
51 | @Override |
52 | public void onSaveInstanceState(Bundle outState) { |
53 | //存储游戏状态到View里 |
54 | outState.putBundle(ICICLE_KEY, mSnakeView.saveState()); |
55 | } |
56 | } |
你自己继续研究代码,本节课就到这里吧。