开源项目Amazed

开源项目是个学东西的好地方。http://code.google.com/p/apps-for-android/ ,我把代码上传一份http://download.csdn.net/detail/pipisky2006/3672322

Amazed是个重力感应的小球的游戏。

[效果图]

开源项目Amazed_第1张图片

先说下代码结构,一个activity,把view和window关联起来,并且在onResume调用了view中的registerListener方法来重新注册重力感应事件
public class AmazedActivity extends Activity {
	
	private AmazedView mView;
	
	
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // remove title bar.
        requestWindowFeature(Window.FEATURE_NO_TITLE);

        // setup our view, give it focus and display.
        mView = new AmazedView(getApplicationContext(), this);
        mView.setFocusable(true);
        this.setContentView(mView);
    }
    
    @Override
    protected void onResume() {
        super.onResume();
        mView.registerListener();
    }

    @Override
    public void onSaveInstanceState(Bundle icicle) {
        super.onSaveInstanceState(icicle);
        mView.unregisterListener();
    }
    
}

AmazedView是个唯一的一个view,但是真正的界面的绘制工作在Marble和Maze。Maze是底图迷宫,Marble是上面的弹球。AmazedView更多的是逻辑的控制。控制游戏的状态。

    /**
     * Updates the current game state with a new state. At the moment this is
     * very basic however if the game was to get more complicated the code
     * required for changing game states could grow quickly.
     * 
     * @param newState
     *            New game state
     */
    public void switchGameState(int newState) {
        mCurState = newState;
    }

首先初始化为 switchGameState(GAME_INIT),随后就是根据touch事件或者onkeydown这些用户手动的事件来驱动更改状态,或者是游戏运行的状态到失败或者重新开始的状态。最终游戏是需要显示出来的,而且在运行状态下是需要实时刷新的,这样才能更有更流畅和细腻的用户体验。先看看draw方法

    @Override
    public void onDraw(Canvas canvas) {
    	Log.d("AmazedView", "onDraw>>"+mMarble.getX()+">>>"+mMarble.getY());
        // update our canvas reference.
        mCanvas = canvas;

        // clear the screen.
        mPaint.setColor(Color.WHITE);
        mCanvas.drawRect(0, 0, mCanvasWidth, mCanvasHeight, mPaint);

        // simple state machine, draw screen depending on the current state.
        switch (mCurState) {
        case GAME_RUNNING:
            // draw our maze first since everything else appears "on top" of it.
            mMaze.draw(mCanvas, mPaint);

            // draw our marble and hud.
            mMarble.draw(mCanvas, mPaint);

            // draw hud
            drawHUD();
            break;

        case GAME_OVER:
            drawGameOver();
            break;

        case GAME_COMPLETE:
            drawGameComplete();
            break;

        case GAME_LANDSCAPE:
            drawLandscapeMode();
            break;
        }

        gameTick();
    }

  其他的状态比较简单,运行状态时,透传了当前view的Canvas,调用迷宫Maze来绘制迷宫的地板,调用子弹Marble绘制子弹的位置。
    /**
     * Called every cycle, used to process current game state.
     */
    public void gameTick() {
        // very basic state machine, makes a good foundation for a more complex
        // game.
        switch (mCurState) {
        case GAME_INIT:
            // prepare a new game for the user.
            initNewGame();
            switchGameState(GAME_RUNNING);

        case GAME_RUNNING:
            // update our marble.
            if (!mWarning)
                updateMarble();
            break;
        }

        // redraw the screen once our tick function is complete.
        invalidate();
    }
至于刷新的循环驱动,这里做的比较傻,一直在循环刷新,我觉得这里可以优化一下,在状态更改为运行时的时候进行一次invalidate() ;然后gameTick()中的invalidate()可以在mCurstate为GAME_RUNNING时调用,其他的状态绘制一次就可以了。


另外值得一说的是迷宫的地图的加载。是通过读取Assert下面的levelXX.txt中来获取的,1代表空,0代表可以运行的路径,2代表目标

开源项目Amazed_第2张图片


下面的代码实现了从assets文件夹指定的文件中读取迷宫描述数据,保存至整型数组mMazeData中,在绘制的时候更加mMazeData的数值的不同加载不同的bitmap。

    /**
     * Load specified maze level.
     * 
     * @param activity
     *           Activity controlled the maze, we use this load the level data
     * @param newLevel
     *            Maze level to be loaded.
     */
    void load(Activity activity, int newLevel) {
        // maze data is stored in the assets folder as level1.txt, level2.txt
        // etc....
        String mLevel = "level" + newLevel + ".txt";

        InputStream is = null;

        try {
            // construct our maze data array.
            mMazeData = new int[MAZE_ROWS * MAZE_COLS];
            // attempt to load maze data.
            is = activity.getAssets().open(mLevel);

            // we need to loop through the input stream and load each tile for
            // the current maze.
            for (int i = 0; i < mMazeData.length; i++) {
                // data is stored in unicode so we need to convert it.
                mMazeData[i] = Character.getNumericValue(is.read());

                // skip the "," and white space in our human readable file.
                is.read();
                is.read();
            }
        } catch (Exception e) {
            Log.i("Maze", "load exception: " + e);
        } finally {
            closeStream(is);
        }

    }

    /**
     * Draw the maze.
     * 
     * @param canvas
     *            Canvas object to draw too.
     * @param paint
     *            Paint object used to draw with.
     */
    public void draw(Canvas canvas, Paint paint) {
        // loop through our maze and draw each tile individually.
        for (int i = 0; i < mMazeData.length; i++) {
            // calculate the row and column of the current tile.
            mRow = i / MAZE_COLS;
            mCol = i % MAZE_COLS;

            // convert the row and column into actual x,y co-ordinates so we can
            // draw it on screen.
            mX = mCol * TILE_SIZE;
            mY = mRow * TILE_SIZE;

            // draw the actual tile based on type.
            if (mMazeData[i] == PATH_TILE)
                canvas.drawBitmap(mImgPath, mX, mY, paint);
            else if (mMazeData[i] == EXIT_TILE)
                canvas.drawBitmap(mImgExit, mX, mY, paint);
            else if (mMazeData[i] == VOID_TILE) {
                // since our "void" tile is purely black lets draw a rectangle
                // instead of using an image.

                // tile attributes we are going to paint.
                mRect.left = mX;
                mRect.top = mY;
                mRect.right = mX + TILE_SIZE;
                mRect.bottom = mY + TILE_SIZE;

                paint.setColor(VOID_COLOR);
                canvas.drawRect(mRect, paint);
            }
        }

    }


最后,提一下流关闭的这个方法。
    /**
     * Closes the specified stream.
     * 
     * @param stream
     *            The stream to close.
     */
    private static void closeStream(Closeable stream) {
        if (stream != null) {
            try {
                stream.close();
            } catch (IOException e) {
                // Ignore
            }
        }
    }

java.io.Closeable的定义如下,定义了一个接口用来关闭那些以后不再用到的类,通常包括InputStream和OutputStream等。调用close方法可以释放资源和所持有的对象。

Defines an interface for classes that can (or need to) be closed once they are not used any longer. This usually includes all sorts ofInputStreams andOutputStreams. Calling theclose method releases resources that the object holds.

A common pattern for using a Closeable resource:

   Closable foo = new Foo();
   try {
      ...;
    finally {
      foo.close();
   }
 }

这个小游戏不是很复杂,看完代码后觉得整体的面向对象做的非常好,各个类的分工都明确,代码看起来很舒服,值得学习。

参考 http://blog.csdn.net/stefzeus/article/details/6387462

你可能感兴趣的:(开源项目Amazed)