android小游戏——2048 核心功能的实现与简单的代码重构(2)

来源:互联网 发布:mac air qq 远程桌面 编辑:程序博客网 时间:2024/06/03 22:56

上一篇博文,我们实现了初步的左滑,那我们这一篇就把上下右滑的代码给写了,再加一个输赢的判断条件(其实原版2048也就只有输)。

2.右滑(紧接上一篇博文)

由于原理跟左滑一模一样我在这里就直接贴代码了。

private void moveToRight() {//右滑        for (int i = 0; i < 4; i++) {            for (int j = 3; j >= 0; j--) {                int n = 0;//若0后面全为0则下面的while会死循环,设置n表示顶多一行只有4个0                while (tvNum[i][j] == 0 && n < 4) {//若检测到哪一位为0则把这一位放到最右边那一列                    n++;                    for (int k = j; (k - 1) >= 0; k--) {//简单的冒泡,把0浮上来                        int temp = 0;                        temp = tvNum[i][k];                        tvNum[i][k] = tvNum[i][k - 1];                        tvNum[i][k - 1] = temp;                    }                }            }        }        for (int i = 0; i < 4; i++) {//相同数相加            for (int j = 3; (j - 1) >= 0; j--) {                if (tvNum[i][j] == tvNum[i][j - 1] && tvNum[i][j] != 0) {//两数相同且不为0                    tvNum[i][j] *= 2;                    tvNum[i][j - 1] = 0;                    for (int k = j - 1; (k - 1) >= 0; k--) {//把第二个数置0冒泡法移到最后                        int temp = 0;                        temp = tvNum[i][k];                        tvNum[i][k] = tvNum[i][k - 1];                        tvNum[i][k - 1] = temp;                    }                }            }        }        boolean ifEmpty = false;        for (int i = 0; i < 4; i++) {            for (int j = 0; j < 4; j++) {                if (tvNum[i][j] == 0) {                    ifEmpty = true;                }            }        }        if (ifEmpty) {            int randomXY;            randomXY = (int) Math.round(Math.random() * 15);            while (tvNum[randomXY / 4][randomXY % 4] != 0) {                randomXY = (int) Math.round(Math.random() * 15);            }            tvNum[randomXY / 4][randomXY % 4] = ((int) Math.round(Math.random() + 1)) * 2;        }        for (int i = 0; i < 4; i++) {//把更新之后的tvNum在游戏界面上显示出来            for (int j = 0; j < 4; j++) {                if (tvNum[i][j] == 0) {//0则不显示数值                    textViews[4 * i + j].setText("");                } else {                    textViews[4 * i + j].setText(tvNum[i][j] + "");                }            }        }    }
3.上滑

上下滑动跟左右滑动有一些区别,主要在于矩阵的遍历方式。左右滑动矩阵是横向遍历,那么我们的上下滑动就应该是纵向遍历。而且由于2048游戏的特性,矩阵必定为方阵,所以不需要担心由于Java的二维数组可以不等长而引起的数组越界。下面就是上滑的代码:

private void moveToTop() {//上滑        for (int j = 0; j < 4; j++) {            for (int i = 0; i < 4; i++) {                int n = 0;//若0后面全为0则下面的while会死循环,设置n表示顶多一行只有4个0                while (tvNum[i][j] == 0 && n < 4) {//若检测到哪一位为0则把这一位放到最下面那一行                    n++;                    for (int k = i; (k + 1) < 4; k++) {//简单的冒泡,把0浮上来                        int temp = 0;                        temp = tvNum[k][j];                        tvNum[k][j] = tvNum[k + 1][j];                        tvNum[k + 1][j] = temp;                    }                }            }        }        for (int j = 0; j < 4; j++) {//相同数相加            for (int i = 0; (i + 1) < 4; i++) {                if (tvNum[i][j] == tvNum[i + 1][j] && tvNum[i][j] != 0) {//两数相同且不为0                    tvNum[i][j] *= 2;                    tvNum[i + 1][j] = 0;                    for (int k = i + 1; (k + 1) < 4; k++) {//把第二个数置0冒泡法移到最后                        int temp = 0;                        temp = tvNum[k][j];                        tvNum[k][j] = tvNum[k + 1][j];                        tvNum[k + 1][j] = temp;                    }                }            }        }        boolean ifEmpty = false;        for (int i = 0; i < 4; i++) {            for (int j = 0; j < 4; j++) {                if (tvNum[i][j] == 0) {                    ifEmpty = true;                }            }        }        if (ifEmpty) {            int randomXY;            randomXY = (int) Math.round(Math.random() * 15);            while (tvNum[randomXY / 4][randomXY % 4] != 0) {                randomXY = (int) Math.round(Math.random() * 15);            }            tvNum[randomXY / 4][randomXY % 4] = ((int) Math.round(Math.random() + 1)) * 2;        }        for (int i = 0; i < 4; i++) {//把更新之后的tvNum在游戏界面上显示出来            for (int j = 0; j < 4; j++) {                if (tvNum[i][j] == 0) {//0则不显示数值                    textViews[4 * i + j].setText("");                } else {                    textViews[4 * i + j].setText(tvNum[i][j] + "");                }            }        }    }
4.下滑
 private void moveToBottom() {//下滑        for (int j = 0; j < 4; j++) {            for (int i = 3; i >= 0; i--) {                int n = 0;//若0后面全为0则下面的while会死循环,设置n表示顶多一行只有4个0                while (tvNum[i][j] == 0 && n < 4) {//若检测到哪一位为0则把这一位放到最上面那一行                    n++;                    for (int k = i; (k - 1) >= 0; k--) {//简单的冒泡,把0浮上来                        int temp = 0;                        temp = tvNum[k][j];                        tvNum[k][j] = tvNum[k - 1][j];                        tvNum[k - 1][j] = temp;                    }                }            }        }        for (int j = 0; j < 4; j++) {//相同数相加            for (int i = 3; (i - 1) >= 0; i--) {                if (tvNum[i][j] == tvNum[i - 1][j] && tvNum[i][j] != 0) {//两数相同且不为0                    tvNum[i][j] *= 2;                    tvNum[i - 1][j] = 0;                    for (int k = i - 1; (k - 1) >= 0; k--) {//把第二个数置0冒泡法移到最后                        int temp = 0;                        temp = tvNum[k][j];                        tvNum[k][j] = tvNum[k - 1][j];                        tvNum[k - 1][j] = temp;                    }                }            }        }        boolean ifEmpty = false;        for (int i = 0; i < 4; i++) {            for (int j = 0; j < 4; j++) {                if (tvNum[i][j] == 0) {                    ifEmpty = true;                }            }        }        if (ifEmpty) {            int randomXY;            randomXY = (int) Math.round(Math.random() * 15);            while (tvNum[randomXY / 4][randomXY % 4] != 0) {                randomXY = (int) Math.round(Math.random() * 15);            }            tvNum[randomXY / 4][randomXY % 4] = ((int) Math.round(Math.random() + 1)) * 2;        }        for (int i = 0; i < 4; i++) {//把更新之后的tvNum在游戏界面上显示出来            for (int j = 0; j < 4; j++) {                if (tvNum[i][j] == 0) {//0则不显示数值                    textViews[4 * i + j].setText("");                } else {                    textViews[4 * i + j].setText(tvNum[i][j] + "");                }            }        }    }

哇说实话代码写到这里,我是真的忍不住要重构这坨又臭又长的代码了。虽然还有一些功能没实现,但没事,我们重构完再写。

重构垃圾代码

1.分离代码

我写完下滑代码以后,我的GameActivity已经整整322行了!!!真是受不了,于是我决定把跟Activity无关的代码分离开来。新建一个MyOper.java文件,里边用来放我们的上下左右滑动的代码。对了把setXY也放进去吧。
直接剪切所有的滑动和setXY代码到这里面:
这里写图片描述
虽然会有很多错误,我们一步一步来解决他们。

  1. 把我们之前定义的数组放进MyOper.java里面
  2. 把GameActivity的onCreate方法里面的对TextView的绑定代码也封装成一个函数init放入MyOper中。
  3. 定义一个MyOper的成员变量private Activity activity;
  4. 然后定义MyOper的构造函数,
    MyOper(Activity activity)
    {
    this.activity = activity;
    }

    用来获取Activity对象。再在init()的findViewById之前加上activity:
    void init()    {        for (int i = 0; i < 16; i++) {//绑定所有TextView            textViews[i] = (TextView) activity.findViewById(tvId[i]);            textViews[i].setText("");        }    }
  1. 接着就只要在GameActvity里初始化一个MyOper对象,然后在各个方法原来的位置调用对象的方法就可以。
    GameActivity
package com.example.a2048;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.util.Log;import android.view.MotionEvent;public class GameActivity extends AppCompatActivity {    int downX = 0;//按下时候的坐标    int downY = 0;    MyOper oper;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_game);        oper = new MyOper(this);        oper.init();        int initNum1, initNum2;//初始化位置        initNum1 = (int) Math.round(Math.random() * 15);//生成0~15随机整数        initNum2 = (int) Math.round(Math.random() * 15);        while (initNum1 == initNum2) {            initNum1 = (int) Math.round(Math.random() * 15);            initNum2 = (int) Math.round(Math.random() * 15);        }        oper.setXY(initNum1, 2);        oper.setXY(initNum2, 2);    }    @Override    public boolean onTouchEvent(MotionEvent event) {//监听触摸事件        switch (event.getAction()) {            case MotionEvent.ACTION_DOWN:                downX = (int) event.getRawX();                downY = (int) event.getRawY();                break;            case MotionEvent.ACTION_MOVE:                break;            case MotionEvent.ACTION_UP:                int upX = (int) event.getRawX();                int upY = (int) event.getRawY();                if (Math.abs(upX - downX) > Math.max(2 * Math.abs(upY - downY), 100)) {                    if (upX > downX) {                        oper.moveToRight();                    } else {                        oper.moveToLeft();                    }                } else if (Math.abs(upY - downY) > Math.max(2 * Math.abs(upX - downX), 100)) {                    if (upY > downY) {                        oper.moveToBottom();                    } else {                        oper.moveToTop();                    }                } else {                    Log.d("111", "不滑动");                }                break;        }        return true;    }}

到现在应该已经消除所有的错误了,打开模拟器跑一下我们的游戏,确保我们做的改动没有出错。

确保自己能够继续跑下去,我们就接着重构。如果自己的程序有问题,我建议先解决一下,不要硬着头皮往下改了。接着我们就把上下左右滑动中相同的代码抽离出来封装成方法。那么哪些代码是一样的呢?

  1. 每次平移后随机生成2或者4的代码。
  2. 平移完之后根据tvNum数值矩阵来调整textViews的显示数值的代码。

1的代码抽离出来叫做produce2_4(),2抽离出来叫做showText().

 void produce2_4() {     boolean ifEmpty = false;     for (int i = 0; i < 4; i++) {         for (int j = 0; j < 4; j++) {             if (tvNum[i][j] == 0) {                 ifEmpty = true;             }         }     }     if (ifEmpty) {         int randomXY;         randomXY = (int) Math.round(Math.random() * 15);         while (tvNum[randomXY / 4][randomXY % 4] != 0) {             randomXY = (int) Math.round(Math.random() * 15);         }         tvNum[randomXY / 4][randomXY % 4] = ((int) Math.round(Math.random() + 1)) * 2;     } } void showText() {     for (int i = 0; i < 4; i++) {//把更新之后的tvNum在游戏界面上显示出来         for (int j = 0; j < 4; j++) {             if (tvNum[i][j] == 0) {//0则不显示数值                 textViews[4 * i + j].setText("");             } else {                 textViews[4 * i + j].setText(tvNum[i][j] + "");             }         }     } }

回过头来看看我们的setXY()方法似乎用处也不大,只在刚开始的时候用了一下。那我们就把这个方法删除掉,然后把GameActivity的生成initNum1, initNum2放在MyOper的init方法里面。
做到现在,我们成功地初步重构了一下我们的代码。虽然还是有很多地方可以再修改,但相比于一开始肯定是简约和清晰了好多。
MyOper.java
这里写图片描述
GameActivity.java
这里写图片描述

好,我们接着写功能。一个是在每次滑动之后判断当前矩阵能否滑动(即游戏是否结束),我们在MyOper里面新加一个ifLose()方法。
判断能否滑动

boolean ifLose() {    for (int i = 0; i < 4; i++) {//横向比较是否有邻近的两个数字相同        for (int j = 0; (j + 1) < 4; j++) {            if (tvNum[i][j] == tvNum[i][j + 1]) {                return false;            }        }    }    for (int j = 0; j < 4; j++) {//纵向比较是否有邻近的两个数字相同        for (int i = 0; (i + 1) < 4; i++) {            if (tvNum[i][j] == tvNum[i + 1][j]) {                return false;            }        }    }    return true;}

考虑我们每次判断都是在滑动之后,所以把是否结束游戏的判断放在produce2_4()方法里面

if (ifEmpty) {...          if (ifLose()) {              Toast.makeText(activity, "游戏结束", Toast.LENGTH_SHORT).show();          }}

再有一个就是我们随机产生2或者4必须在有效滑动之后,所以在滑动的时候需要加个判断,到底矩阵有没有变换。
以左滑作为例子:

 void moveToLeft() {//左滑        boolean ifSlide = false;        for (int i = 0; i < 4; i++) {            for (int j = 0; j < 4; j++) {                int n = 0;//若0后面全为0则下面的while会死循环,设置n表示顶多一行只有4个0                while (tvNum[i][j] == 0 && n < 4) {//若检测到哪一位为0则把这一位放到最右边那一列                    n++;                    for (int k = j; (k + 1) < 4; k++) {//简单的冒泡,把0沉下去                        if (tvNum[i][k + 1] != 0) {//只要不是都是0则一定进行了有效滑动                            ifSlide = true;                        }                        int temp = 0;                        temp = tvNum[i][k];                        tvNum[i][k] = tvNum[i][k + 1];                        tvNum[i][k + 1] = temp;                    }                }            }        }        for (int i = 0; i < 4; i++) {//相同数相加            for (int j = 0; (j + 1) < 4; j++) {                if (tvNum[i][j] == tvNum[i][j + 1] && tvNum[i][j] != 0) {//两数相同且不为0                    ifSlide = true;//有相加操作则一定进行了有效滑动                    tvNum[i][j] *= 2;                    tvNum[i][j + 1] = 0;                    for (int k = j + 1; (k + 1) < 4; k++) {//把第二个数置0冒泡法移到最后                        int temp = 0;                        temp = tvNum[i][k];                        tvNum[i][k] = tvNum[i][k + 1];                        tvNum[i][k + 1] = temp;                    }                }            }        }        if (ifSlide) {            produce2_4();            showText();        }    }

右滑上滑下滑大家就自己写一下吧。

到现在为止我们可算是真正意义上的写完了2048这个游戏的核心代码。这个游戏已经可以玩了,大家可以自己玩个几盘,看看有没有什么bug。我们下一篇来写得分功能,以及界面的一些美化。

0 0