Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1535258
  • 博文数量: 113
  • 博客积分: 3526
  • 博客等级: 中校
  • 技术积分: 1815
  • 用 户 组: 普通用户
  • 注册时间: 2009-09-08 09:46
个人简介

记录总结自己的工作

文章分类

全部博文(113)

文章存档

2015年(19)

2014年(10)

2013年(6)

2012年(16)

2011年(24)

2010年(21)

2009年(17)

分类: 嵌入式

2011-04-20 09:08:56

    前几天看Android自带的例子代码,其中有个贪吃蛇的游戏,看了其代码后觉得写得非常好,同时又有了想写一个俄罗斯方块游戏的想法。俄罗斯方块的游戏我以前曾经用JAVA实现过,当时实现得很复杂,而现在有了这个贪吃蛇的例子后,实现起来就简单多了。在贪吃蛇游戏中有个类TileView.java,这个类基本实现了对单个方块的操作,因此我就直接拿来用了,并没有做任何的修改,这个类的代码就不在这里贴出了。
    俄罗斯方块游戏比贪吃蛇稍微复杂一些,其主要的难点主要有:
    1.实现方块的旋转。我们都知道在游戏中按上方块是要旋转的,我对方块的旋转就是选择一个方块作为中心,另外3个方块都绕着它顺时针旋转90度。需要注意的一点就是田形的方块是不需要旋转的,如果按照我的方法旋转田形方块就会造成方块平移的结果。
    2.边界的判定。就是判断一下方块是否可以移动,这包含两方面的内容,一方面是水平方向的移动,这里需要注意的就是在边界上旋转方块又可能使方块旋转出边界,所以在旋转前必要预先判断一下旋转后的方块是否已经超出了边界;另一方面是往下移动,一旦不能移动了就需要冻结方块并产生新的方块。
    3.消行。每落下一个方块都需要判断一下是否可以消行。而消行后上面的方块又都要垂直下落,同时原来的位置也要被清空。

    下面说一下实现这个游戏的步骤:
    1.创建一个新的ANDROID工程,命名为Tetris,包名为com.test。
    2.创建一个activity类,命名为:Tetris.java。

    1. package com.test;

    2. import android.app.Activity;
    3. import android.content.Context;
    4. import android.os.Bundle;
    5. import android.util.Log;
    6. import android.widget.TextView;

    7. import java.io.File;
    8. import java.io.FileInputStream;
    9. import java.io.FileNotFoundException;
    10. import java.io.FileOutputStream;
    11. import java.io.IOException;
    12. import java.io.InputStream;
    13. import java.io.OutputStream;

    14. public class Tetris extends Activity {
    15.     private String TAG = "Tetris";

    16.     private TetrisView tetrisView;

    17.     private TextView highestScore;

    18.     private TextView info;

    19.     private TextView currentScore;

    20.     private TextView currentLevel;

    21.     String NAME = "score.txt";

    22.     /**
    23.      * 保存数据时用到的key
    24.      */
    25.     private static String ICICLE_KEY = "tetris-view";

    26.     /**
    27.      * 游戏的主要activity
    28.      */
    29.     public void onCreate(Bundle savedInstanceState) {
    30.         super.onCreate(savedInstanceState);
    31.         setContentView(R.layout.tetris_layout);
    32.         tetrisView = (TetrisView)findViewById(R.id.tetris);
    33.         highestScore = (TextView)findViewById(R.id.highest_score);
    34.         currentScore = (TextView)findViewById(R.id.current_score);
    35.         info = (TextView)findViewById(R.id.info);
    36.         currentLevel = (TextView)findViewById(R.id.current_level);
    37.         // 为view实例化这些文本框
    38.         tetrisView.setTextView(currentScore, highestScore, info, currentLevel);
    39.         if (savedInstanceState == null) {
    40.             // 开始一个新游戏,将游戏的状态设为READY
    41.             tetrisView.setMode(TetrisView.READY);
    42.         } else {
    43.             // 如果存储了一个游戏的状态,则将状态读出来,可以继续游戏
    44.             Bundle map = savedInstanceState.getBundle(ICICLE_KEY);
    45.             if (map != null) {
    46.                 tetrisView.restoreState(map);
    47.             } else {
    48.                 tetrisView.setMode(TetrisView.PAUSE);
    49.             }
    50.         }
    51.         // 读取历史最高分
    52.         FileInputStream in;
    53.         byte[] by = new byte[10];
    54.         try {
    55.             in = openFileInput(NAME);
    56.             in.read(by);
    57.             StringBuffer buffer = new StringBuffer();
    58.             for (int i = 0; i < 10; i++) {
    59.                 if (by[i] != 0) {
    60.                     buffer.append(by[i] - 48);
    61.                 }
    62.             }
    63.             Log.d("dd", buffer.toString());
    64.             tetrisView.historyScore = Long.valueOf(buffer.toString());
    65.         } catch (FileNotFoundException e) {
    66.         } catch (IOException e) {
    67.             // TODO Auto-generated catch block
    68.             e.printStackTrace();
    69.         }

    70.     }

    71.     @Override
    72.     protected void onPause() {
    73.         //如果分数超出了历史最高分,则保存数据
    74.         if (tetrisView.overhistroy) {
    75.             FileOutputStream fos;
    76.             try {
    77.                 fos = openFileOutput(NAME, Context.MODE_PRIVATE);
    78.                 fos.write(String.valueOf(TetrisView.historyScore).getBytes());
    79.                 fos.close();

    80.             } catch (FileNotFoundException e) {
    81.                 // TODO Auto-generated catch block
    82.                 e.printStackTrace();
    83.             } catch (IOException e) {
    84.                 // TODO Auto-generated catch block
    85.                 e.printStackTrace();
    86.             }
    87.         }
    88.         super.onPause();
    89.         // 暂停游戏
    90.         tetrisView.setMode(TetrisView.PAUSE);
    91.     }

    92.     @Override
    93.     public void onSaveInstanceState(Bundle outState) {
    94.         // 保存游戏状态
    95.         outState.putBundle(ICICLE_KEY, tetrisView.saveState());
    96.     }

    97. }


    1. package com.test;

    2. import android.content.Context;
    3. import android.content.res.Resources;
    4. import android.os.Bundle;
    5. import android.os.Handler;
    6. import android.os.Message;
    7. import android.util.AttributeSet;
    8. import android.util.Log;
    9. import android.view.KeyEvent;
    10. import android.view.View;
    11. import android.widget.TextView;

    12. import java.io.File;
    13. import java.io.FileNotFoundException;
    14. import java.io.FileOutputStream;
    15. import java.io.IOException;
    16. import java.util.ArrayList;
    17. import java.util.Random;

    18. public class TetrisView extends TileView {

    19.     private static final String TAG = "TetrisView";

    20.     /**
    21.      * 游戏的四种状态
    22.      */
    23.     private int mMode = READY;

    24.     public static final int PAUSE = 0;

    25.     public static final int READY = 1;

    26.     public static final int RUNNING = 2;

    27.     public static final int LOSE = 3;

    28.     /**
    29.      * 三种背景图片,由这些图片拼装成游戏的基本界面
    30.      */
    31.     private static final int RED_STAR = 1;

    32.     private static final int YELLOW_STAR = 2;

    33.     private static final int GREEN_STAR = 3;

    34.     /**
    35.      * 产生随机数,根据随机数决定方块的形状
    36.      */
    37.     private static final Random RNG = new Random();

    38.     /**
    39.      * 当前游戏的分数
    40.      */
    41.     private long mScore = 0;

    42.     /**
    43.      * 历史最高分
    44.      */
    45.     public static long historyScore = 0;
    46.     
    47.     /**
    48.      * 是否超出了最高分,如果超出了,则在退出时会保存数据
    49.      */
    50.     public static boolean overhistroy=false;
    51.     /**
    52.      * 方块移动的速度,数值越小则方块的速度越高,难度也就越大
    53.      */
    54.     private long mMoveDelay;

    55.     /**
    56.      * 当前方块移动的速度,和mMoveDelay配合使用,在游戏中更改游戏的速度都是更改此变量值
    57.      * 在每个方块产生的时候,将此变量值赋给mMoveDelay。之所以采用两个变量是因为当按下加速 方块落下之后,下一个方块的速度还要保持原来的速度。
    58.      */
    59.     private long currentDelay;

    60.     /**
    61.      * 预先显示的方块,在前一个方块落下后,使其变为当前方块
    62.      */
    63.     private ArrayList<Coordinate> preShape = new ArrayList<Coordinate>();

    64.     /**
    65.      * 当前正在下落的方块
    66.      */
    67.     private ArrayList<Coordinate> mShape = new ArrayList<Coordinate>();

    68.     private ArrayList<Coordinate> oldShape = new ArrayList<Coordinate>();

    69.     /**
    70.      * 显示历史最高分的文本框
    71.      */
    72.     private TextView highestScore;

    73.     /**
    74.      * 显示当前分数的文本框
    75.      */
    76.     private TextView currentScore;

    77.     /**
    78.      * 显示当前游戏级别的文本框
    79.      */
    80.     private TextView currentLevel;

    81.     /**
    82.      * 记录游戏的级别
    83.      */
    84.     private int gameLevel = 1;

    85.     /**
    86.      * 在屏幕中央显示提示信息的文本框
    87.      */
    88.     private TextView info;

    89.     /**
    90.      * 记录目前落下的方块的最高层数
    91.      */
    92.     private int highLevel = 0;

    93.     /**
    94.      * 当前方块类型,主要用来标示田形方块,从而在旋转方块的时候可以让田形方块不旋转
    95.      */
    96.     private int shapeType;

    97.     /**
    98.      * 预先显示方块的类型
    99.      */
    100.     private int preType;

    101.     /**
    102.      * 构造方法
    103.      */
    104.     public TetrisView(Context context, AttributeSet attrs) {
    105.         super(context, attrs);
    106.         initGame();
    107.     }

    108.     public TetrisView(Context context, AttributeSet attrs, int defStyle) {
    109.         super(context, attrs, defStyle);
    110.         initGame();
    111.     }

    112.     /**
    113.      * 通过一个handler来更新界面的显示,以及方块下落的速度
    114.      */
    115.     private RefreshHandler mRedrawHandler = new RefreshHandler();

    116.     class RefreshHandler extends Handler {

    117.         @Override
    118.         public void handleMessage(Message msg) {
    119.             TetrisView.this.update();
    120.             TetrisView.this.invalidate();

    121.         }

    122.         public void sleep(long delayMillis) {
    123.             this.removeMessages(0);
    124.             sendMessageDelayed(obtainMessage(0), delayMillis);
    125.         }
    126.     };

    127.     /**
    128.      * 初始化游戏
    129.      */
    130.     private void initGame() {
    131.         setFocusable(true);
    132.         Resources r = this.getContext().getResources();
    133.         resetTiles(4);
    134.         loadTile(RED_STAR, r.getDrawable(R.drawable.redstar));
    135.         loadTile(YELLOW_STAR, r.getDrawable(R.drawable.yellowstar));
    136.         loadTile(GREEN_STAR, r.getDrawable(R.drawable.greenstar));

    137.     }

    138.     /**
    139.      * 开始一个新游戏,重置各种数据
    140.      */
    141.     private void startGame() {
    142.         clearTiles();
    143.         gameLevel = 1;
    144.         highLevel = 0;
    145.         currentDelay = 600;
    146.         mScore = 0;
    147.         currentScore.setText("当前分数:\n" + mScore);
    148.         currentLevel.setText("当前级别:\n" + gameLevel);
    149.         highestScore.setText("最高分数:\n"+historyScore);
    150.         setMode(RUNNING);
    151.     }


    152.     /**
    153.      * 将arraylist转换为数组,从而可以将该部分数据保存起来
    154.      */
    155.     private int[] coordArrayListToArray(ArrayList<Coordinate> cvec) {
    156.         int count = cvec.size();
    157.         int[] rawArray = new int[count * 2];
    158.         for (int index = 0; index < count; index++) {
    159.             Coordinate c = cvec.get(index);
    160.             rawArray[2 * index] = c.x;
    161.             rawArray[2 * index + 1] = c.y;
    162.         }
    163.         return rawArray;
    164.     }

    165.     /**
    166.      * 将已经落下来的方块的坐标保存在一个ArrayList中
    167.      */
    168.     private ArrayList<Coordinate> tailsToList(int[][] tileGrid) {
    169.         ArrayList<Coordinate> tranList = new ArrayList<Coordinate>();
    170.         for (int i = 0; i < mXTileCount - 6; i++) {
    171.             for (int j = 1; j < mYTileCount - 1; j++) {
    172.                 if (tileGrid[i][j] == RED_STAR) {
    173.                     Coordinate cor = new Coordinate(i, j);
    174.                     tranList.add(cor);
    175.                 }
    176.             }
    177.         }
    178.         return tranList;
    179.     }

    180.     /**
    181.      * 保存游戏的状态,从而在切换游戏后还可以继续游戏
    182.      *
    183.      * @return a Bundle with this view's state
    184.      */
    185.     public Bundle saveState() {
    186.         Bundle map = new Bundle();
    187.         map.putIntArray("preShapeList", coordArrayListToArray(preShape));
    188.         map.putIntArray("mShapeList", coordArrayListToArray(mShape));
    189.         map.putLong("mMoveDelay", Long.valueOf(mMoveDelay));
    190.         map.putLong("mScore", Long.valueOf(mScore));
    191.         map.putLong("hisScore", Long.valueOf(historyScore));
    192.         map.putInt("mLevel", highLevel);
    193.         map.putIntArray("tailList", coordArrayListToArray(tailsToList(mTileGrid)));

    194.         return map;
    195.     }

    196.     /**
    197.      * 将数组转换为Arraylist,从而将保存的数据转化为游戏的状态
    198.      *
    199.      * @param rawArray : [x1,y1,x2,y2,...]
    200.      * @return a ArrayList of Coordinates
    201.      */
    202.     private ArrayList<Coordinate> coordArrayToArrayList(int[] rawArray) {
    203.         ArrayList<Coordinate> coordArrayList = new ArrayList<Coordinate>();
    204.         int coordCount = rawArray.length;
    205.         for (int index = 0; index < coordCount; index += 2) {
    206.             Coordinate c = new Coordinate(rawArray[index], rawArray[index + 1]);
    207.             coordArrayList.add(c);
    208.         }
    209.         return coordArrayList;
    210.     }

    211.     /**
    212.      * 从保存的数据中读取已经落下的方块坐标,并重新在界面上画出这些方块
    213.      */
    214.     private void listToTail(ArrayList<Coordinate> cor) {
    215.         int count = cor.size();

    216.         for (int index = 0; index < count; index++) {
    217.             Coordinate c = cor.get(index);
    218.             mTileGrid[c.x][c.y] = RED_STAR;
    219.         }
    220.     }

    221.     /**
    222.      * 切换到游戏的时候,读取所保存的数据,重现游戏先前的状态
    223.      *
    224.      * @param icicle a Bundle containing the game state
    225.      */
    226.     public void restoreState(Bundle icicle) {
    227.         setMode(PAUSE);
    228.         historyScore = icicle.getLong("hisScore");
    229.         highLevel = icicle.getInt("mLevel");
    230.         mMoveDelay = icicle.getLong("mMoveDelay");
    231.         mScore = icicle.getLong("mScore");
    232.         preShape = coordArrayToArrayList(icicle.getIntArray("preShapeList"));
    233.         mShape = coordArrayToArrayList(icicle.getIntArray("mShapeList"));
    234.         listToTail(coordArrayToArrayList(icicle.getIntArray("tailList")));
    235.     }

    236.     /**
    237.      * 对按键的响应
    238.      */
    239.     public boolean onKeyDown(int keyCode, KeyEvent msg) {
    240.         // 按下了上键
    241.         if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
    242.             if (mMode == READY | mMode == LOSE) {

    243.                 startGame();
    244.                 setMode(RUNNING);
    245.                 return (true);
    246.             } else if (mMode == RUNNING) {
    247.                 transShape();
    248.                 update();
    249.                 return (true);
    250.             }
    251.             if (mMode == PAUSE) {
    252.                 setMode(RUNNING);
    253.                 update();
    254.                 return (true);
    255.             }
    256.         }
    257.         // 按下了下键
    258.         if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
    259.             moveDown();

    260.             return (true);
    261.         }
    262.         // 按下了左键
    263.         if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
    264.             moveLeft();
    265.             updateShape();
    266.             return (true);
    267.         }
    268.         // 按下了右键
    269.         if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
    270.             moveRight();
    271.             updateShape();
    272.             return (true);
    273.         }
    274.         return super.onKeyDown(keyCode, msg);
    275.     }

    276.     /**
    277.      * 加速方块的下落
    278.      */
    279.     private void moveDown() {
    280.         mMoveDelay = 50;
    281.     }

    282.     /**
    283.      * 将方块向右移动,在移动前要判断一下是否可以移动
    284.      */
    285.     private void moveRight() {
    286.         newToOld(mShape, oldShape);
    287.         for (Coordinate c : mShape) {
    288.             c.x = c.x + 1;
    289.         }
    290.         // 如果不可以移动,则保持位置不变
    291.         if (!isMoveAble(mShape)) {
    292.             for (Coordinate c : mShape) {
    293.                 c.x = c.x - 1;
    294.             }
    295.         }
    296.     }

    297.     /**
    298.      * 将方块向左移动
    299.      */
    300.     private void moveLeft() {
    301.         newToOld(mShape, oldShape);
    302.         for (Coordinate c : mShape) {
    303.             c.x = c.x - 1;
    304.         }
    305.         if (!isMoveAble(mShape)) {
    306.             for (Coordinate c : mShape) {
    307.                 c.x = c.x + 1;
    308.             }
    309.         }
    310.     }

    311.     /**
    312.      * 旋转方块
    313.      */
    314.     private void transShape() {
    315.         Coordinate core = mShape.get(0);
    316.         if (shapeType == 3) {
    317.             // 田形,不用做任何事情
    318.         } else {
    319.             newToOld(mShape, oldShape);
    320.             for (Coordinate c : mShape) {
    321.                 int x = core.x + (core.y - c.y);
    322.                 int y = core.y + (c.x - core.x);
    323.                 c.x = x;
    324.                 c.y = y;
    325.             }
    326.             // 如果可以旋转,则旋转90度并更新显示
    327.             if (isMoveAble(mShape)) {
    328.                 updateShape();
    329.                 TetrisView.this.invalidate();
    330.             } else {
    331.                 for (Coordinate c : mShape) {
    332.                     int x = core.x + (c.y - core.y);
    333.                     int y = core.y + (core.x - c.x);
    334.                     c.x = x;
    335.                     c.y = y;
    336.                 }
    337.             }

    338.         }

    339.     }

    340.     /**
    341.      * 将一个方块的形状赋给另外一个
    342.      *
    343.      * @param mShape2
    344.      * @param oldShape2
    345.      */
    346.     private void newToOld(ArrayList<Coordinate> mShape2, ArrayList<Coordinate> oldShape2) {
    347.         oldShape2.clear();
    348.         for (int i = 0; i < 4; i++) {
    349.             Coordinate c1 = mShape2.get(i);
    350.             Coordinate c2 = new Coordinate(c1.x, c1.y);
    351.             oldShape2.add(c2);
    352.         }

    353.     }

    354.     /**
    355.      * 处理游戏的更新
    356.      */
    357.     public void update() {
    358.         if (mMode == RUNNING) {
    359.             // clearTiles();
    360.             updateBlackGround();
    361.             if (mXTileCount != 0) {
    362.                 // 如果是刚开始游戏,则初始化方块形状
    363.                 if (mShape.size() == 0) {
    364.                     shapeType = RNG.nextInt(7);
    365.                     mShape = getShape(shapeType);
    366.                     preType = RNG.nextInt(7);
    367.                     preShape = getShape(preType);
    368.                     mMoveDelay = currentDelay;
    369.                     // 设置方块的初始位置
    370.                     for (Coordinate c : mShape) {
    371.                         c.x = c.x + mXTileCount / 3;
    372.                         c.y = c.y + 1;
    373.                     }
    374.                 }
    375.                 // 将方块往下移动一格
    376.                 newToOld(mShape, oldShape);
    377.                 for (Coordinate c : mShape) {
    378.                     c.y++;
    379.                 }
    380.                 // 如果方块可以往下移动,则更新图形
    381.                 if (canMoveDown(mShape)) {
    382.                     updateShape();
    383.                 } else {
    384.                     // 如果不可以往下移动则将方块的状态改为已落下并开始落下新方块
    385.                     updateBlew();
    386.                     mShape = preShape;
    387.                     shapeType = preType;
    388.                     for (Coordinate c : mShape) {
    389.                         c.x = c.x + mXTileCount / 3;
    390.                         c.y = c.y + 1;
    391.                     }
    392.                     preType = RNG.nextInt(7);
    393.                     preShape = getShape(preType);
    394.                     mMoveDelay = currentDelay;
    395.                 }
    396.                 updatePreShape();
    397.             }
    398.         }
    399.         // 设置方块落下的速度
    400.         mRedrawHandler.sleep(mMoveDelay);

    401.     }

    402.     // 方块落下过程中颜色为黄色
    403.     private void updateShape() {
    404.         for (Coordinate c : oldShape) {
    405.             setTile(0, c.x, c.y);
    406.             Log.d(TAG, "old" + c.x + c.y);
    407.         }
    408.         for (Coordinate c : mShape) {
    409.             Log.d(TAG, "new" + c.x + c.y);
    410.             if (mXTileCount != 0) {
    411.                 setTile(YELLOW_STAR, c.x, c.y);
    412.             }

    413.         }

    414.     }

    415.     // 方块落下后颜色为红色
    416.     private void updateBlew() {
    417.         for (Coordinate c : mShape) {
    418.             if (mXTileCount != 0) {
    419.                 setTile(RED_STAR, c.x, c.y - 1);
    420.                 if (mYTileCount - c.y > highLevel) {
    421.                     highLevel = mYTileCount - c.y;

    422.                 }
    423.             }

    424.         }
    425.         // GAME OVER
    426.         if (highLevel > mYTileCount - 3) {
    427.             setMode(LOSE);
    428.         }
    429.         // 已经消去的行数
    430.         int deleRows = 0;
    431.         for (int i = 1; i <= highLevel + 1; i++) {
    432.             int redCount = 0;
    433.             for (int x = 1; x < mXTileCount - 7; x++) {
    434.                 if (mTileGrid[x][mYTileCount - i] == RED_STAR) {
    435.                     redCount++;
    436.                 }
    437.             }
    438.             // 如果某一行的红色方格数等于列总数,则该列需要消去
    439.             if (redCount == mXTileCount -8 ) {
    440.                 deleRows++;
    441.                 continue;
    442.             } else {
    443.                 // 将不需要消去的行向下移动,移动的幅度取决于前面消去的行数
    444.                 if (deleRows != 0) {
    445.                     for (int x = 1; x < mXTileCount - 6; x++) {
    446.                         mTileGrid[x][mYTileCount - i + deleRows] = mTileGrid[x][mYTileCount - i];
    447.                         mTileGrid[x][mYTileCount - i] = 0;
    448.                     }
    449.                 }
    450.             }
    451.         }
    452.         // 更改分数,一次性消去的行数越多,得到的分数就越多
    453.         switch (deleRows) {
    454.             case 1:
    455.                 mScore = mScore + 100;
    456.                 break;
    457.             case 2:
    458.                 mScore = mScore + 300;
    459.                 break;
    460.             case 3:
    461.                 mScore = mScore + 500;
    462.                 break;
    463.             case 4:
    464.                 mScore = mScore + 800;
    465.                 break;
    466.         }
    467.         // 更新最高分
    468.         if (mScore > historyScore) {
    469.             overhistroy=true;
    470.             historyScore = mScore;
    471.             highestScore.setText("最高分数: \n" + historyScore);
    472.         }
    473.         // 更新当前分
    474.         currentScore.setText("当前分数:\n" + mScore);
    475.         // 当级别达到一定的程度后不再增加方块下落的速度
    476.         if (mScore >= (500 * (gameLevel * 2 - 1))) {
    477.             gameLevel++;
    478.             currentDelay -= 50;
    479.             if (currentDelay < 50)
    480.                 currentDelay = 50;
    481.         }
    482.         // 更新当前级别
    483.         currentLevel.setText("当前级别:\n" + gameLevel);
    484.     }

    485.     /**
    486.      * 设置游戏的状态
    487.      *
    488.      * @param newMode
    489.      */
    490.     public void setMode(int newMode) {
    491.         int oldMode = mMode;
    492.         mMode = newMode;
    493.         if (newMode == RUNNING & oldMode != RUNNING) {
    494.             info.setVisibility(View.INVISIBLE);
    495.             update();
    496.             return;
    497.         }

    498.         Resources res = getContext().getResources();
    499.         CharSequence str = "";
    500.         if (newMode == PAUSE) {
    501.             str = res.getText(R.string.mode_pause);
    502.         }
    503.         if (newMode == READY) {
    504.             str = res.getText(R.string.mode_ready);
    505.         }
    506.         if (newMode == LOSE) {
    507.             str = res.getString(R.string.mode_lose_prefix) + mScore
    508.                     + res.getString(R.string.mode_lose_suffix);
    509.         }
    510.         // 显示提示信息
    511.         info.setText(str);
    512.         info.setVisibility(View.VISIBLE);
    513.     }

    514.     /**
    515.      * 更新预先显示方块
    516.      */
    517.     private void updatePreShape() {
    518.         for (int x = mXTileCount - 4; x < mXTileCount; x++) {
    519.             for (int y = 1; y < 6; y++) {
    520.                 setTile(0, x, y);

    521.             }
    522.         }
    523.         for (Coordinate c : preShape) {
    524.             if (mXTileCount != 0) {
    525.                 setTile(YELLOW_STAR, c.x + mXTileCount - 3, c.y + 2);
    526.             }
    527.         }
    528.     }

    529.     /**
    530.      * 判断方块是否可以移动
    531.      *
    532.      * @param list
    533.      * @return
    534.      */
    535.     private boolean isMoveAble(ArrayList<Coordinate> list) {
    536.         boolean moveAble = true;
    537.         for (Coordinate c : list) {

    538.             if (mTileGrid[c.x][c.y] != GREEN_STAR && mTileGrid[c.x][c.y] != RED_STAR) {
    539.                 continue;
    540.             } else {
    541.                 moveAble = false;
    542.                 break;
    543.             }
    544.         }
    545.         return moveAble;

    546.     }

    547.     /**
    548.      * 判断方块是否可以往下移动
    549.      *
    550.      * @param list
    551.      * @return
    552.      */
    553.     private boolean canMoveDown(ArrayList<Coordinate> list) {
    554.         boolean moveAble = true;
    555.         for (Coordinate c : list) {
    556.             if (c.y < mYTileCount - 1 && mTileGrid[c.x][c.y] != RED_STAR) {
    557.                 continue;
    558.             } else {
    559.                 moveAble = false;
    560.                 break;
    561.             }
    562.         }
    563.         return moveAble;

    564.     }

    565.     /**
    566.      * 画出游戏的背景.即游戏的边框
    567.      *
    568.      */
    569.     private void updateBlackGround() {
    570.         for (int x = 0; x < mXTileCount - 5; x++) {
    571.             setTile(GREEN_STAR, x, 0);
    572.             setTile(GREEN_STAR, x, mYTileCount - 1);
    573.         }
    574.         for (int y = 1; y < mYTileCount - 1; y++) {
    575.             setTile(GREEN_STAR, 0, y);
    576.             setTile(GREEN_STAR, mXTileCount - 6, y);
    577.         }
    578.     }

    579.     /**
    580.      * 根据随机数产生各种形状的方块
    581.      *
    582.      * @param n
    583.      * @return
    584.      */
    585.     private ArrayList<Coordinate> getShape(int n) {
    586.         ArrayList<Coordinate> shape = new ArrayList<Coordinate>();
    587.         switch (n) {
    588.             case 1:
    589.                 // 反Z拐角
    590.                 shape.add(new Coordinate(0, 0));
    591.                 shape.add(new Coordinate(1, 0));
    592.                 shape.add(new Coordinate(0, 1));
    593.                 shape.add(new Coordinate(-1, 1));
    594.                 break;
    595.             case 2:
    596.                 // 正Z拐角
    597.                 shape.add(new Coordinate(0, 0));
    598.                 shape.add(new Coordinate(-1, 0));
    599.                 shape.add(new Coordinate(0, 1));
    600.                 shape.add(new Coordinate(1, 1));
    601.                 break;
    602.             case 3:
    603.                 // 田形
    604.                 shape.add(new Coordinate(0, 0));
    605.                 shape.add(new Coordinate(0, 1));
    606.                 shape.add(new Coordinate(1, 0));
    607.                 shape.add(new Coordinate(1, 1));
    608.                 break;
    609.             case 4:
    610.                 // 长条
    611.                 shape.add(new Coordinate(0, 0));
    612.                 shape.add(new Coordinate(-1, 0));
    613.                 shape.add(new Coordinate(1, 0));
    614.                 shape.add(new Coordinate(2, 0));
    615.                 break;
    616.             case 5:
    617.                 // 长左拐形
    618.                 shape.add(new Coordinate(0, 0));
    619.                 shape.add(new Coordinate(-1, 0));
    620.                 shape.add(new Coordinate(0, 1));
    621.                 shape.add(new Coordinate(0, 2));
    622.                 break;
    623.             case 6:
    624.                 // 长右拐形
    625.                 shape.add(new Coordinate(0, 0));
    626.                 shape.add(new Coordinate(1, 0));
    627.                 shape.add(new Coordinate(0, 1));
    628.                 shape.add(new Coordinate(0, 2));
    629.                 break;
    630.             case 0:
    631.                 // 凸形
    632.                 shape.add(new Coordinate(0, 0));
    633.                 shape.add(new Coordinate(-1, 0));
    634.                 shape.add(new Coordinate(1, 0));
    635.                 shape.add(new Coordinate(0, 1));
    636.                 break;
    637.         }
    638.         return shape;
    639.     }

    640.     public void setTextView(TextView curView, TextView higView, TextView infView, TextView levView) {
    641.         currentScore = curView;
    642.         highestScore = higView;
    643.         info = infView;
    644.         currentLevel = levView;
    645.     }

    646.     /**
    647.      * 用来标示某一坐标上的方块
    648.      *
    649.      */
    650.     private class Coordinate {
    651.         public int x;

    652.         public int y;

    653.         public Coordinate(int newX, int newY) {
    654.             x = newX;
    655.             y = newY;
    656.         }

    657.         public boolean equals(Coordinate other) {
    658.             if (x == other.x && y == other.y) {
    659.                 return true;
    660.             }
    661.             return false;
    662.         }

    663.         public String toString() {
    664.             return "Coordinate: [" + x + "," + y + "]";
    665.         }
    666.     }
    667. }
   4.创建类TileView.java。该类是从贪吃蛇中复制过来的,没做任何修改,可以直接拿来用。可
