• 贪吃蛇源代码分析


    今天已经是农历12月16号了,用这一篇博文给今年的博客画上一个句号吧。

    首先,下载一个贪吃蛇的源代码,结构如下:(网上资源很多,我这里就不给出了)


    打开AndroidManifest.xml找到应用入口

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.xmobileapp.Snake" android:versionName="1.0" android:versionCode="1">
        <application android:icon="@drawable/icon" android:label="@string/app_name">
            <activity android:name=".Snake" android:label="贪吃蛇"
            android:screenOrientation="portrait">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
        <uses-sdk android:minSdkVersion="3" />
    </manifest> 
    可以看到入口Activity是Snake,好吧我们先进到Snake.java文件中看看onCreate方法

    在onCreate方法中首先是如下两句代码:

    //设置为无标题的主题样式
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    		
    setContentView(R.layout.snake_layout);
    我们看看贪吃蛇的布局文件吧,继续打开snake_layout.xml

    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    	android:layout_width="fill_parent"
    	android:layout_height="fill_parent">
    	
    	<com.xmobileapp.Snake.SnakeView
    	 android:id="@+id/snake"
    		android:layout_width="fill_parent"
                    android:layout_height="fill_parent"
                    tileSize="12" android:background="@color/teneight"/>
    	
    	<RelativeLayout
    		android:layout_width="fill_parent"
    		android:layout_height="fill_parent" >
    		
    		<TextView
    		 android:id="@+id/text"
    			android:visibility="visible"
    			android:layout_width="wrap_content"
    			android:layout_height="wrap_content"
    			android:layout_centerInParent="true"
    			android:gravity="center_horizontal"
    			android:textColor="#FF0033"
    			android:textSize="24sp"/>
    			
    	     <Button android:id="@+id/play"
    	        android:layout_width="wrap_content" 
    	        android:layout_height="wrap_content"
    	        android:layout_centerInParent="true"
    		    android:layout_marginTop="10px"
    		    android:gravity="center_horizontal"
    	        android:text="点击这里开始哦"/>
    	</RelativeLayout>
    	<AbsoluteLayout
    	    android:layout_width="fill_parent"
    		android:layout_height="fill_parent"	
    	>
    	
    	
    	<ImageButton	android:id="@+id/left"
    			        android:layout_width="100px" 
    			        android:layout_height="100px"
    		            
    		            style="?android:attr/buttonStyleSmall" 
    		    		android:src="@drawable/left" 
    		    		android:layout_x="75px" 
    		    		android:layout_y="100px"/>
       <ImageButton	    android:id="@+id/right"
    			        android:layout_width="100px" 
    			        android:layout_height="100px"
    		            
    		            style="?android:attr/buttonStyleSmall" 
    		    		android:src="@drawable/right" 
    		    		android:layout_x="175px" 
    		    		android:layout_y="100px"/>
    	
    	<ImageButton	android:id="@+id/up"
    			        android:layout_width="100px" 
    			        android:layout_height="100px"
    		            
    		            style="?android:attr/buttonStyleSmall" 
    		    		android:src="@drawable/up" 
    		    		android:layout_x="125px" 
    		    		android:layout_y="50px"/>
    		    		
       <ImageButton	    android:id="@+id/down"
    			        android:layout_width="100px" 
    			        android:layout_height="100px"
    		            
    		            style="?android:attr/buttonStyleSmall" 
    		    		android:src="@drawable/down" 
    		    		android:layout_x="125px" 
    		    		android:layout_y="150px"/>	    		
    	</AbsoluteLayout>
    </FrameLayout>

    从布局源码和上图可以看出最外面是一个帧布局(层布局)FrameLayout

    接下来上面是一个叫做SnakeView的视图

    再上面是一个相对布局,该布局中放置了开始按钮和说明文字

    最上面是一个绝对布局,该不居中放置了四个ImageButton

    这个界面布局很容易理解,唯独里面的com.xmobileapp.Snake.SnakeView是一个自定义View.好吧下面我们来看看这个自定义的视图。

    刚进去就发现它继承自TileView,这个貌似没有见过,原来也是一个自定义的View啊,我们先看看这个TileView是个什么吧,回头再看SnakeView类


    	@Override
    	public void onDraw(Canvas canvas) {
    		super.onDraw(canvas);
    		for (int x = 0; x < mXTileCount; x += 1) {
    			for (int y = 0; y < mYTileCount; y += 1) {
    				if (mTileGrid[x][y] > 0) {
    					canvas.drawBitmap(mTileArray[mTileGrid[x][y]], mXOffset + x
    							* mTileSize, mYOffset + y * mTileSize, mPaint);
    				}
    			}
    		}
    
    	}
    

    可以看到这个TileView继承自View并重写了onDraw方法,猜想应该是画贪吃蛇中的每个方块的。这个疑问暂且留在这里,我们再回到Snake.java中的onCreate方法中。

    mSnakeView = (SnakeView) findViewById(R.id.snake);
    mSnakeView.setTextView((TextView) findViewById(R.id.text));
    将界面中的TextView对象注入到了SnakeView类中。

    		if (savedInstanceState == null) {
    			mSnakeView.setMode(mSnakeView.READY);
    		} else {
    			Bundle map = savedInstanceState.getBundle(ICICLE_KEY);
    			if (map != null) {
    				mSnakeView.restoreState(map);
    			} else {
    				mSnakeView.setMode(SnakeView.PAUSE);
    			}
    		}
    如果刚启动的时候saveInstanceState == null 则设置状态为准备就绪,否则执行下面的方法。

    先查看Bundle对象的getBundle方法http://developer.android.com/reference/android/os/Bundle.html


    大概意思是通过一个键返回一个值,其中的ICICLE_KEY是一个常量key

    	private static String ICICLE_KEY = "snake-view";
    下面我们再来看看restoreState方法

    	public void restoreState(Bundle icicle) {
    		setMode(PAUSE);
    
    		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"));
    	}
    现在也看不懂,但是大概能知道这是保存状态的方法。

    setMode方法我们就不进去看了,估计也看不懂,这个从方法名上我们猜测是一个设置游戏状态的方法。

    下面我们再看看开始游戏的按钮监听函数

    		case PLAY:
    			play.setVisibility(View.GONE);
    			left.setVisibility(View.VISIBLE);
    			right.setVisibility(View.VISIBLE);
    			up.setVisibility(View.VISIBLE);
    			down.setVisibility(View.VISIBLE);
    			if (mSnakeView.mMode == mSnakeView.READY
    					| mSnakeView.mMode == mSnakeView.LOSE) {
    				mSnakeView.initNewGame();
    				mSnakeView.setMode(mSnakeView.RUNNING);
    				mSnakeView.update();
    				updateStatus = new UpdateStatus();
    				updateStatus.start();
    				break;
    			}
    
    			if (mSnakeView.mMode == mSnakeView.PAUSE) {
    				mSnakeView.setMode(mSnakeView.RUNNING);
    				mSnakeView.update();
    
    				break;
    			}
    
    			if (mSnakeView.mDirection != mSnakeView.SOUTH) {
    				mSnakeView.mNextDirection = mSnakeView.NORTH;
    
    				break;
    			}
    
    			break;

    上面的几句是游戏开始的时候上面的四个方向按钮显示,开始按钮隐藏。

    游戏中有如下几种状态在SnakeView类中定义

    	public static final int PAUSE = 0; //暂停
    	public static final int READY = 1;  //准备开始
    	public static final int RUNNING = 2;  //运行状态
    	public static final int LOSE = 3;  //游戏结束,也就是输了
    如果刚进入游戏,然后开始游戏就执行如下几句代码

    mSnakeView.initNewGame();
    mSnakeView.setMode(mSnakeView.RUNNING);
    mSnakeView.update();
    updateStatus = new UpdateStatus();
    updateStatus.start();
    进入initNewGam()方法看看

    	void initNewGame() {
    		mSnakeTrail.clear();
    		mAppleList.clear();
    
    		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;
    		addRandomApple();
    		addRandomApple();
    
    		mMoveDelay = 600;
    		mScore = 0;
    	}
    其他的参数不用管,我们现在可以明白了,里面的Coordinate其实就是贪吃蛇游戏开始的时候的小方块,在这里创建了6个小方块

    	private class Coordinate {
    		public int x;
    		public int y;
    
    		public Coordinate(int newX, int newY) {
    			x = newX;
    			y = newY;
    		}
    
    		public boolean equals(Coordinate other) {
    			if (x == other.x && y == other.y) {
    				return true;
    			}
    			return false;
    		}
    
    		@Override
    		public String toString() {
    			return "Coordinate: [" + x + "," + y + "]";
    		}
    	}
    再看看update方法

    	public void update() {
    		if (mMode == RUNNING) {
    			long now = System.currentTimeMillis();
    
    			if (now - mLastMove > mMoveDelay) {
    				clearTiles();
    				updateWalls();
    				updateSnake();
    				updateApples();
    				mLastMove = now;
    			}
    			mRedrawHandler.sleep(mMoveDelay);
    		}
    
    	}
    明白了,我们可以调节mMoveDelay控制贪吃蛇移动的速度,详细控制就不看了,改变坐标位置就可以实现了。

    再来看看这两句代码的作用

    updateStatus = new UpdateStatus();
    updateStatus.start();
    点进去一看,原来这是一个线程,用来监听游戏是否结束,如果结束则通过Handle将控制方向按钮隐藏,将开始游戏按钮显示。

    接下来我们看看前后左右四个按钮是怎么控制贪吃蛇的方向的。由于四个方向原理一样,我们就看LEFT吧

    		case LEFT:
    
    			if (mSnakeView.mDirection != mSnakeView.EAST) {
    				mSnakeView.mNextDirection = mSnakeView.WEST;
    			}
    			break;
    上面代码的意思是,假如说现在正在向右移动则我们按向左的按钮无效,如果不是向右移动则按向右按钮改变方向。

    这里它用到了两个变量一个是当前的方向mDirection另一个是下一次刷新时候的方向(也就是改变的方向)mNextDirection.可以在updateSnake方法中看到实际上在下次刷新的时候会将mNextDirection的值赋给mDirection。

    通过上面的分析,对基本的结构和原理有了一个认识,下面我们来具体看一下各个类的作用及实现过程。

    这部分分析发现有人已经做过,就不重复劳动了:http://blog.csdn.net/biaobiaoqi/article/details/6618313

    代码运行如下:



  • 相关阅读:
    linux下18种监测网络带宽方式
    python常用正则表达式
    python获取当前路径
    python获取本机的IP
    Linux 误卸载自带python后的解决办法
    jmeter分布式运行
    jmeter非GUI的运行命令
    linux下安装jmeter
    java基础笔记(8)
    java基础笔记(7)
  • 原文地址:https://www.cnblogs.com/lanzhi/p/6469247.html
Copyright © 2020-2023  润新知