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代码到这里面:
虽然会有很多错误,我们一步一步来解决他们。
- 把我们之前定义的数组放进MyOper.java里面
- 把GameActivity的onCreate方法里面的对TextView的绑定代码也封装成一个函数init放入MyOper中。
- 定义一个MyOper的成员变量
private Activity activity;
- 然后定义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(""); } }
- 接着就只要在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; }}
到现在应该已经消除所有的错误了,打开模拟器跑一下我们的游戏,确保我们做的改动没有出错。
确保自己能够继续跑下去,我们就接着重构。如果自己的程序有问题,我建议先解决一下,不要硬着头皮往下改了。接着我们就把上下左右滑动中相同的代码抽离出来封装成方法。那么哪些代码是一样的呢?
- 每次平移后随机生成2或者4的代码。
- 平移完之后根据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。我们下一篇来写得分功能,以及界面的一些美化。
- android小游戏——2048 核心功能的实现与简单的代码重构(2)
- android小游戏——2048 核心功能的实现(1)
- Android小游戏2048的简单实现
- Android 小游戏2048 代码简单实现
- 简单的小游戏---代码实现三子棋
- Android手机重启的核心代码
- Android 小游戏之五子棋 代码简单实现
- android代码重构日记(四)——关于按钮部分的代码重构
- 小游戏——2048的简易实现
- Android Widget实现重启的功能
- 登录注册功能的实现,核心的验证代码
- JS代码的简单重构与优化
- 核心代码的实现
- Android游戏——学习小结(一个简单的设计小游戏)动画射击
- Android游戏——学习小结(一个简单的设计小游戏)动画射击
- 简单的实现计算机功能代码如下
- AOP简单的核心代码
- S代码的简单重构与优化(适合新手)
- NYOJ 整数划分
- RANSAC算法总结
- 前端 mac 上编程软件 编程软件
- 极大似然估计
- SpringMVC学习总结(五)——转发和重定向
- android小游戏——2048 核心功能的实现与简单的代码重构(2)
- web.py框架
- IAP 协议
- 使用junit测试初步(1)
- 剑指offer-23.二叉搜索树的后序遍历序列
- 打造vim成为python IDE, 支持自动补全, 定义跳转, 按PEP8规范自动缩进和代码格式化
- POJ-1159-Palindrome(简单dp)
- PHP知识小记
- Codeforces 7E Defining Macros 题解