• 我也来开发2048之终极奥义


    版权声明:本文为博主原创文章,未经博主同意不得转载。 https://blog.csdn.net/x359981514/article/details/24582957

    本次教程跟之前隔了不少时间哈,有点忘记了的建议先看看前面的熟悉下,今天我准备把这个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 须要源代码的请留言


    PS 须要源代码的请留言
  • 相关阅读:
    13 款开源的全文检索引擎
    Laravel5.5 Jwt 1.0 beta 配置
    Laravel SQL 查询语句集锦
    laravel在中间件内生成的变量如何传到控制器
    laravel中的自定义函数的加载和第三方扩展库加载
    laravel5.5 dingo/api+jwt-auth
    微信小程序之使用checkbox
    微信小程序之使用wx:for遍历循环
    微信小程序之页面导航栏
    微信小程序之数据缓存
  • 原文地址:https://www.cnblogs.com/ldxsuanfa/p/10959859.html
  • Copyright © 2020-2023  润新知