最近觉得在android上开发游戏是一件很有趣的事情,所以查找了一些资料,自己做出了一个简单俄罗斯方块游戏,现将其总结如下:

1、基础数据模型

一个俄罗斯方块都是以一个4*4的二维数组来存储的,在我的demo游戏中,一共有7种方块类型: IS ZJ O L T七种类型。见下图:



每种类型都有四种旋转状态,如何在一个4*4的二位数组表示呢?可以用0和1来表示,1表示该小单元格需要显示,0表示该小单元格不需要显示。
设置屏幕为320*480,每个小单元格是20像素,这样宽就是COLS=16,高就是ROWS=24。

2、涉及到的几个JavaBean

2.1 Shape:把每种图形抽象出来为一个对象,具有的属性是

int的 left(x方向)和top(y方向)、

int的 status 旋转状态,可选择的值是0,1,2,3 默认是0

int[][] body存储四种旋转状态的数据

int LEFT = 1, RIGHT = 2, DOWN = 3, ROTATE = 4

Shape具有的方法是moveLeft() moveRight() moveDown()和rotate()四种基本方法,对应模拟器上的四个方向键。如果模拟器的方向键不能使用,自己在百度或google上看看是怎么解决的,这里不记录。

同时Shape还应该具有drawMe()画出值为1的单元格、checkBound()检查是否出了边界 和 checkValue(x, y)检查对应的小单元格的值是否为1或0, 1显示0不显示。

2.2 Ground:地面类 就是各个小单元格堆放起来的那个地面,我们也把它抽象成为一个JavaBean,经过分析,它也应该具有如下的属性和方法。

int[][] body 这个body就是面板的最大面积COLS * ROWS =16*24

它应该有一个drawMe()的方法,显示他目前已经接纳且未消行的小单元格,另外它应该有消行和统计分数的方法removeRows(),检查某个小单元格是否为1的方法checkValue(),接纳Shape的方法addShape()。

2.3 定义一个GameView,用来显示Shape和Ground的图形变化,需要重写的方法有

onDraw():主要是画Shape和Ground

onKeyDown():处理方向键按下的事件

在它的构造函数里面需要实现小单元格的静态资源的获取,

调用setFocusable(true); 以显示图形

在正式运行时需要执行定时器,以3秒钟不停刷新页面的数据。

2.4 MainActivity 将系统的标题栏去掉,调用

//以下去掉标题和全屏
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);

然后调用GameView。

===================================================================

按照以上的步骤,我开始逐步编程

1、ShapeFactory的代码

