对于这种和贪吃蛇类似的游戏,坐标的计算在程序中占了很大的比重,只有坐标算对了,才能在正确的时间、正确的地点画出来正确的东西。
下面是Maze迷宫的坐标计算
// maze level data private static int[] mMazeData;这个一维数组存放所有的tile的类型
// maze tile size and dimension private final static int TILE_SIZE = 16; private final static int MAZE_COLS = 20; private final static int MAZE_ROWS = 26;
// tile types public final static int PATH_TILE = 0; public final static int VOID_TILE = 1;//death area public final static int EXIT_TILE = 2;//the target而这个坐标的读取是通过从文件读取的,当然也可以通过写几个数组的形式,但是以文件的形式的话就不需要修改代码就可以实现软件的修改是一种比在代码中写几个大的数组要好的形式。
/** * 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); } }mMazeData数组存放了每个tile是什么类型的,MAZE_ROWS一共有多少行,MAZE_COLS一共多少列,TILE_SIZE每个tile的宽度,这样就可以在界面上画出来整个界面的布局
/** * 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); } } }
// calculate the row and column of the current tile. mRow = i / MAZE_COLS; mCol = i % MAZE_COLS;获得第i个点所在的行号和列号
// 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;已知行号和列号,知道在屏幕上的位置,但是并不是实际屏幕上的位置,通过乘以每个tile的宽度,获得在屏幕上实际的位置,这个位置是可以画在屏幕上的位置。
上面onDraw的过程是根据mMazeData[i]的第i个点计算在屏幕上的实际位置,而下面的getCellType则是根据在屏幕上的实际的x/y坐标点,计算出在mMazeData中的位置,也就是上面过程的逆过程,函数getCellType(int x, int y)的作用也就是计算出现在Marble所在的屏幕的位置所在的tile是什么类型的。
/** * Determine which cell the marble currently occupies. * * @param x * Current x co-ordinate of marble on the screen. * @param y * Current y co-ordinate of marble on the screen. * @return The actual cell occupied by the marble. */ public int getCellType(int x, int y) { // convert the x,y co-ordinate into row and col values. int mCellCol = x / TILE_SIZE; int mCellRow = y / TILE_SIZE; // location is the row,col coordinate converted so we know where in the // maze array to look. int mLocation = 0; // if we are beyond the 1st row need to multiple by the number of // columns. if (mCellRow > 0) mLocation = mCellRow * MAZE_COLS; // add the column location. mLocation += mCellCol; return mMazeData[mLocation]; }下面是Marble小球的坐标计算
// marble attributes // x,y are private because we need boundary checking on any new values to // make sure they are valid. private int mX = 0; private int mY = 0; private int mRadius = 8; private int mColor = Color.WHITE; private int mLives = 5;在AmazedView中的onDraw()调用gameTick()的时候会更新小球Marble的坐标:
/** * Called from gameTick(), update marble x,y based on latest values obtained * from the Accelerometer sensor. AccelX and accelY are values received from * the accelerometer, higher values represent the device tilted at a more * acute angle. */ public void updateMarble() { // we CAN give ourselves a buffer to stop the marble from rolling even // though we think the device is "flat". if (mAccelX > mSensorBuffer || mAccelX < -mSensorBuffer) mMarble.updateX(mAccelX); if (mAccelY > mSensorBuffer || mAccelY < -mSensorBuffer) mMarble.updateY(mAccelY); // check which cell the marble is currently occupying. if (mMaze.getCellType(mMarble.getX(), mMarble.getY()) == mMaze.VOID_TILE) { // user entered the "void". if (mMarble.getLives() > 0) { // user still has some lives remaining, restart the level. mMarble.death(); mMarble.init(); mWarning = true; } else { // user has no more lives left, end of game. mEndTime = System.currentTimeMillis(); mTotalTime += mEndTime - mStartTime; switchGameState(GAME_OVER); } } else if (mMaze.getCellType(mMarble.getX(), mMarble.getY()) == mMaze.EXIT_TILE) { // user has reached the exit tiles, prepare the next level. mEndTime = System.currentTimeMillis(); mTotalTime += mEndTime - mStartTime; initLevel(); } }在Marble中更新小球坐标:
/** * Attempt to update the marble with a new x value, boundary checking * enabled to make sure the new co-ordinate is valid. * * @param newX * Incremental value to add onto current x co-ordinate. */ public void updateX(float newX) { mX += newX; // boundary checking, don't want the marble rolling off-screen. if (mX + mRadius >= mView.getWidth()) mX = mView.getWidth() - mRadius; else if (mX - mRadius < 0) mX = mRadius; }if-else中是对左右边界的处理,同样的是对上下边界的处理。