android 项目实训—贪吃蛇Snake(一)

一、游戏介绍: 贪吃蛇是一个古老而经典的游戏,讲的是在一个美丽的花园里,有一只爱吃苹果的小蛇,它每吃一个苹果都会变得更大更快,只是它有个致命的弱点,如果它想逃出花园或者一不小心咬到自身就会立刻死亡。作为玩家,你的目标是操纵小蛇吃掉更多的苹果而不死掉。

二、游戏截图:

a、开始画面

b、游戏暂停

c、游戏结束

三、代码总体分析

1、布局方面:我们使用一个FrameLayout绝对定位,在里面放两个和父元素一样大的子元素,一个是我们自定义的View:snake,这个元素就是游戏界面,我们通过不停的操纵和重绘该View来完成游戏交互,一个是在中间有蓝色文本的相对定位布局框架,它用来显示游戏状态。

2、类设计方面:贪吃蛇游戏使用了三个主类和两个内部类。

TitleView :一个游戏贴片(Tile)类,是我们的自定义View。是它实现游戏画面的贴片计算、贴片的种类定义、贴片的绘制等和Tile相关的方法。

SnakeView :是TileView的子类,是游戏的主体类。定义游戏状态、操作方式、游戏规则、初始化游戏、刷新视图、处理打电话导致游戏暂停时保存状态,接收焦点时恢复状态等等工作都在这个类中进行,注意它还是个View。

MainActivity :游戏窗口类,负责载入SnakeView,为SnakeView服务。

Snake.RefreshHandler : Handler类,刷新View

Snake.Coordinate : 坐标类,简化问题

四、代码阅读:

为了你阅读方便,我对原有代码做了简化、汉化和增加注释这三件事。你读起来应该会轻松许多。废话不说了,还是多给你一些时间消化代码。我提几个问题你看代码时不妨思考思考。

1、AndroidManifest.xml

1 xml version="1.0" encoding="utf-8"?>

请留意android:configChanges=”keyboardHidden|orientation”这句话,这里有一个onConfigurationChanged()方法的用法问题,你了解吗?是不是自己先研究一下?当然,你也可以等我下一讲讲解这个问题。

2、res/values/strings.xml

1 xml version="1.0" encoding="utf-8"?>
2  
3     贪吃蛇\n按“向上”键开始
4     暂停中\n按“向上”键恢复
5     游戏结束\n得分:
6     \n按“向上”键开始

我们在以往很少用Strings.xml来管理字符串资源,都是自己写死进去了,这次为什么单独写了?

3、res/layout/main.xml

1 xml version="1.0" encoding="utf-8"?>

4、TileView

view source
print ?
001 package android.basic.lesson48;
002  
003 import android.content.Context;
004 import android.graphics.Bitmap;
005 import android.graphics.Canvas;
006 import android.graphics.Paint;
007 import android.graphics.drawable.Drawable;
008 import android.util.AttributeSet;
009 import android.util.Log;
010 import android.view.View;
011  
012 public class TileView extends View {
013  
014     private static final String tag = "yao";
015  
016     // 贴片大小
017     protected static int mTileSize = 20;
018  
019     // X轴的贴片数量
020     protected static int mXTileCount;
021     // Y轴的贴片数量
022     protected static int mYTileCount;
023  
024     // X偏移量
025     private static int mXOffset;
026     // Y偏移量
027     private static int mYOffset;
028  
029     // 三种贴片图像的图像数组
030     private Bitmap[] mTileArray;
031  
032     // 存每个贴片的索引
033     private int[][] mTileGrid;
034  
035     // Paint对象(画笔、颜料)
036     private final Paint mPaint = new Paint();
037  
038     // 构造函数
039     public TileView(Context context, AttributeSet attrs) {
040         super(context, attrs);
041         Log.i(tag, "TileView Constructor");
042         Log.i(tag, "mTileSize=" + mTileSize);
043     }
044  
045     // 设置贴片图片数组
046     public void resetTiles(int tilecount) {
047         mTileArray = new Bitmap[tilecount];
048     }
049  
050     // 当该View的尺寸改变时调用,在onDraw()方法调用之前就会被调用,所以用来设置一些变量的初始值
051     @Override
052     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
053  
054         Log.i(tag, "onSizeChanged," "w=" + w + " h=" + h + " oldw=" + oldw + " oldh=" + oldh);
055  
056         // 定义X轴贴片数量
057         mXTileCount = (int) Math.floor(w / mTileSize);
058         mYTileCount = (int) Math.floor(h / mTileSize);
059         Log.i(tag, "mXTileCount=" + mXTileCount);
060         Log.i(tag, "mYTileCount=" + mYTileCount);
061  
062         // X轴偏移量
063         mXOffset = ((w - (mTileSize * mXTileCount)) / 2);
064  
065         // Y轴偏移量
066         mYOffset = ((h - (mTileSize * mYTileCount)) / 2);
067  
068         Log.i(tag, "mXOffset=" + mXOffset);
069         Log.i(tag, "mYOffset=" + mYOffset);
070  
071         // 定义贴片的二维数组
072         mTileGrid = new int[mXTileCount][mYTileCount];
073  
074         // 清空所有切片
075         clearTiles();
076     }
077  
078     // 给mTileArray这个Bitmap图片数组设置值
079     public void loadTile(int key, Drawable tile) {
080         Bitmap bitmap = Bitmap.createBitmap(mTileSize, mTileSize, Bitmap.Config.ARGB_8888);
081         Canvas canvas = new Canvas(bitmap);
082         tile.setBounds(00, mTileSize, mTileSize);
083         //把一个drawable转成一个Bitmap
084         tile.draw(canvas);
085         //在数组里存入该Bitmap
086         mTileArray[key] = bitmap;
087     }
088  
089     // 清空所有贴片
090     public void clearTiles() {
091         Log.i(tag, "TileView.clearTiles");
092         for (int x = 0; x < mXTileCount; x++) {
093             for (int y = 0; y < mYTileCount; y++) {
094                 // 全部设置为0
095                 setTile(0, x, y);
096             }
097         }
098     }
099  
100     // 给某个贴片位置设置一个状态索引
101     public void setTile(int tileindex, int x, int y) {
102         mTileGrid[x][y] = tileindex;
103     }
104  
105     // onDraw
106     @Override
107     public void onDraw(Canvas canvas) {
108  
109         Log.i(tag, "onDraw");
110         super.onDraw(canvas);
111  
112         Bitmap bmp;
113         float left;
114         float top;
115  
116         for (int x = 0; x < mXTileCount; x++) {
117             for (int y = 0; y < mYTileCount; y++) {
118                 // 当索引大于零,也就是不空时
119                 if (mTileGrid[x][y] > 0) {
120                     bmp = mTileArray[mTileGrid[x][y]];
121                     left = x * mTileSize + mXOffset;
122                     top = y * mTileSize + mYOffset;
123                     // mTileGrid中不为零时画此贴片
124                     canvas.drawBitmap(bmp, left, top, mPaint);
125                 }
126             }
127         }
128  
129     }
130 }

你可能感兴趣的:(android学习篇)