Android中贪吃蛇游戏的学习(三)
文章分类: 移动开发
视图VIew的类的Snake的,主要关注的学习的类。
- package com.easyway.dev.android.snake;
- import java.util.ArrayList;
- import java.util.Random;
- import android.content.Context;
- import android.content.res.Resources;
- import android.os.Bundle;
- import android.os.Handler;
- import android.os.Message;
- import android.util.AttributeSet;
- import android.util.Log;
- import android.view.KeyEvent;
- import android.view.View;
- import android.widget.TextView;
- /**
- *View类是Android的一个超类,这个类几乎包含了所有的屏幕类型。但它们之间有一些不同。每一个view
- *都有一个用于绘画的画布。这个画布可以用来进行任意扩展。
- *
- *一个项目的R.java文件是一个定义所有资源的索引文件。使用这个类就像使用一种速记方式来引用你项
- *目中包含的资源。这个有点特别的强大像对于Eclipse这类IDE的代码编译特性,因为它使你快速的,互动
- *式的定位你正在寻找的特定引用。
- *
- *到目前需要注意的重要事情是叫做”layout”的内部类和他的成员变量”main”,插件会通知你添加一个新
- *的XML布局文件,然后从新产生这个R.java文件,比如你添加了新的资源到你的项目,你将会看到R.java
- *也相应的改变了。放在你的项目目录的res/文件夹下。“res”是”resources”的缩写,它是存放所有非
- *代码资源的文件夹,包含象图片,本地化字符串和XML布局文件。
- *
- *
- *SnakeView:implementationofasimplegameofSnake
- *创建的view中一般要传入一个Context的实例,Context可以控制系统的调用,它提供了诸如资源解析
- *,访问数据库等。Activty类继承自Context类。
- *
- *视图也可以被嵌套,但和J2ME不同,我们可以将定制的视图和Android团队发布的Widgets一起使用。
- *在J2ME中,开发人员被迫选择GameCanvas和J2ME应用程序画布。这就意味着如果我们想要一个定制
- *的效果,就必须在GameCanvas上重新设计我们所有的widget。Android还不仅仅是这些,视图类型
- *也可以混合使用。Android还带了一个widget库,这个类库包括了滚动条,文本实体,进度条以及其
- *他很多控件。这些标准的widget可以被重载或被按着我们的习惯定制。
- *
- */
- public class SnakeView extends TileView{
- private static final StringTAG= "SnakeView" ;
- /**
- *Currentmodeofapplication:READYtorun,RUNNING,oryouhavealready
- *lost.staticfinalintsareusedinsteadofanenumforperformance
- *reasons.
- */
- private int mMode=READY;
- public static final int PAUSE= 0 ;
- public static final int READY= 1 ;
- public static final int RUNNING= 2 ;
- public static final int LOSE= 3 ;
- /**
- *设置方向
- *Currentdirectionthesnakeisheaded.
- */
- private int mDirection=NORTH;
- private int mNextDirection=NORTH;
- private static final int NORTH= 1 ;
- private static final int SOUTH= 2 ;
- private static final int EAST= 3 ;
- private static final int WEST= 4 ;
- /**
- *LabelsforthedrawablesthatwillbeloadedintotheTileViewclass
- */
- private static final int RED_STAR= 1 ;
- private static final int YELLOW_STAR= 2 ;
- private static final int GREEN_STAR= 3 ;
- /**
- *mScore:usedtotrackthenumberofapplescapturedmMoveDelay:numberof
- *millisecondsbetweensnakemovements.Thiswilldecreaseasapplesare
- *captured.
- */
- private long mScore= 0 ;
- private long mMoveDelay= 600 ;
- /**
- *mLastMove:trackstheabsolutetimewhenthesnakelastmoved,andisused
- *todetermineifamoveshouldbemadebasedonmMoveDelay.
- */
- private long mLastMove;
- /**
- *mStatusText:textshowstotheuserinsomerunstates
- */
- private TextViewmStatusText;
- /**
- *用于存储贪吃蛇中,苹果和蛇的点阵的坐标的信息的集合
- *mSnakeTrail:alistofCoordinatesthatmakeupthesnake'sbody
- *mAppleList:thesecretlocationofthejuicyapplesthesnakecraves.
- */
- private ArrayList<Coordinate>mSnakeTrail= new ArrayList<Coordinate>();
- private ArrayList<Coordinate>mAppleList= new ArrayList<Coordinate>();
- /**
- *为创建苹果坐标使用随机对象
- *Everyoneneedsalittlerandomnessintheirlife
- */
- private static final RandomRNG= new Random();
- /**
- *刷新界面处理器
- *Createasimplehandlerthatwecanusetocauseanimationtohappen.We
- *setourselvesasatargetandwecanusethesleep()
- *functiontocauseanupdate/invalidatetooccuratalaterdate.
- */
- private RefreshHandlermRedrawHandler= new RefreshHandler();
- /**
- *实现刷新的功能:
- *在J2ME中,刷新都是在canvas中通过调用线程结合repaint()来刷新,他们使线程不断去循环,
- *去调用canvas,笔者在android入门时也曾经想用J2ME的模式用在android中,结果报异常了,
- *为什么呢?很多人认为Dalvik虚拟机是一个Java虚拟机,因为Android的编程语言恰恰就是Java语言。
- *但是这种说法并不准确,因为Dalvik虚拟机并不是按照Java虚拟机的规范来实现的,两者并不兼容;
- *同时还要两个明显的不同:Java虚拟机运行的是Java字节码,而Dalvik虚拟机运行的则是其专有的
- *文件格式DEX(DalvikExecutable)。所以在以前JAVA里面能使用的模式,可能在android
- *里面用不起来。在自带的例子里面他是通过消息的机制来刷新的。而在android的消定义比较广泛,
- *比如,手机的暂停,启动,来电话、短信,键盘按下,弹起都是一个消息。总的来说,事件就是消息;
- *只要继承Handler类就可以对消息进行控制,或者处理,根据具体情况进行具体处理:
- *
- *@authorAdministrator
- *@date2010-5-24
- *@version1.0
- *@sinceJDK6.0
- */
- class RefreshHandler extends Handler{
- /**
- *响应消息
- */
- @Override
- public void handleMessage(Messagemsg){
- SnakeView. this .update();
- SnakeView. this .invalidate();
- }
- /**
- *向外提供人工的调用消息的接口
- *@paramdelayMillis
- */
- public void sleep( long delayMillis){
- //注销消息
- this .removeMessages( 0 );
- //添加消息
- sendMessageDelayed(obtainMessage( 0 ),delayMillis);
- }
- };
- /**
- *ConstructsaSnakeViewbasedoninflationfromXML
- *
- *@paramcontext
- *@paramattrs
- */
- public SnakeView(Contextcontext,AttributeSetattrs){
- super (context,attrs);
- initSnakeView();
- }
- public SnakeView(Contextcontext,AttributeSetattrs, int defStyle){
- super (context,attrs,defStyle);
- initSnakeView();
- }
- /**
- *初始化界面的
- */
- private void initSnakeView(){
- 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));
- }
- /**
- *初始化新的游戏
- */
- private void initNewGame(){
- mSnakeTrail.clear();
- mAppleList.clear();
- //Fornowwe'rejustgoingtoloadupashortdefaulteastboundsnake
- //that'sjustturnednorth
- //设置初始化蛇的位置
- mSnakeTrail.add( new Coordinate( 7 , 7 ));
- mSnakeTrail.add( new Coordinate( 6 , 7 ));
- mSnakeTrail.add( new Coordinate( 5 , 7 ));
- mSnakeTrail.add( new Coordinate( 4 , 7 ));
- mSnakeTrail.add( new Coordinate( 3 , 7 ));
- mSnakeTrail.add( new Coordinate( 2 , 7 ));
- mNextDirection=NORTH;
- //Twoapplestostartwith
- //设置苹果的位置
- addRandomApple();
- addRandomApple();
- //
- mMoveDelay= 600 ;
- //设置积分的
- mScore= 0 ;
- }
- /**
- *GivenaArrayListofcoordinates,weneedtoflattenthemintoanarrayof
- *intsbeforewecanstuffthemintoamapforflatteningandstorage.
- *
- *@paramcvec:aArrayListofCoordinateobjects
- *@return:asimplearraycontainingthex/yvaluesofthecoordinates
- *as[x1,y1,x2,y2,x3,y3...]
- */
- private int []coordArrayListToArray(ArrayList<Coordinate>cvec){
- int count=cvec.size();
- int []rawArray= new int [count* 2 ];
- for ( int index= 0 ;index<count;index++){
- Coordinatec=cvec.get(index);
- rawArray[ 2 *index]=c.x;
- rawArray[ 2 *index+ 1 ]=c.y;
- }
- return rawArray;
- }
- /**
- *Savegamestatesothattheuserdoesnotloseanything
- *ifthegameprocessiskilledwhileweareinthe
- *background.
- *
- *@returnaBundlewiththisview'sstate
- */
- public BundlesaveState(){
- Bundlemap= new Bundle();
- map.putIntArray( "mAppleList" ,coordArrayListToArray(mAppleList));
- map.putInt( "mDirection" ,Integer.valueOf(mDirection));
- map.putInt( "mNextDirection" ,Integer.valueOf(mNextDirection));
- map.putLong( "mMoveDelay" ,Long.valueOf(mMoveDelay));
- map.putLong( "mScore" ,Long.valueOf(mScore));
- map.putIntArray( "mSnakeTrail" ,coordArrayListToArray(mSnakeTrail));
- return map;
- }
- /**
- *Givenaflattenedarrayofordinatepairs,wereconstitutethemintoa
- *ArrayListofCoordinateobjects
- *
- *@paramrawArray:[x1,y1,x2,y2,...]
- *@returnaArrayListofCoordinates
- */
- private ArrayList<Coordinate>coordArrayToArrayList( int []rawArray){
- ArrayList<Coordinate>coordArrayList= new ArrayList<Coordinate>();
- int coordCount=rawArray.length;
- for ( int index= 0 ;index<coordCount;index+= 2 ){
- Coordinatec= new Coordinate(rawArray[index],rawArray[index+ 1 ]);
- coordArrayList.add(c);
- }
- return coordArrayList;
- }
- /**
- *Restoregamestateifourprocessisbeingrelaunched
- *
- *@paramicicleaBundlecontainingthegamestate
- */
- public void restoreState(Bundleicicle){
- setMode(PAUSE);
- //从资源中获取ArrayList
- mAppleList=coordArrayToArrayList(icicle.getIntArray( "mAppleList" ));
- mDirection=icicle.getInt( "mDirection" );
- mNextDirection=icicle.getInt( "mNextDirection" );
- mMoveDelay=icicle.getLong( "mMoveDelay" );
- mScore=icicle.getLong( "mScore" );
- mSnakeTrail=coordArrayToArrayList(icicle.getIntArray( "mSnakeTrail" ));
- }
- /**
- *重点的控制代码
- *
- *实现键盘事件:键盘主要起操作作用,在任何的手机游戏中键盘都是起重要的用,要本游戏中,
- *他就是起控制蛇的行走方向:现在我们分析他的代码:
- *就是通过判断那个键按下,然后再给要走的方向(mDirection)赋值。
- *
- *handleskeyeventsinthegame.Updatethedirectionoursnakeistraveling
- *basedontheDPAD.Ignoreeventsthatwouldcausethesnaketoimmediately
- *turnbackonitself.
- *
- *(non-Javadoc)
- *
- *@seeandroid.view.View#onKeyDown(int,android.os.KeyEvent)
- */
- @Override
- public boolean onKeyDown( int keyCode,KeyEventmsg){
- if (keyCode==KeyEvent.KEYCODE_DPAD_UP){
- if (mMode==READY|mMode==LOSE){
- /*
- *Atthebeginningofthegame,ortheendofapreviousone,
- *weshouldstartanewgame.
- */
- initNewGame();
- setMode(RUNNING);
- update();
- return ( true );
- }
- if (mMode==PAUSE){
- /*
- *Ifthegameismerelypaused,weshouldjustcontinuewhere
- *weleftoff.
- */
- setMode(RUNNING);
- update();
- return ( true );
- }
- if (mDirection!=SOUTH){
- mNextDirection=NORTH;
- }
- return ( true );
- }
- if (keyCode==KeyEvent.KEYCODE_DPAD_DOWN){
- if (mDirection!=NORTH){
- mNextDirection=SOUTH;
- }
- return ( true );
- }
- if (keyCode==KeyEvent.KEYCODE_DPAD_LEFT){
- if (mDirection!=EAST){
- mNextDirection=WEST;
- }
- return ( true );
- }
- if (keyCode==KeyEvent.KEYCODE_DPAD_RIGHT){
- if (mDirection!=WEST){
- mNextDirection=EAST;
- }
- return ( true );
- }
- return super .onKeyDown(keyCode,msg);
- }
- /**
- *SetstheTextViewthatwillbeusedtogiveinformation(suchas"Game
- *Over"totheuser.
- *
- *@paramnewView
- */
- public void setTextView(TextViewnewView){
- mStatusText=newView;
- }
- /**
- *Updatesthecurrentmodeoftheapplication(RUNNINGorPAUSEDorthelike)
- *aswellassetsthevisibilityoftextviewfornotification
- *
- *@paramnewMode
- */
- public void setMode( int newMode){
- int oldMode=mMode;
- mMode=newMode;
- if (newMode==RUNNING&oldMode!=RUNNING){
- mStatusText.setVisibility(View.INVISIBLE);
- update();
- return ;
- }
- Resourcesres=getContext().getResources();
- CharSequencestr= "" ;
- if (newMode==PAUSE){
- str=res.getText(R.string.mode_pause);
- }
- if (newMode==READY){
- str=res.getText(R.string.mode_ready);
- }
- if (newMode==LOSE){
- str=res.getString(R.string.mode_lose_prefix)+mScore
- +res.getString(R.string.mode_lose_suffix);
- }
- mStatusText.setText(str);
- mStatusText.setVisibility(View.VISIBLE);
- }
- /**
- *
- *生成苹果位置的代码:
- *苹果的位置就是更简单了,他是随机生成的,而且必须在现在蛇的位置相对远距离。
- *
- *Selectsarandomlocationwithinthegardenthatisnotcurrentlycovered
- *bythesnake.Currently_could_gointoaninfiniteloopifthesnake
- *currentlyfillsthegarden,butwe'llleavediscoveryofthisprizetoa
- *trulyexcellentsnake-player.
- *
- */
- private void addRandomApple(){
- CoordinatenewCoord= null ;
- boolean found= false ;
- while (!found){
- //随机生成新的X,Y位置
- //Chooseanewlocationforourapple
- int newX= 1 +RNG.nextInt(mXTileCount- 2 );
- int newY= 1 +RNG.nextInt(mYTileCount- 2 );
- newCoord= new Coordinate(newX,newY);
- //Makesureit'snotalreadyunderthesnake
- boolean collision= false ;
- int snakelength=mSnakeTrail.size();
- for ( int index= 0 ;index<snakelength;index++){
- //检查一下存放的位置是否合理
- if (mSnakeTrail.get(index).equals(newCoord)){
- collision= true ;
- }
- }
- //ifwe'rehereandthere'sbeennocollision,thenwehave
- //agoodlocationforanapple.Otherwise,we'llcircleback
- //andtryagain
- found=!collision;
- }
- if (newCoord== null ){
- Log.e(TAG, "SomehowendedupwithanullnewCoord!" );
- }
- //添加苹果的列表中的
- mAppleList.add(newCoord);
- }
- /**
- *Handlesthebasicupdateloop,checkingtoseeifweareintherunning
- *state,determiningifamoveshouldbemade,updatingthesnake'slocation.
- */
- public void update(){
- if (mMode==RUNNING){
- long now=System.currentTimeMillis();
- if (now-mLastMove>mMoveDelay){
- clearTiles();
- updateWalls();
- updateSnake();
- updateApples();
- mLastMove=now;
- }
- mRedrawHandler.sleep(mMoveDelay);
- }
- }
- /**
- *调用以上的方法以循环的方式位置数组赋值以及图片的索引。
- *
- *Drawssomewalls.
- *
- */
- private void updateWalls(){
- for ( int x= 0 ;x<mXTileCount;x++){
- setTile(GREEN_STAR,x, 0 ); //设置顶部的界限的位置
- setTile(GREEN_STAR,x,mYTileCount- 1 ); //设置底部界限的位置
- }
- for ( int y= 1 ;y<mYTileCount- 1 ;y++){
- setTile(GREEN_STAR, 0 ,y); //设置顶部的界限的位置
- setTile(GREEN_STAR,mXTileCount- 1 ,y); //设置底部界限的位置
- }
- }
- /**
- *Drawssomeapples.
- *
- */
- private void updateApples(){
- for (Coordinatec:mAppleList){
- setTile(YELLOW_STAR,c.x,c.y);
- }
- }
- /**
- *设置当前蛇的方向位置:
- *从以上的键盘代码我们可以看得出,程序中是通过触发来改变坐标(+1,-1)的方式来改蛇头的方向,
- *可见坐标在游戏编程中的作用,这个也是根据手机的屏幕是点阵的方式来显示,所以坐标就是一个
- *定位器。在这里大家可能还有一个疑问。就是就这个蛇什么能够以“7”字形来移动行走,其实我们
- *稍微仔细观察一下就知道了,在这里面,他们也是通过坐标的传递来实现的,只要把头部的坐标点
- *依次赋给下一个点,后面的每一个点都走过了头部所走过的点,而蛇的头部就是负责去获取坐标,整
- *个蛇的行走起来就很自然和连贯。坐标的方向变换又是通过判断那个方向按键的按下来改变的,这
- *样一来,键盘的作用就发挥出来了。蛇吃苹果又是怎样去实现?上面我所说到的坐标就起了作用。在蛇
- *所经过的每一个坐标,他们都要在苹果所在的(ArrayList<Coordinate>mAppleList=new
- *ArrayList<Coordinate>())坐标集里面集依次判断,若是坐标相同,那个这个苹果就被蛇吃了。
- *
- *Figureoutwhichwaythesnakeisgoing,seeifhe'srunintoanything(the
- *walls,himself,oranapple).Ifhe'snotgoingtodie,wethenaddtothe
- *frontandsubtractfromtherearinordertosimulatemotion.Ifwewantto
- *growhim,wedon'tsubtractfromtherear.
- *
- */
- private void updateSnake(){
- boolean growSnake= false ;
- //grabthesnakebythehead
- //获取蛇的头部
- Coordinatehead=mSnakeTrail.get( 0 );
- //创建一个新的蛇的头部应该的位置
- CoordinatenewHead= new Coordinate( 1 , 1 );
- //根据当前的为方向设置坐标的信息
- mDirection=mNextDirection;
- switch (mDirection){
- case EAST:{
- newHead= new Coordinate(head.x+ 1 ,head.y);
- break ;
- }
- case WEST:{
- newHead= new Coordinate(head.x- 1 ,head.y);
- break ;
- }
- case NORTH:{
- newHead= new Coordinate(head.x,head.y- 1 );
- break ;
- }
- case SOUTH:{
- newHead= new Coordinate(head.x,head.y+ 1 );
- break ;
- }
- }
- //Collisiondetection
- //Fornowwehavea1-squarewallaroundtheentirearena
- if ((newHead.x< 1 )||(newHead.y< 1 )||(newHead.x>mXTileCount- 2 )
- ||(newHead.y>mYTileCount- 2 )){
- setMode(LOSE);
- return ;
- }
- //Lookforcollisionswithitself
- int snakelength=mSnakeTrail.size();
- for ( int snakeindex= 0 ;snakeindex<snakelength;snakeindex++){
- Coordinatec=mSnakeTrail.get(snakeindex);
- if (c.equals(newHead)){
- setMode(LOSE);
- return ;
- }
- }
- //Lookforapples
- //查找苹果设置苹果新的位置的信息
- int applecount=mAppleList.size();
- for ( int appleindex= 0 ;appleindex<applecount;appleindex++){
- Coordinatec=mAppleList.get(appleindex);
- if (c.equals(newHead)){
- mAppleList.remove(c);
- addRandomApple();
- mScore++;
- //设置的移动的速度
- mMoveDelay*= 0.9 ;
- growSnake= true ;
- }
- }
- //将蛇头的位置信息放在第一个的对象中方取值
- //pushanewheadontotheArrayListandpulloffthetail
- mSnakeTrail.add( 0 ,newHead);
- //exceptifwewantthesnaketogrow
- if (!growSnake){
- mSnakeTrail.remove(mSnakeTrail.size()- 1 );
- }
- int index= 0 ;
- for (Coordinatec:mSnakeTrail){
- if (index== 0 ){
- setTile(YELLOW_STAR,c.x,c.y);
- } else {
- setTile(RED_STAR,c.x,c.y);
- }
- index++;
- }
- }
- /**
- *用于存储每一个位点的x,y坐标信息
- *Simpleclasscontainingtwointegervaluesandacomparisonfunction.
- *There'sprobablysomethingIshoulduseinstead,butthiswasquickand
- *easytobuild.
- *
- */
- private class Coordinate{
- public int x;
- public int y;
- public Coordinate( int newX, int newY){
- x=newX;
- y=newY;
- }
- public boolean equals(Coordinateother){
- if (x==other.x&&y==other.y){
- return true ;
- }
- return false ;
- }
- @Override
- public StringtoString(){
- return "Coordinate:[" +x+ "," +y+ "]" ;
- }
- }
- }