贪吃蛇游戏分析.docx
《贪吃蛇游戏分析.docx》由会员分享,可在线阅读,更多相关《贪吃蛇游戏分析.docx(23页珍藏版)》请在冰豆网上搜索。
贪吃蛇游戏分析
第6章Android游戏入门
(一)
第一部分本次上机任务
贪吃蛇游戏实现
第二部分任务实现
贪吃蛇游戏是一款经典的老游戏,很多游戏教程都会用该游戏作为游戏入门的示例。
今天我们也采取该游戏作为游戏入门示例。
在理论课上已经对于如何使用View组件进行游戏开发有了初步了解,下面通过View组件实现完成的贪吃蛇游戏。
掌握要点:
掌握将View组件作为游戏开发组件的基本流程
掌握当手机屏幕发生改变时数据的保存
任务说明:
1、游戏的基本流程。
游戏加载时界面如图:
当点击模拟器上的向上的方向键,开始游戏,界面如下:
此时点击上下左右方向键,可以控制“蛇”按照特定的方式移动,每吃一个食物,蛇身会长一格,同时在背景内会再次提供一个新的食物,当蛇头碰到“墙壁”时,游戏结束。
2、游戏的场景实现
通过上面的截图可以看出,整个游戏的场景有红黄绿三种颜色的小图片构成,故在此编写TileView,该类作为整个场景的基础类,代码如下:
publicclassTileViewextendsView{
privatestaticfinalStringTAG="TileView";
//每个方格的大小
protectedstaticintmTileSize;
//横向的方格数
protectedstaticintmXTileCount;
//纵向的方格数
protectedstaticintmYTileCount;
//xy坐标系的偏移量
privatestaticintmXOffset;
privatestaticintmYOffset;
//图片
privateBitmap[]mTitleArray;
//二维方格地图
privateint[][]mTileGrid;
//画笔
privatefinalPaintmPaint=newPaint();
publicTileView(Contextcontext,AttributeSetattrs,intdefStyle){
super(context,attrs,defStyle);
//TODOAuto-generatedconstructorstub
TypedArraya=context.obtainStyledAttributes(attrs,R.styleable.TileView);
mTileSize=a.getInteger(R.styleable.TileView_tileSize,12);
Log.v(TAG,String.valueOf(mTileSize));
a.recycle();
}
//实际调用的是该构造方法
publicTileView(Contextcontext,AttributeSetattrs){
super(context,attrs);
TypedArraya=context.obtainStyledAttributes(attrs,R.styleable.TileView);
mTileSize=a.getInt(R.styleable.TileView_tileSize,12);
a.recycle();
}
//设置图片数组的大小
publicvoidresetTiles(inttilecount){
mTitleArray=newBitmap[tilecount];
Log.v(TAG,String.valueOf(mTitleArray.length));
}
//在游戏还未正式开始前,
//首先要做一些初始化工作,
//在View第一次加载时会首先调用onSizeChanged,
//这里就是做这些事的最好时机
@Override
protectedvoidonSizeChanged(intw,inth,intoldw,intoldh){
//TODOAuto-generatedmethodstub
super.onSizeChanged(w,h,oldw,oldh);Log.v(TAG,"w"+String.valueOf(w)+"h"+String.valueOf(h)+"oldw"+String.valueOf(oldw)+"oldh"+String.valueOf(oldh));
Log.v(TAG,String.valueOf(mTileSize));
mXTileCount=(int)Math.floor(w/mTileSize);
Log.v(TAG,"mXTileCount"+String.valueOf(mXTileCount));
mYTileCount=(int)Math.floor(h/mTileSize);
Log.v(TAG,"mYTileCount"+String.valueOf(mYTileCount));
mXOffset=((w-(mTileSize*mXTileCount))/2);
mYOffset=((h-(mTileSize*mYTileCount))/2);
Log.v(TAG,"mXOffset"+String.valueOf(mXOffset));
Log.v(TAG,"mYOffset"+String.valueOf(mYOffset));
mTileGrid=newint[mXTileCount][mYTileCount];
clearTiles();
}
//加载图片
publicvoidloadTile(intkey,Drawabletile){
Bitmapbitmap=Bitmap.createBitmap(mTileSize,mTileSize,Bitmap.Config.ARGB_8888);
Canvascanvas=newCanvas(bitmap);
tile.setBounds(0,0,mTileSize,mTileSize);
tile.draw(canvas);
mTitleArray[key]=bitmap;
}
publicvoidclearTiles(){
Log.v(TAG,"clearTiles");
for(intx=0;xfor(inty=0;ysetTile(0,x,y);
}
}
}
publicvoidsetTile(inttileindex,intx,inty){
mTileGrid[x][y]=tileindex;
}
//第一次调用完onSizeChanged后,会紧跟着第一次来调用onDraw来绘制View自身
//当然,此时由于所有方格的状态都是0,所以它在屏幕上等于什么也不会去绘制。
@Override
protectedvoidonDraw(Canvascanvas){
//TODOAuto-generatedmethodstub
Log.v(TAG,"onDrawiscalled");
super.onDraw(canvas);
for(intx=0;xfor(inty=0;yif(mTileGrid[x][y]>0){
canvas.drawBitmap(mTitleArray[mTileGrid[x][y]],mXOffset+x*mTileSize,mYOffset+y*mTileSize,mPaint);
}
}
}
}
}
在该类中loadTile()方法进行图片的加载,resetTiles()方法设置图片数组的大小,onSizeChanged()方法进行游戏开始前相关元素初始化工作,onDraw()方法在onSizeChanged()方法调用后被调用,完成图形的绘制工作。
3、游戏主类SnakeView的实现。
该类继承TileView,在该类中完成游戏图形渲染以及游戏碰撞检测等主要功能。
首先在该类中进行状态设置常量代码编写:
publicclassSnakeViewextendsTileView{
privatestaticfinalStringTAG="SnakeView";
//游戏状态
publicstaticfinalintPAUSE=0;
publicstaticfinalintREADY=1;
publicstaticfinalintRUNNING=2;
publicstaticfinalintLOSE=3;
privateintmMode=READY;
//“蛇”运动方向
privatestaticfinalintNORTH=1;
privatestaticfinalintSOUTH=2;
privatestaticfinalintEAST=3;
privatestaticfinalintWEST=4;
privateintmDirection=NORTH;
privateintmNextDirection=NORTH;
//不同颜色图片
privatestaticfinalintRED_STAR=1;
privatestaticfinalintYELLOW_STAR=2;
privatestaticfinalintGREEN_STAR=3;
//分数
privatelongmScore=0;
//延迟时间
privatelongmMoveDelay=600;
//最后移动的位置
privatelongmLastMove;
……
}
第二步,编写initSnakeView()方法,负责游戏开始时初始化游戏场景。
代码如下:
privatevoidinitSnakeView(){
setFocusable(true);
Resourcesr=this.getContext().getResources();
resetTiles(4);
loadTile(RED_STAR,r.getDrawable(R.drawable.redstar));
loadTile(YELLOW_STAR,r.getDrawable(R.drawable.yellowstar));
loadTile(GREEN_STAR,r.getDrawable(R.drawable.greenstar));
}
第三步编写内部类Coordinate,该类主要的功用是负责图片移动坐标的维护。
代码如下:
privateclassCoordinate{
publicintx;
publicinty;
publicCoordinate(intnewX,intnewY){
x=newX;
y=newY;
}
publicbooleanequals(Coordinateother){
if(x==other.x&&y==other.y){
returntrue;
}
returnfalse;
}
@Override
publicStringtoString(){
//TODOAuto-generatedmethodstub
return"Coordinate:
["+x+","+y+"]";
}
}
第四步编写内部Handler负责UI的更新。
代码如下:
classRefreshHandlerextendsHandler{
@Override
publicvoidhandleMessage(Messagemsg){
//TODOAuto-generatedmethodstub
Log.v(TAG,"refreshHandleriscalled!
!
");
super.handleMessage(msg);
SnakeView.this.update();
SnakeView.this.invalidate();
}
//时间越来越短,刷新越来越快。
publicvoidsleep(longdelayMillis){
this.removeMessages(0);
Log.v(TAG,"handlersleepiscalled"+delayMillis);
sendMessageDelayed(obtainMessage(0),delayMillis);
}
}
第五步构建添加食物方法,代码如下:
privatevoidaddRandomApple(){
//Log.v(TAG,"addRandomAppleiscalled");
CoordinatenewCoord=null;
booleanfound=false;
while(!
found){
intnewX=1+RNG.nextInt(mXTileCount-2);
intnewY=1+RNG.nextInt(mYTileCount-2);
newCoord=newCoordinate(newX,newY);
booleancollision=false;
intsnakelength=mSnakeTrail.size();
for(intindex=0;indexif(mSnakeTrail.get(index).equals(newCoord)){
collision=true;
}
}
found=!
collision;
}
if(newCoord==null){
Log.e(TAG,"SomehowendedupwithanullnewCoord!
");
}
mAppleList.add(newCoord);
}
当表示蛇的集合存在当前食物的坐标,表示发生了碰撞了。
代码如下:
classRefreshHandlerextendsHandler{
@Override
publicvoidhandleMessage(Messagemsg){
//TODOAuto-generatedmethodstub
Log.v(TAG,"refreshHandleriscalled!
!
");
super.handleMessage(msg);
SnakeView.this.update();
SnakeView.this.invalidate();
}
//时间越来越短,刷新越来越快。
publicvoidsleep(longdelayMillis){
this.removeMessages(0);
Log.v(TAG,"handlersleepiscalled"+delayMillis);
sendMessageDelayed(obtainMessage(0),delayMillis);
}
}
第七步编写initNewGame方法,程序第一次加载显示的游戏场景。
代码如下:
privatevoidinitNewGame(){
mSnakeTrail.clear();
mAppleList.clear();
mSnakeTrail.add(newCoordinate(7,7));
mSnakeTrail.add(newCoordinate(6,7));
mSnakeTrail.add(newCoordinate(5,7));
mSnakeTrail.add(newCoordinate(4,7));
mSnakeTrail.add(newCoordinate(3,7));
mSnakeTrail.add(newCoordinate(2,7));
mNextDirection=NORTH;
//Twoapplestostartwith
addRandomApple();
addRandomApple();
mMoveDelay=600;
mScore=0;
}
该方法清空“蛇”的图片和食物图片,而后六章图片组成一个初始的蛇,而且蛇最开始向北移动,而后随机的添加两个食物。
第八步编写oordArrayListToArray和coordArrayToArrayList将数组和集合中进行转换的方法。
代码如下:
privateArrayListcoordArrayToArrayList(int[]rawArray){
Log.v(TAG,"coordArrayToArrayListiscalled!
");
ArrayListcoordArrayList=newArrayList();
intcoordCount=rawArray.length;
for(intindex=0;indexCoordinatec=newCoordinate(rawArray[index],rawArray[index+1]);
coordArrayList.add(c);
}
returncoordArrayList;
}
privateint[]coordArrayListToArray(ArrayListcvec){
Log.v(TAG,"coordArrayListToArray!
");
intcount=cvec.size();
int[]rawArray=newint[count*2];
for(intindex=0;indexCoordinatec=cvec.get(index);
rawArray[2*index]=c.x;
rawArray[2*index+1]=c.y;
}
returnrawArray;
}
通过这两个方法将整型数组和集合框架进行方便的转换。
第九步编写食物被吃掉后,更新食物方法。
代码如下:
privatevoidupdateApples(){
for(Coordinatec:
mAppleList){
setTile(YELLOW_STAR,c.x,c.y);
}
}
第十步编写更新外围“墙”的方法,代码如下:
privatevoidupdateWalls(){
for(intx=0;xsetTile(GREEN_STAR,x,0);
setTile(GREEN_STAR,x,mYTileCount-1);
}
for(inty=1;ysetTile(GREEN_STAR,0,y);
setTile(GREEN_STAR,mXTileCount-1,y);
}
}
从上述代码中可以看出墙四面铺满了绿色的图片。
第十一步编写更新蛇的方法,代码如下:
privatevoidupdateSnake(){
//TODOAuto-generatedmethodstub
//Log.v(TAG,"---------------updateSnakeiscalled!
!
");
booleangrowSnake=false;
//grabthesnakebythehead
Coordinatehead=mSnakeTrail.get(0);
CoordinatenewHead=newCoordinate(1,1);
mDirection=mNextDirection;
Log.v(TAG,"updateSnakemDirection="+mDirection);
//判断用户输入的方向
//右横坐标加1
switch(mDirection){
caseEAST:
{
newHead=newCoordinate(head.x+1,head.y);
break;}
//左横坐标减1
caseWEST:
{
newHead=newCoordinate(head.x-1,head.y);
break;}
//上纵坐标加1
caseNORTH:
{
newHead=newCoordinate(head.x,head.y-1);
break;}
//下纵坐标-1
caseSOUTH:
{
newHead=newCoordinate(head.x,head.y+1);
break;}
}
//检测碰撞
if((newHead.x<1)||(newHead.y<1)||(newHead.x>mXTileCount-2)||(newHead.y>mYTileCount-2)){
setMode(LOSE);
return;
}
intsnakelength=mSnakeTrail.size();
//自己碰到自己
for(intsnakeindex=0;snakeindexCoordinatec=mSnakeTrail.get(snakeindex);
if(c.equals(newHead)){
setMode(LOSE);
return;
}
}
intapplecount=mAppleList.size();
//当吃到苹果后的处理,移除一个添加一个,同时分数加1,速度加快。
for(intappleindex=0;appleindexCoordinatec=mAppleLis