以在sdk的smples/android 2.x.x/Snake目录中找到。

   5.将贪吃蛇游戏res/drawable目录下的三个图片文件复制到自己工程的res/drawable目录中,如果没有这个目录可以创建或者放在其他的drawable-xxx目录中。

   6.创建布局文件tetris_layout.xml

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <FrameLayout
  3.     xmlns:android="http://schemas.android.com/apk/res/android"
  4.     android:layout_width="match_parent"
  5.     android:layout_height="match_parent">
  6.     <com.test.TetrisView
  7.         android:id="@+id/tetris"
  8.         android:layout_width="match_parent"
  9.         android:layout_height="match_parent"
  10.         tileSize="24" />
  11.     <RelativeLayout
  12.         android:layout_width="match_parent"
  13.         android:layout_height="match_parent">
  14.         <TextView
  15.             android:id="@+id/info"
  16.             android:text="@string/mode_ready"
  17.             android:visibility="visible"
  18.             android:layout_width="wrap_content"
  19.             android:layout_height="wrap_content"
  20.             android:layout_centerInParent="true"
  21.             android:gravity="center_horizontal"
  22.             android:textColor="#ff8888ff"
  23.             android:textSize="20sp" />
  24.         <TextView
  25.             android:id="@+id/current_score"
  26.             android:text="当前分数:\n 0"
  27.             android:visibility="visible"
  28.             android:layout_width="wrap_content"
  29.             android:layout_height="wrap_content"
  30.             android:layout_centerVertical="true"

  31.             android:layout_alignParentRight="true"
  32.             android:textColor="#ff8888ff"
  33.             android:textSize="16sp" />
  34.         <TextView
  35.             android:id="@+id/highest_score"
  36.             android:text="最高分数:\n 0"
  37.             android:visibility="visible"
  38.             android:layout_width="wrap_content"
  39.             android:layout_height="wrap_content"
  40.             android:layout_above="@id/current_score"
  41.             android:layout_alignParentRight="true"
  42.             android:textColor="#ff8888ff"
  43.             android:textSize="16sp" />

  44.         <TextView
  45.             android:id="@+id/current_level"
  46.             android:text="当前级别:\n 1"
  47.             android:visibility="visible"
  48.             android:layout_width="wrap_content"
  49.             android:layout_height="wrap_content"
  50.             android:layout_below="@id/current_score"
  51.             android:layout_alignParentRight="true"
  52.             android:textColor="#ff8888ff"
  53.             android:textSize="16sp" />

  54.     </RelativeLayout>
  55. </FrameLayout>
  7.打开res/values/strings.xml,将其修改为以下内容:

 
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <resources>

  3.     <string
  4.         name="app_name">Tetris</string>
  5.     <string
  6.         name="mode_ready">\n按上开始游戏 </string>
  7.     <string
  8.         name="mode_pause"> 暂停\n按上继续游戏 </string>
  9.     <string
  10.         name="mode_lose_prefix">Game Over\nScore: </string>
  11.     <string
  12.         name="mode_lose_suffix">\n按上重新开始游戏 </string>
  13. </resources>
    8.打开AndroidManifest.xml,修改其内容如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest
  3.     xmlns:android="http://schemas.android.com/apk/res/android"
  4.     package="com.test"
  5.     android:versionCode="1"
  6.     android:versionName="1.0">
  7.     <application
  8.         android:icon="@drawable/icon"
  9.         android:label="@string/app_name">
  10.         <activity
  11.             android:name=".Tetris"
  12.             android:theme="@android:style/Theme.NoTitleBar"
  13.             android:screenOrientation="portrait"
  14.             android:configChanges="keyboardHidden|orientation">
  15.             <intent-filter>
  16.                 <action
  17.                     android:name="android.intent.action.MAIN" />
  18.                 <category
  19.                     android:name="android.intent.category.LAUNCHER" />
  20.             </intent-filter>
  21.         </activity>

  22.     </application>
  23. </manifest>
    至此,一个简单的俄罗斯方块游戏就完成了,游戏运行时的图片为:



    项目源码请移步






    
   







阅读(16128) | 评论(2) | 转发(0) |
给主人留下些什么吧!~~

云少嘎嘎嘎2015-12-08 09:15:09

cc13926838599:您好,可以分享这整个项目所有资源给我吗?我是个初学者

源码已经附上了

回复 | 举报

cc139268385992015-12-07 13:34:16

您好,可以分享这整个项目所有资源给我吗?我是个初学者