import java.util.Random;public class ShapeFactory {public static Shape getShape(){Shape shape = new Shape();int type = new Random().nextInt(shapes.length);shape.setBody(shapes[type]);shape.setStatus(0);return shape;}// 方块的形状 第一组代表方块类型有S、Z、L、J、I、O、T 7种// 第二组为图型的型状态//每三组图型数据,根据这些数来描绘在view中private final static int shapes[][][] = new int[][][] {   // i   { { 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 },     { 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0 },     { 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 },     { 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0 } },   // s   { { 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },     { 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },     { 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },     { 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 } },   // z   { { 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },     { 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 },     { 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },     { 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 } },   // J   { { 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0 },     { 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },     { 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 },     { 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },   // o(田字)   { { 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },     { 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },     { 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },     { 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },   // l(竖右勾)   { { 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0 },     { 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },     { 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },     { 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },   // T   { { 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },     { 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },     { 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },     { 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0 } } };}

配置文件

public class Config {public static final int CELL_SIZE = 20;//320*480  宽 *高public static final int ROWS = 24;//行--高public static final int COLS = 16;//列 --宽}

2、Shape的代码

import android.graphics.Canvas;import android.graphics.drawable.Drawable;public class Shape {private int left;private int top;private int[][] body;private int status;public static final int LEFT = 1, RIGHT = 2, DOWN = 3, ROTATE = 4;public void moveLeft(){left--;}public void moveRight(){left++;}//move down//must controller multiple thread(1-refresh schedule, 2-event) public synchronized void moveDown(Ground ground) {   if (checkBound(Shape.DOWN, ground))    top ++;  }//旋转其实就是转到下一个图形中,通过改变status的值即可public void rotate(){status = (status+1)%body.length;}/**绘制Shape需要传入画布Canvas和图片资源**/public void drawMe(Canvas canvas, Drawable d){for(int x=0; x<4; x++){for(int y=0; y<4; y++){if(checkValue(x,y)){//如果单元格的值不为0则为1,需要绘制单元格//绘制之前需要先设置边界,设置边界有两种方法,这里选择四个参数的//这四个参数需要计算出其绝对坐标值int tempx = (x+left)*Config.CELL_SIZE;int tempy = (y+top)*Config.CELL_SIZE;int right = tempx+Config.CELL_SIZE;int bottom = tempy+Config.CELL_SIZE;d.setBounds(tempx, tempy, right, bottom);d.draw(canvas);}}}}//检查Shape是否到达边界,这里需要考虑向左 向右和向下三种情况public boolean checkBound(int action, Ground ground){int temptop = top;int templeft = left;switch(action){case LEFT:templeft--;break;case RIGHT:templeft++;break;case DOWN:temptop++;break;case ROTATE:break;}for(int x=0; x<4; x++){for(int y=0; y<4; y++){if(checkValue(x,y)){//如果单元格的值为1,需要判断它是否到达边界if(templeft<0 || templeft >Config.COLS //X方向的边界判断|| temptop > Config.ROWS //Y方向的边界判断|| ground.checkValue(templeft,temptop,x,y) ){return false;}//end if}}}//end forreturn true;}//判断x,y点的值是否为1,1返回true 0返回falsepublic boolean checkValue(int x, int y){return body[status][4*y+x]==1;}public int[][] getBody() {return body;}public void setBody(int[][] body) {this.body = body;}public int getStatus() {return status;}public void setStatus(int status) {this.status = status;}

3、 Ground的代码

import android.graphics.Canvas;import android.graphics.drawable.Drawable;public class Ground {public boolean checkValue(int left, int top, int x, int y) {if (body[left + x][top + y] == 1) {  return true;    }    return false;}   public int[][] body = new int[Config.COLS][Config.ROWS];public void drawMe(Canvas canvas, Drawable d) {for (int x = 0; x < Config.COLS; x++) {//列 --> X方向for (int y = 0; y < Config.ROWS; y++) {//行--> Y方向if (body[x][y] == 1) {int tempX = x * Config.CELL_SIZE;int tempY = y * Config.CELL_SIZE;            int right = tempX + Config.CELL_SIZE;            int bottom = tempY + Config.CELL_SIZE;            d.setBounds(tempX, tempY, right, bottom);            d.draw(canvas);        }   }    }   }/** * remember drawed block  * @param type  block type(seven type)   * @param status block status * @param left   x coordinate  * @param top   y coordinate */ public void add(int left, int top, Shape shape) { for (int x = 0; x < 4; x++) { for (int y = 0; y < 4; y++) { if (shape.checkValue(x, y)){ try{ body[left + x][top + y] = 1;   } catch(Exception e) { e.printStackTrace(); }//end try-catch }//end if }//end for loop }//end for loop}  /**     * remove rows 当所堆积的小框框满一行时,就要进行消行。     * 消行的逻辑是:以Y方向为基准,从最底下一行的开始检查,当某一行全部满格时,则进行消行     * 消行其实就是把上一行的数据填充到下一行的位置上,以此进行循环操作     */public void removeRow() {//消掉多少行int c = 0;    int removeRows = 0;//counter     for (int y = Config.ROWS - 1; y > 0; y--) {//y coordinate    for (int x = 0; x < Config.COLS; x++) {//x coordinate    if (body[x][y] == 1){    c++;            if (c == Config.COLS) {//should remove the row            removeRows++;            for (int j = y; j > 0; j--) {//reset all block in body y coordinate            for (int z = 0; z < Config.COLS; z++) {//x coordinate            body[z][j] = body[z][j - 1];            }//end for loop            }//end for loop            y++;//because the row has fall down one row.            }//end if    }//end if    }//end for    c = 0;    }//end for    countScore(removeRows);}   /** * count score统计分数 * @param removeRows */public void countScore(int removeRows) {}}

4、GameView

import java.util.Timer;import java.util.TimerTask;import android.content.Context;import android.graphics.Canvas;import android.graphics.drawable.Drawable;import android.view.KeyEvent;import android.view.View;public class GameView extends View {private Shape shape;private Drawable d;Ground ground = new Ground();public GameView(Context context) {super(context);d = context.getResources().getDrawable(R.drawable.brick);shape = ShapeFactory.getShape();//new Thread(this).start();setFocusable(true);schedule();}/*** onDraw()方法只会在初始化后被调用一次,或者在使用postInvalidate()的时候会被调用*/@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);shape.drawMe(canvas, d);ground.drawMe(canvas, d);}@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event) {switch (keyCode) {   case KeyEvent.KEYCODE_DPAD_UP:    shape.rotate();    break;  case KeyEvent.KEYCODE_DPAD_DOWN:   if(shape.checkBound(Shape.DOWN, ground))shape.moveDown(ground);     break;   case KeyEvent.KEYCODE_DPAD_LEFT:  if(shape.checkBound(Shape.LEFT,ground))shape.moveLeft();    break;   case KeyEvent.KEYCODE_DPAD_RIGHT:   if(shape.checkBound(Shape.RIGHT, ground))shape.moveRight();    break;   default:    break;   }    return super.onKeyDown(keyCode, event);  }private void schedule(){ Timer nT1 = new Timer(); nT1.schedule(new TimerTask(){ //计划运行时间间隔 public void run(){ refresh(); //过3秒调用一下refresh()         }     },0,500);} public void refresh(){    //auto down    if (shape.checkBound(Shape.DOWN, ground)){     shape.moveDown(ground);     this.postInvalidate(); //使用postInvalidate();刷新    } else{//generator new block         ground.add(shape.left, shape.top, shape);         ground.removeRow();         shape = ShapeFactory.getShape();    }      } /*@Overridepublic void run() {while(true){if(shape.checkBound(Shape.DOWN))shape.moveDown();postInvalidate();try {Thread.sleep(500);} catch (Exception e) {e.printStackTrace();}}}*/}

6、MainActivity

import android.app.Activity;import android.os.Bundle;import android.view.Window;import android.view.WindowManager;public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//setContentView(R.layout.activity_main);      //以下去掉标题和全屏        requestWindowFeature(Window.FEATURE_NO_TITLE);        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,           WindowManager.LayoutParams.FLAG_FULLSCREEN);        GameView gameView = new GameView(this);        setContentView(gameView);}}

6、

更多相关文章

  1. 将VitamioBundle工程作为Android(安卓)Library引入Demo工程使用
  2. Android应用开发笔记(4):再探Android多应用间数据共享机制,自定义C
  3. Android(安卓)Activity启动流程分析--------基于Android(安卓)O
  4. Android(安卓)MVP模式 初步理解
  5. 第八章 网络的时代—网络开发(3)
  6. Android进阶知识树——Android(安卓)多进程、Binder 你必须知道
  7. android解析XML文件的三方法之DOM
  8. Android调用天气接口(和风天气)
  9. Android(安卓)界面滑动实现---Scroller类 从源码和开发文档中学

随机推荐

  1. android:imeOptions
  2. Android短信数据库简析
  3. linux eclipse中运行android AVD 错误
  4. 记录EditText的输入属性InputType以及ime
  5. Android 3.0 r1 API中文文档(108) —— E
  6. Kotlin log 工具类
  7. android简单的乘法运算
  8. Android手动显示和隐藏软键盘
  9. AS Layout布局
  10. Hierarchy Viewer