本次教程跟之前隔了不少时间哈,有点忘记了的建议先看看前面的熟悉下,今天我准备把这个2048给结束了,拖了这么久。
按照惯例,我们已经把准备工作都做好了,今天这一部分信息量比较大,也是整个游戏的核心所在,所以我准备分功能来讲,最后大家结合源码来看就不会感觉太吃力了。
1、初始化游戏
初始化的时候,我们要干嘛呢,首先要看配置,配置了几行,然后先画好面板,然后要给在面板上随机生成2个数字Item,这涉及到2个方法,一个是初始化面板,一个是添加随机数字
- private void initGameView(int cardSize) {
- removeAllViews();
- GameItem card;
- for (int i = 0; i < gameLines; i++) {
- for (int j = 0; j < gameLines; j++) {
- card = new GameItem(getContext(), 0);
- addView(card, cardSize, cardSize);
- // 初始化GameMatrix全部为0 空格List为所有
- gameMatrix[i][j] = card;
- blanks.add(new Point(i, j));
- }
- }
- // 添加随机数字
- addRandomNum();
- addRandomNum();
- }
- private void addRandomNum() {
- getBlanks();
- if (blanks.size() > 0) {
- int randomNum = (int) (Math.random() * blanks.size());
- Point randomPoint = blanks.get(randomNum);
- gameMatrix[randomPoint.x][randomPoint.y].setNum(Math.random() > 0.2d ? 2 : 4);
- Game.getGameActivity().getAnimationLayer().animcreate(gameMatrix[randomPoint.x][randomPoint.y]);
- }
- }
在判断生成2、4的时候,我们使用了通常产生范围随机数的方法,同时指定2和4的比例在1:4,当然这个大家可以根据需要更改
2、具体参数等初始化
这个比较简单了,直接上代码,大家应该都看得懂
- private void initGameMatrix() {
- // 初始化矩阵
- removeAllViews();
- scoreHistory = 0;
- Config.Scroe = 0;
- Config.GameLines = Config.sp.getInt(Config.KEY_GameLines, 4);
- gameLines = Config.GameLines;
- gameMatrix = new GameItem[gameLines][gameLines];
- gameMatrixHistory = new int[gameLines][gameLines];
- calList = new ArrayList<Integer>();
- blanks = new ArrayList<Point>();
- highScore = Config.sp.getInt(Config.KEY_HighScore, 0);
- setColumnCount(gameLines);
- setRowCount(gameLines);
- setOnTouchListener(this);
- // 初始化View参数
- DisplayMetrics metrics = new DisplayMetrics();
- WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
- Display display = wm.getDefaultDisplay();
- display.getMetrics(metrics);
- Config.ItemSize = metrics.widthPixels / Config.GameLines;
- initGameView(Config.ItemSize);
- }
这部分最好还是结合源码看比较好,注释清晰
3、触摸事件
触摸的时候判断4个方向,这个基本也是通用的写法了,不多说了
- public boolean onTouch(View v, MotionEvent event) {
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- saveHistoryMatrix();
- startX = (int) event.getX();
- startY = (int) event.getY();
- break;
- case MotionEvent.ACTION_MOVE:
- break;
- case MotionEvent.ACTION_UP:
- endX = (int) event.getX();
- endY = (int) event.getY();
- judgeDirection(endX - startX, endY - startY);
- if (isMoved()) {
- addRandomNum();
- // 修改显示分数
- Game.getGameActivity().setScore(Config.Scroe, 0);
- }
- int result = checkCompleted();
- if (result == 0) {
- if (Config.Scroe > highScore) {
- Editor editor = Config.sp.edit();
- editor.putInt(Config.KEY_HighScore, Config.Scroe);
- editor.commit();
- }
- Toast.makeText(getContext(), "lose", Toast.LENGTH_LONG).show();
- Config.Scroe = 0;
- } else if (result == 2) {
- Toast.makeText(getContext(), "success", Toast.LENGTH_LONG).show();
- Config.Scroe = 0;
- }
- break;
- default:
- break;
- }
- return true;
- }
- private void judgeDirection(int offsetX, int offsetY) {
- if (Math.abs(offsetX) > Math.abs(offsetY)) {
- if (offsetX > 10) {
- swipeRight();
- } else {
- swipeLeft();
- }
- } else {
- if (offsetY > 10) {
- swipeDown();
- } else {
- swipeUp();
- }
- }
- }
下面我们来讲2048的终极奥义了,就是算法的实现,具体的算法在第一篇中已经讲解了大概的过程, 点我去看 ,下面我们选一个方向来讲如何实现
- private void swipeLeft() {
- for (int i = 0; i < gameLines; i++) {
- for (int j = 0; j < gameLines; j++) {
- int currentNum = gameMatrix[i][j].getNum();
- if (currentNum != 0) {
- if (keyItemNum == -1) {
- keyItemNum = currentNum;
- } else {
- if (keyItemNum == currentNum) {
- calList.add(keyItemNum * 2);
- Config.Scroe += keyItemNum * 2;
- keyItemNum = -1;
- } else {
- calList.add(keyItemNum);
- keyItemNum = currentNum;
- }
- }
- } else {
- continue;
- }
- }
- if (keyItemNum != -1) {
- calList.add(keyItemNum);
- }
- // 改变Item值
- for (int j = 0; j < calList.size(); j++) {
- gameMatrix[i][j].setNum(calList.get(j));
- }
- for (int m = calList.size(); m < gameLines; m++) {
- gameMatrix[i][m].setNum(0);
- }
- // 重置行参数
- keyItemNum = -1;
- calList.clear();
- }
- }
概括来说,就是选取基准,挨个比较,重新排列
代码很清晰,大家看看就知道了,关键是如何总结出这个算法。
4、下面就是判断游戏什么时候需要新加入一个数字Item
当当前的数字矩阵结构域上次的结构发生差别的时候,我们就要add一个新的数字Item了
- private boolean isMoved() {
- for (int i = 0; i < gameLines; i++) {
- for (int j = 0; j < gameLines; j++) {
- if (gameMatrixHistory[i][j] != gameMatrix[i][j].getNum()) {
- return true;
- }
- }
- }
- return false;
- }
这个地方我们使用了一个历史矩阵来存储上一次的数字矩阵
5、最后就是如何判断结束了
如果当前还有空格的Item,则必定没有结束,若相邻的数字都没有相同的数字,则必定结束,若出现配置的Goal,则赢了
- private int checkCompleted() {
- getBlanks();
- if (blanks.size() == 0) {
- for (int i = 0; i < gameLines; i++) {
- for (int j = 0; j < gameLines; j++) {
- if (j < gameLines - 1) {
- if (gameMatrix[i][j].getNum() == gameMatrix[i][j + 1].getNum()) {
- return 1;
- }
- }
- if (i < gameLines - 1) {
- if (gameMatrix[i][j].getNum() == gameMatrix[i + 1][j].getNum()) {
- return 1;
- }
- }
- }
- }
- return 0;
- }
- for (int i = 0; i < gameLines; i++) {
- for (int j = 0; j < gameLines; j++) {
- if (gameMatrix[i][j].getNum() == 2048) {
- return 2;
- }
- }
- }
- return 1;
- }
下面放出一些最终的图片:
由于刚换了工作,上下班时间倍增,所以写代码的时间也少了,其实关于这个游戏我还有很多想法没做,这个版本的2048,比网上的版本,多了撤销上次移动功能,多了可以定制游戏维数的功能,多了配置目标值的功能,我这个版本的2048,配置要求极低,相比cocos2dx版本的来说,极大的降低了电量消耗,可配置性更强。这些都是我在玩的过程中,觉得不爽的地方,然后改进的,还有一些想法,目前还没有去做,平时工作去做android的framework了,应用层也就研究的少了,希望大家能改进我的代码,做出更好的2048。
1、添加debug功能,也称作弊后门,我原来是想,当手指滑动超过多少距离后,调用一个新方法,设置添加的随机数的位置和大小,这个用我现在的代码是很好实现的,只要把addRandom方法改下,写一个debugRandom方法就OK了
2、分享功能,这个用ShareSdk就可以了,玩游戏嘛,就是要大家一起玩才好玩,无社交不游戏
3、更换2、4、8、16……数字的背景,这个网上很多了,我们也可以自定义一套背景,这个实现也是比较简单的,只要把GameItem这个类里面的Item的背景添加一个set方法就ok了
以上,终了
以下,刷代码
- package com.xys.game2048.view;
- import java.util.ArrayList;
- import java.util.List;
- import android.content.Context;
- import android.content.SharedPreferences.Editor;
- import android.graphics.Point;
- import android.util.AttributeSet;
- import android.util.DisplayMetrics;
- import android.view.Display;
- import android.view.MotionEvent;
- import android.view.View;
- import android.view.View.OnTouchListener;
- import android.view.WindowManager;
- import android.widget.GridLayout;
- import android.widget.Toast;
- import com.xys.game2048.activity.Game;
- import com.xys.game2048.bean.GameItem;
- import com.xys.game2048.config.Config;
- public class GameView extends GridLayout implements OnTouchListener {
- // GameView对应矩阵
- private GameItem[][] gameMatrix;
- // 空格List
- private List<Point> blanks;
- // 矩阵行列数
- private int gameLines;
- // 记录坐标
- private int startX, startY, endX, endY;
- // 辅助数组
- private List<Integer> calList;
- private int keyItemNum = -1;
- // 历史记录数组
- private int[][] gameMatrixHistory;
- // 历史记录分数
- private int scoreHistory;
- // 最高记录
- private int highScore;
- public GameView(Context context) {
- super(context);
- initGameMatrix();
- }
- public GameView(Context context, AttributeSet attrs) {
- super(context, attrs);
- initGameMatrix();
- }
- public void startGame() {
- initGameMatrix();
- initGameView(Config.ItemSize);
- }
- private void initGameView(int cardSize) {
- removeAllViews();
- GameItem card;
- for (int i = 0; i < gameLines; i++) {
- for (int j = 0; j < gameLines; j++) {
- card = new GameItem(getContext(), 0);
- addView(card, cardSize, cardSize);
- // 初始化GameMatrix全部为0 空格List为所有
- gameMatrix[i][j] = card;
- blanks.add(new Point(i, j));
- }
- }
- // 添加随机数字
- addRandomNum();
- addRandomNum();
- }
- /**
- * 撤销上次移动
- */
- public void revertGame() {
- if (gameMatrixHistory.length != 0) {
- Game.getGameActivity().setScore(scoreHistory, 0);
- Config.Scroe = scoreHistory;
- for (int i = 0; i < gameLines; i++) {
- for (int j = 0; j < gameLines; j++) {
- gameMatrix[i][j].setNum(gameMatrixHistory[i][j]);
- }
- }
- }
- }
- /**
- * 添加随机数字
- */
- private void addRandomNum() {
- getBlanks();
- if (blanks.size() > 0) {
- int randomNum = (int) (Math.random() * blanks.size());
- Point randomPoint = blanks.get(randomNum);
- gameMatrix[randomPoint.x][randomPoint.y].setNum(Math.random() > 0.2d ? 2 : 4);
- Game.getGameActivity().getAnimationLayer().animcreate(gameMatrix[randomPoint.x][randomPoint.y]);
- }
- }
- /**
- * 获取空格Item数组
- */
- private void getBlanks() {
- blanks.clear();
- for (int i = 0; i < gameLines; i++) {
- for (int j = 0; j < gameLines; j++) {
- if (gameMatrix[i][j].getNum() == 0) {
- blanks.add(new Point(i, j));
- }
- }
- }
- }
- /**
- * 初始化View
- */
- private void initGameMatrix() {
- // 初始化矩阵
- removeAllViews();
- scoreHistory = 0;
- Config.Scroe = 0;
- Config.GameLines = Config.sp.getInt(Config.KEY_GameLines, 4);
- gameLines = Config.GameLines;
- gameMatrix = new GameItem[gameLines][gameLines];
- gameMatrixHistory = new int[gameLines][gameLines];
- calList = new ArrayList<Integer>();
- blanks = new ArrayList<Point>();
- highScore = Config.sp.getInt(Config.KEY_HighScore, 0);
- setColumnCount(gameLines);
- setRowCount(gameLines);
- setOnTouchListener(this);
- // 初始化View参数
- DisplayMetrics metrics = new DisplayMetrics();
- WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
- Display display = wm.getDefaultDisplay();
- display.getMetrics(metrics);
- Config.ItemSize = metrics.widthPixels / Config.GameLines;
- initGameView(Config.ItemSize);
- }
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- saveHistoryMatrix();
- startX = (int) event.getX();
- startY = (int) event.getY();
- break;
- case MotionEvent.ACTION_MOVE:
- break;
- case MotionEvent.ACTION_UP:
- endX = (int) event.getX();
- endY = (int) event.getY();
- judgeDirection(endX - startX, endY - startY);
- if (isMoved()) {
- addRandomNum();
- // 修改显示分数
- Game.getGameActivity().setScore(Config.Scroe, 0);
- }
- int result = checkCompleted();
- if (result == 0) {
- if (Config.Scroe > highScore) {
- Editor editor = Config.sp.edit();
- editor.putInt(Config.KEY_HighScore, Config.Scroe);
- editor.commit();
- }
- Toast.makeText(getContext(), "lose", Toast.LENGTH_LONG).show();
- Config.Scroe = 0;
- } else if (result == 2) {
- Toast.makeText(getContext(), "success", Toast.LENGTH_LONG).show();
- Config.Scroe = 0;
- }
- break;
- default:
- break;
- }
- return true;
- }
- /**
- * 保存历史记录
- */
- private void saveHistoryMatrix() {
- scoreHistory = Config.Scroe;
- for (int i = 0; i < gameLines; i++) {
- for (int j = 0; j < gameLines; j++) {
- gameMatrixHistory[i][j] = gameMatrix[i][j].getNum();
- }
- }
- }
- /**
- * 根据偏移量判断移动方向
- *
- * @param offsetX
- * @param offsetY
- */
- private void judgeDirection(int offsetX, int offsetY) {
- if (Math.abs(offsetX) > Math.abs(offsetY)) {
- if (offsetX > 10) {
- swipeRight();
- } else {
- swipeLeft();
- }
- } else {
- if (offsetY > 10) {
- swipeDown();
- } else {
- swipeUp();
- }
- }
- }
- /**
- * 判断是否结束
- *
- * @return 0:结束 1:正常 2:成功
- */
- private int checkCompleted() {
- getBlanks();
- if (blanks.size() == 0) {
- for (int i = 0; i < gameLines; i++) {
- for (int j = 0; j < gameLines; j++) {
- if (j < gameLines - 1) {
- if (gameMatrix[i][j].getNum() == gameMatrix[i][j + 1].getNum()) {
- return 1;
- }
- }
- if (i < gameLines - 1) {
- if (gameMatrix[i][j].getNum() == gameMatrix[i + 1][j].getNum()) {
- return 1;
- }
- }
- }
- }
- return 0;
- }
- for (int i = 0; i < gameLines; i++) {
- for (int j = 0; j < gameLines; j++) {
- if (gameMatrix[i][j].getNum() == 2048) {
- return 2;
- }
- }
- }
- return 1;
- }
- /**
- * 判断是否移动过(是否需要新增Item)
- *
- * @return
- */
- private boolean isMoved() {
- for (int i = 0; i < gameLines; i++) {
- for (int j = 0; j < gameLines; j++) {
- if (gameMatrixHistory[i][j] != gameMatrix[i][j].getNum()) {
- return true;
- }
- }
- }
- return false;
- }
- /**
- * 滑动事件:上
- */
- private void swipeUp() {
- for (int i = 0; i < gameLines; i++) {
- for (int j = 0; j < gameLines; j++) {
- int currentNum = gameMatrix[j][i].getNum();
- if (currentNum != 0) {
- if (keyItemNum == -1) {
- keyItemNum = currentNum;
- } else {
- if (keyItemNum == currentNum) {
- calList.add(keyItemNum * 2);
- Config.Scroe += keyItemNum * 2;
- keyItemNum = -1;
- } else {
- calList.add(keyItemNum);
- keyItemNum = currentNum;
- }
- }
- } else {
- continue;
- }
- }
- if (keyItemNum != -1) {
- calList.add(keyItemNum);
- }
- // 改变Item值
- for (int j = 0; j < calList.size(); j++) {
- gameMatrix[j][i].setNum(calList.get(j));
- }
- for (int m = calList.size(); m < gameLines; m++) {
- gameMatrix[m][i].setNum(0);
- }
- // 重置行参数
- keyItemNum = -1;
- calList.clear();
- }
- }
- /**
- * 滑动事件:下
- */
- private void swipeDown() {
- for (int i = gameLines - 1; i >= 0; i--) {
- for (int j = gameLines - 1; j >= 0; j--) {
- int currentNum = gameMatrix[j][i].getNum();
- if (currentNum != 0) {
- if (keyItemNum == -1) {
- keyItemNum = currentNum;
- } else {
- if (keyItemNum == currentNum) {
- calList.add(keyItemNum * 2);
- Config.Scroe += keyItemNum * 2;
- keyItemNum = -1;
- } else {
- calList.add(keyItemNum);
- keyItemNum = currentNum;
- }
- }
- } else {
- continue;
- }
- }
- if (keyItemNum != -1) {
- calList.add(keyItemNum);
- }
- // 改变Item值
- for (int j = 0; j < gameLines - calList.size(); j++) {
- gameMatrix[j][i].setNum(0);
- }
- int index = calList.size() - 1;
- for (int m = gameLines - calList.size(); m < gameLines; m++) {
- gameMatrix[m][i].setNum(calList.get(index));
- index--;
- }
- // 重置行参数
- keyItemNum = -1;
- calList.clear();
- index = 0;
- }
- }
- /**
- * 滑动事件:左
- */
- private void swipeLeft() {
- for (int i = 0; i < gameLines; i++) {
- for (int j = 0; j < gameLines; j++) {
- int currentNum = gameMatrix[i][j].getNum();
- if (currentNum != 0) {
- if (keyItemNum == -1) {
- keyItemNum = currentNum;
- } else {
- if (keyItemNum == currentNum) {
- calList.add(keyItemNum * 2);
- Config.Scroe += keyItemNum * 2;
- keyItemNum = -1;
- } else {
- calList.add(keyItemNum);
- keyItemNum = currentNum;
- }
- }
- } else {
- continue;
- }
- }
- if (keyItemNum != -1) {
- calList.add(keyItemNum);
- }
- // 改变Item值
- for (int j = 0; j < calList.size(); j++) {
- gameMatrix[i][j].setNum(calList.get(j));
- }
- for (int m = calList.size(); m < gameLines; m++) {
- gameMatrix[i][m].setNum(0);
- }
- // 重置行参数
- keyItemNum = -1;
- calList.clear();
- }
- }
- /**
- * 滑动事件:右
- */
- private void swipeRight() {
- for (int i = gameLines - 1; i >= 0; i--) {
- for (int j = gameLines - 1; j >= 0; j--) {
- int currentNum = gameMatrix[i][j].getNum();
- if (currentNum != 0) {
- if (keyItemNum == -1) {
- keyItemNum = currentNum;
- } else {
- if (keyItemNum == currentNum) {
- calList.add(keyItemNum * 2);
- Config.Scroe += keyItemNum * 2;
- keyItemNum = -1;
- } else {
- calList.add(keyItemNum);
- keyItemNum = currentNum;
- }
- }
- } else {
- continue;
- }
- }
- if (keyItemNum != -1) {
- calList.add(keyItemNum);
- }
- // 改变Item值
- for (int j = 0; j < gameLines - calList.size(); j++) {
- gameMatrix[i][j].setNum(0);
- }
- int index = calList.size() - 1;
- for (int m = gameLines - calList.size(); m < gameLines; m++) {
- gameMatrix[i][m].setNum(calList.get(index));
- index--;
- }
- // 重置行参数
- keyItemNum = -1;
- calList.clear();
- index = 0;
- }
- }
- }
PS 需要源码的请留言
FROM: http://blog.csdn.net/eclipsexys/article/details/24582957