您的位置:首页 > 移动开发 > Android开发

Android拼图游戏开发全纪录3

2014-02-01 11:11 288 查看
今天我们要继续开发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 : 需要源码的朋友请留言。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: