目标

在做这个游戏之前,我们先定一些小目标列出来,一个一个的解决,这样,一个小游戏就不知不觉的完成啦。我们的目标如下:

  1. 游戏全屏,将图片拉伸成屏幕大小,并将其切成若干块。
  2. 将拼图块随机打乱,并保证其能有解。
  3. 在屏幕上留出一个空白块,当点空白块旁边的块,将这块移动到空白块。
  4. 判断是否已经拼好。

实现目标

1.将图片拉伸成屏幕大小,并将其切成若干块。

想拉伸成屏幕大小,首先要知道屏幕的大小,Android获得屏幕大小的代码如下:

DisplayMetrics metrics =new DisplayMetrics();
getWindowManager().getDefaultDisplay().getRealMetrics(metrics);//sdk17+
int screenWidth = metrics.widthPixels;//屏幕宽
int screenHeight = metrics.heightPixels;//屏幕高

将图片拉伸到屏幕大小

Bitmap back=Bitmap.createScaledBitmap(bitmap,
MainActivity.getScreenWidth(),
MainActivity.getScreenHeight(),
true);

将图片切成若干块

 private final int COL=3;//列,默认3列
private final int ROW=3;//行,默认3行
int tileWidth=back.getWidth()/COL;//每一块的宽
int tileHeight=back.getHeight()/ROW;//每一块的高
Bitmap[] bitmapTiles =new Bitmap[COL*ROW];
int idx=0;
for(int i=0;i<ROW;i++)
{
for(int j=0;j<COL;j++)
{
bitmapTiles[idx++]=Bitmap.createBitmap(back,
j*tileWidth,
i*tileHeight,
tileWidth,tileHeight);
}
}

2. 将拼图块随机打乱,并保证其能有解。

这个问题应该是这个小游戏的核心了,有些人在做拼图的时候就随便乱摆,最后发现拼不回来,超级尴尬。要想打乱了还能拼回来,我们呢,就想到了模拟人打乱拼图的方法,就是将空白块与旁边的非空白块交换位置,与旁边哪个非空白块交换是随机的,然后将这个过程重复若干次,重复的次数也是随机的,这样一来,保证了图块的随机,又保证了能拼回来。在这里我们用数字0到N-1(N为块的数量)表示每一块,并用二维数组存储他们。

    private void createIntegerArray(int row,int col)
{
array=new int[row][col];
int idx=0;
for(int i=0;i<row;i++)
for(int j=0;j<col;j++)
array[i][j]=idx++;
}

下面是打乱块的算法,最后一块是空白块,让它随机与旁边的某一块进行交换,这个过程中要检查数组边界,不要让它越界。

    //四个方向
private int[][] dir={
{0,1},//下
{1,0},//右
{0,-1},//上
{-1,0}//左
};
/**
* 移动块的位置
* @param srcX 初始x位置
* @param srcY 初始y位置
* @param xOffset x偏移量
* @param yOffset y偏移量
* @return 新的位置,错误返回new Point(-1,-1);
*/
private Point move(int srcX,int srcY,int xOffset,int yOffset)
{
int x=srcX+xOffset;
int y=srcY+yOffset;
if(x<0||y<0||x>=col||y>=row)
return new Point(-1,-1); int temp=array[y][x];
array[y][x]=array[srcY][srcX];
array[srcY][srcX]=temp; return new Point(x,y);
} /**
* 得到下一个可以移动的位置
* @param src 初始的点
* @return
*/
private Point getNextPoint(Point src)
{
Random rd=new Random();
int idx=rd.nextInt(4);//,因为有4个方向,所以产生0~3的随机数
int xOffset=dir[idx][0];
int yOffset=dir[idx][1];
Point newPoint=move(src.getX(),src.getY(),xOffset,yOffset);
if(newPoint.getX()!=-1&&newPoint.getY()!=-1) {
return newPoint;//找到了新的点
} return getNextPoint(src);//没有找到,继续
} /**
* 生成拼图数据
* @param row
* @param col
* @return
*/
public int[][] createRandomBoard(int row,int col)
{
if(row<2||col<2)
throw new IllegalArgumentException("行和列都不能小于2");
this.row=row;
this.col=col;
createIntegerArray(row,col);//初始化拼图数据
int count=0;
Point tempPoint=new Point(col-1,row-1);//最后一块是空白块
Random rd=new Random();
int num=rd.nextInt(100)+20;//产生20~119的随机数,表示重复的次数
while (count<num)
{
tempPoint=getNextPoint(tempPoint);//获得下个点,并更新空白块位置
count++;
}
return array;
}

3. 在屏幕上留出一个空白块,当点空白块旁边的块,将这块移动到空白块。

留出空白块很简单,由于上面我们将最后一块作为空白块。当我们绘图时,略过它即可。代码实现如下:

    @Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.GRAY);
for(int i=0;i<ROW;i++) {
for (int j = 0; j < COL; j++) {
int idx=dataTiles[i][j];
if(idx==ROW*COL-1&&!isSuccess)
continue;
canvas.drawBitmap(bitmapTiles[idx],
j*tileWidth,
i*tileHeight,paint);
}
}
}

移动块也很简单,当点击屏幕时,计算其在拼图数据中对应的索引。当计算到点击非空白块就寻找它旁边有没有空白块,有,则将拼图数据中表示空白块和非空白块的数据交换,并刷新View即可

    /**
* 将屏幕上的点转换成,对应拼图块的索引
* @param x
* @param y
* @return
*/
private Point xyToIndex(int x,int y)
{
int extraX=x%tileWidth>0?1:0;
int extraY=x%tileWidth>0?1:0;
int col=x/tileWidth+extraX;
int row=y/tileHeight+extraY; return new Point(col-1,row-1);
}
/**
*点击屏幕时发生
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction()==MotionEvent.ACTION_DOWN) {
Point point = xyToIndex((int) event.getX()
, (int) event.getY()); for(int i=0;i<dir.length;i++)
{
int newX=point.getX()+dir[i][0];
int newY=point.getY()+dir[i][1]; if(newX>=0&&newX<COL&&newY>=0&&newY<ROW){
if(dataTiles[newY][newX]==COL*ROW-1)
{
int temp=dataTiles[point.getY()][point.getX()];
dataTiles[point.getY()][point.getX()]=dataTiles[newY][newX];
dataTiles[newY][newX]=temp;
invalidate();
}
}
}
}
return true;
}

4. 判断是否已经拼好

我们初始化数据时,是从0开始,依次增加作为拼图数据。当拼好的时候,拼图数据也应该是一样的,所以我们比较数组中每一个数据与它的下一个数据,如果每一个数据都小于它的下一个数据,说明数组里面的数据已经从小到大排列好。

/**
* 判断是否拼图成功
* @param arr
* @return
*/
public boolean isSuccess(int[][] arr)
{
int idx=0; for(int i=0;i<arr.length;i++)
{
for(int j=0;j<arr[i].length&&idx<row*col-1;j++)
{ if(arr[idx/row][idx%col]>arr[(idx+1)/row][(idx+1)%col])
{ return false; }
idx++;
} }
return true;
}

拼图游戏技巧

拼图技巧觉得也有必要说一下,不然有些人就会说:你的算法有问题,这根本拼不好!我也是超级无奈啊!拼图的技巧是,我们先把上面的第一行拼好,然后再把第二行拼好,这样,一直下去~就能完全拼好了。

总结

这个小游戏简单,可以拿来练手,还可以拿来装(liao)逼(mei),如果不会,何乐而不看呢。这个小游戏也是将视图和数据分开,代码容易移植。

项目地址

https://github.com/luoyesiqiu/PuzzleGame

[Android]自己动手做个拼图游戏的更多相关文章

  1. 【Android】自己动手做个扫雷游戏

    1. 游戏规则 扫雷是玩法极其简单的小游戏,点击玩家认为不存在雷的区域,标记出全部地雷所在的区域,即可获得胜利.当点击不包含雷的块的时候,可能它底下存在一个数,也可能是一个空白块.当点击中有数字的块时 ...

  2. Android拼图游戏

    效果如下 游戏的设计 首先我们分析下如何设计这款游戏: 1.我们需要一个容器,可以放这些图片的块块,为了方便,我们准备使用RelativeLayout配合addRule实现 2.每个图片的块块,我们准 ...

  3. Android拼图游戏的设计逻辑,从切图到交互动画,从关卡到倒计时,实例提高!

    Android拼图游戏的设计逻辑,从切图到交互动画,从关卡到倒计时,实例提高! 群英传的最后一章,我大致的看了一下这个例子,发现鸿洋大神也做过,就参考两个人的设计逻辑,感觉都差不多,就这样实现起来了 ...

  4. Android 实战美女拼图游戏 你能坚持到第几关

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/40595385,本文出自:[张鸿洋的博客] 1.概述 继2048之后,今天给大家带 ...

  5. android指纹识别、拼图游戏、仿MIUI长截屏、bilibili最美创意等源码

    Android精选源码 一个动画效果的播放控件,播放,暂停,停止之间的动画 用 RxJava 实现 Android 指纹识别代码 Android仿滴滴打车(滴滴UI)源码 Android高仿哔哩哔哩动 ...

  6. Android群英传-拼图游戏puzzle-代码设计和实现

    上个周末,3个小时总体上读完了<Android群英传>,本周主要在研究代码层次的设计和实现.  编译安装在手机上,玩了几把,结合代码,一周时间才掌握了整体的思路.  大部分时间,其实花在了 ...

  7. 爱拼图游戏android源码完整版

    这个是一款爱拼图游戏源码完整版,该游戏源码比较完整的,可以支持音乐的播放在游戏的玩的过程中,还可以控制系统的声音等,可以支持多种图片的选择来进行玩的,还可以根据自己的爱好选择不同的难度来的,级别分为: ...

  8. Android群英传-拼图游戏puzzle-6点吐槽

    一.缘由  经常写文章,混了一些C币.最近在深入学习Android应用开发,就从商城里买了一本<Android群英传>.这本书的内容,不是纯粹的入门那种,分几个章节,重点讲解Activit ...

  9. JavaScript拼图游戏

    今天是2016年最后一天上班了.最近几天都比较休闲,有时间空闲下来写写文档之类的. 2016过得真是快.感觉没做什么就过去了.想到之前想坚持每个月写一写博客都没坚持到.希望2017年可以吧. 无聊之余 ...

随机推荐

  1. BugFix:URL or HTTP headers are too long (IP=127.0.0.1)

    错误提示: URL or HTTP headers are too long (IP=127.0.0.1) com.caucho.server.dispatch.BadRequestException ...

  2. JSF-使用JSF标记

    使用JSF标记 基于Facelets技术的JSF页面是一个 XHTML页面,文件扩展名为 .xhtml 1)JSF页面可用html标记,但必须满足: ①所有标记都必须闭合.如<p>开始,& ...

  3. FPGA中的除法运算及初识AXI总线

    FPGA中的硬件逻辑与软件程序的区别,相信大家在做除法运算时会有深入体会.硬件逻辑实现的除法运算会占用较多的资源,电路结构复杂,且通常无法在一个时钟周期内完成.因此FPGA实现除法运算并不是一个&qu ...

  4. python 要掌握面向对象,你得会做这些题吗?

    1,面向对象三大特性,各有什么用处,说说你的理解. 继承:解决代码重用问题 多态:多态性,可以在不考虑对象类型的情况下而直接使用对象 封装:明确的区分内外,控制外部对隐藏属性的操作行为,隔离复杂度 2 ...

  5. maven包加载

    1) IDEA包加载pom.xml配置 <build>    <sourceDirectory>src/main/java</sourceDirectory>    ...

  6. Map Reduce和流处理

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由@从流域到海域翻译,发表于腾讯云+社区 map()和reduce()是在集群式设备上用来做大规模数据处理的方法,用户定义一个特定的映射 ...

  7. PAT1088:Rational Arithmetic

    1088. Rational Arithmetic (20) 时间限制 200 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue F ...

  8. 全局唯一ID发号器的几个思路

    标识(ID / Identifier)是无处不在的,生成标识的主体是人,那么它就是一个命名过程,如果是计算机,那么它就是一个生成过程.如何保证分布式系统下,并行生成标识的唯一与标识的命名空间有着密不可 ...

  9. 一个比ack速度快n倍的代码搜索工具: ag

    一个比ack速度快n倍的代码搜索工具:  ag 银搜索者(The Silver Searcher) 一个类似于代码搜索工具ack,着重于速度. Github:   https://github.com ...

  10. JVM 线上故障排查基本操作

    # 前言 对于后端程序员,特别是 Java 程序员来讲,排查线上问题是不可避免的.各种 CPU 飚高,内存溢出,频繁 GC 等等,这些都是令人头疼的问题.楼主同样也遇到过这些问题,那么,遇到这些问题该 ...