Android拼圖遊戲開發全紀錄3
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow
也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!
今天我們要繼續開發Android遊戲拼圖,今天同樣是做一些準備工作,昨天我們把介面的準備工作做好了,今天呢,我們想想,要完成一個拼圖,我們還需要做哪些準備。
首先,我們需要一個工具類,去獲取螢幕的相關資訊,讓我們的程式去自動適應不同解析度大小的螢幕:
package com.xys.xpuzzle.util;import android.content.Context;import android.util.DisplayMetrics;import android.view.Display;import android.view.WindowManager;/** * 螢幕工具類:實現獲取螢幕相關引數 * * @author xys * */public class ScreenUtil { /** * 獲取螢幕相關引數 * * @param context * @return DisplayMetrics 螢幕寬高 */ public static DisplayMetrics getScreenSize(Context context) { DisplayMetrics metrics = new DisplayMetrics(); WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); Display display = wm.getDefaultDisplay(); display.getMetrics(metrics); return metrics; }}
下面我們要開始設計一個實體bean,去封裝我們的拼圖,這裡的方法有很多,我只是舉個磚頭。
我們知道,拼圖時會把圖片分割成NXN個方塊,移動方塊以完成拼圖,所以我將每個分割後的方塊做成一個物件,我們所有的實體bean都是基於每個分割後的方塊,所以自然的我們可以抽象出一個實體bean:
package com.xys.xpuzzle.bean;import android.graphics.Bitmap;/** * 拼圖Item邏輯實體類:封裝邏輯相關屬性 * * @author xys * */public class ItemBean { // Item的Id private int itemId; // bitmap的Id private int bitmapId; // bitmap private Bitmap bitmap; public int getItemId() { return itemId; } public void setItemId(int itemId) { this.itemId = itemId; } public ItemBean() { } public int getBitmapId() { return bitmapId; } public void setBitmapId(int bitmapId) { this.bitmapId = bitmapId; } public Bitmap getBitmap() { return bitmap; } public void setBitmap(Bitmap bitmap) { this.bitmap = bitmap; } public ItemBean(int itemId, int bitmapId, Bitmap bitmap) { this.itemId = itemId; this.bitmapId = bitmapId; this.bitmap = bitmap; }}
bean裡面封裝了
1、方塊對應在NXN格中的序號
2、分割後的圖片的ID和對應的圖片
接下來就是對圖片的分割:
package com.xys.xpuzzle.util;import java.util.ArrayList;import java.util.List;import com.xys.xpuzzle.R;import com.xys.xpuzzle.activity.PuzzleMain;import com.xys.xpuzzle.bean.ItemBean;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Matrix;/** * 影象工具類:實現影象的分割與自適應 * * @author xys * */public class ImagesUtil { public ItemBean itemBean; /** * 切圖、初始狀態(正常順序) * * @param type * @param picSelected * @param context */ public void createInitBitmaps(int type, Bitmap picSelected, Context context) { Bitmap bitmap = null; List<Bitmap> bitmapItems = new ArrayList<Bitmap>(); // 每個Item的寬高 int itemWidth = picSelected.getWidth() / type; int itemHeight = picSelected.getHeight() / type; for (int i = 1; i <= type; i++) { for (int j = 1; j <= type; j++) { bitmap = Bitmap.createBitmap(picSelected, (j - 1) * itemWidth, (i - 1) * itemHeight, itemWidth, itemHeight); bitmapItems.add(bitmap); itemBean = new ItemBean((i - 1) * type + j, (i - 1) * type + j, bitmap); GameUtil.itemBeans.add(itemBean); } } // 儲存最後一個圖片在拼圖完成時填充 PuzzleMain.lastBitmap = bitmapItems.get(type * type - 1); // 設定最後一個為空Item bitmapItems.remove(type * type - 1); GameUtil.itemBeans.remove(type * type - 1); Bitmap blankBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.blank); blankBitmap = Bitmap.createBitmap(blankBitmap, 0, 0, itemWidth, itemHeight); bitmapItems.add(blankBitmap); GameUtil.itemBeans.add(new ItemBean(type * type, 0, blankBitmap)); GameUtil.blankItemBean = GameUtil.itemBeans.get(type * type - 1); } /** * 處理圖片 放大、縮小到合適位置 * * @param newWidth * @param newHeight * @param bitmap * @return */ public Bitmap resizeBitmap(float newWidth, float newHeight, Bitmap bitmap) { Matrix matrix = new Matrix(); matrix.postScale(newWidth / bitmap.getWidth(), newHeight / bitmap.getHeight()); Bitmap newBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); return newBitmap; }}
這個工具類主要完成這兩件事:
1、切圖、初始狀態(正常順序)
2、處理圖片 放大、縮小到合適位置
根據上一篇講的演算法,我們需要將圖片進行分割,然後生成一個切好圖後的Items集合,當然,還要對要拼圖的圖片進行下大小的處理,因為我們除了有預設的圖片,還可以自定義圖片
圖片工具類完成後,接下來我們就要在一個工具類中實現這些演算法,同時還要做一些對遊戲的封裝:
package com.xys.xpuzzle.util;import java.util.ArrayList;import java.util.List;import com.xys.xpuzzle.activity.PuzzleMain;import com.xys.xpuzzle.bean.ItemBean;/** * 拼圖工具類:實現拼圖的交換與生成演算法 * * @author xys * */public class GameUtil { // 遊戲資訊單元格Bean public static List<ItemBean> itemBeans = new ArrayList<ItemBean>(); // 空格單元格 public static ItemBean blankItemBean = new ItemBean(); /** * 判斷點選的Item是否可移動 * * @param position * @return 能否移動 */ public static boolean isMoveable(int position) { int type = PuzzleMain.type; // 獲取空格Item int blankId = GameUtil.blankItemBean.getItemId() - 1; // 不同行 相差為type if (Math.abs(blankId - position) == type) { return true; } // 相同行 相差為1 if ((blankId / type == position / type) && Math.abs(blankId - position) == 1) { return true; } return false; } /** * 交換空格與點選Item的位置 * * @param from * @param blank */ public static void swapItems(ItemBean from, ItemBean blank) { ItemBean tempItemBean = new ItemBean(); // 交換BitmapId tempItemBean.setBitmapId(from.getBitmapId()); from.setBitmapId(blank.getBitmapId()); blank.setBitmapId(tempItemBean.getBitmapId()); // 交換Bitmap tempItemBean.setBitmap(from.getBitmap()); from.setBitmap(blank.getBitmap()); blank.setBitmap(tempItemBean.getBitmap()); // 設定新的Blank GameUtil.blankItemBean = from; } /** * 生成隨機的Item */ public static void getPuzzleGenerator() { int index = 0; for (int i = 0; i < itemBeans.size(); i++) { index = (int) (Math.random() * PuzzleMain.type * PuzzleMain.type); swapItems(itemBeans.get(index), GameUtil.blankItemBean); } List<Integer> data = new ArrayList<Integer>(); for (int i = 0; i < itemBeans.size(); i++) { data.add(itemBeans.get(i).getBitmapId()); } // 判斷生成是否有解 if (canSolve(data)) { return; } else { getPuzzleGenerator(); } } /** * 是否拼圖成功 * * @return 是否拼圖成功 */ public static boolean isSuccess() { for (ItemBean tempBean : GameUtil.itemBeans) { if (tempBean.getBitmapId() != 0 && (tempBean.getItemId()) == tempBean.getBitmapId()) { continue; } else if (tempBean.getBitmapId() == 0 && tempBean.getItemId() == PuzzleMain.type * PuzzleMain.type) { continue; } else { return false; } } return true; } /** * 該資料是否有解 * * @param data * @return 該資料是否有解 */ public static boolean canSolve(List<Integer> data) { // 獲取空格Id int blankId = GameUtil.blankItemBean.getItemId(); // 可行性原則 if (data.size() % 2 == 1) { return getInversions(data) % 2 == 0; } else { // 從底往上數,空格位於奇數行 if (((int) (blankId - 1) / PuzzleMain.type) % 2 == 1) { return getInversions(data) % 2 == 0; } else { // 從底往上數,空位位於偶數行 return getInversions(data) % 2 == 1; } } } /** * 計算倒置和演算法 * * @param data * @return 該序列的倒置和 */ public static int getInversions(List<Integer> data) { int inversions = 0; int inversionCount = 0; for (int i = 0; i < data.size(); i++) { for (int j = i + 1; j < data.size(); j++) { int index = data.get(i); if (data.get(j) != 0 && data.get(j) < index) { inversionCount++; } } inversions += inversionCount; inversionCount = 0; } return inversions; }}
可以看到,遊戲工具類中,我們主要有以下幾個功能:
1、判斷點選的Item是否可移動:主要難點是判斷需要分同行與不同行,否則會出現上一行的最後一個和本行的第一個可以移動的BUG
2、交換空格與點選Item的位置:實際上是交換GridView中的某2個Item的背景
3、生成隨機的Item:根據上一篇講的演算法,打隨機打亂分割後的圖片
4、判斷是否拼圖成功:根據上一篇講的演算法的結論判斷
5、判斷該資料是否有解:根據上一篇講的演算法的結論判斷
6、計算倒置和演算法:演算法的核心注意要踢出空格
到目前為止,我們的準備工作就基本結束了,很多人可能會說開始的時候準備這麼多幹嘛,其實這是我真實的思考順序,剛開始專案的時候,一定要先把專案整體規劃一下,而不是上手就做,當你有了一個清晰的思路後,coding只是一個體力活而已。所以,工欲善其事,必先利其器,畫竹需要成竹在胸。
ps : 需要原始碼的朋友請留言。