在作这个游戏以前,咱们先定一些小目标列出来,一个一个的解决,这样,一个小游戏就不知不觉的完成啦。咱们的目标以下:java
判断是否已经拼好。
git
想拉伸成屏幕大小,首先要知道屏幕的大小,Android得到屏幕大小的代码以下:github
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);
将图片切成若干块canvas
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); } }
这个问题应该是这个小游戏的核心了,有些人在作拼图的时候就随便乱摆,最后发现拼不回来,超级尴尬。要想打乱了还能拼回来,咱们呢,就想到了模拟人打乱拼图的方法,就是将空白块与旁边的非空白块交换位置,与旁边哪一个非空白块交换是随机的,而后将这个过程重复若干次,重复的次数也是随机的,这样一来,保证了图块的随机,又保证了能拼回来。在这里咱们用数字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++; }
下面是打乱块的算法,最后一块是空白块,让它随机与旁边的某一块进行交换,这个过程当中要检查数组边界,不要让它越界。dom
//四个方向 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; }
留出空白块很简单,因为上面咱们将最后一块做为空白块。当咱们绘图时,略过它便可。代码实现以下:ide
@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便可this
/** * 将屏幕上的点转换成,对应拼图块的索引 * @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; }
咱们初始化数据时,是从0开始,依次增长做为拼图数据。当拼好的时候,拼图数据也应该是同样的,因此咱们比较数组中每个数据与它的下一个数据,若是每个数据都小于它的下一个数据,说明数组里面的数据已经从小到大排列好。code
/** * 判断是否拼图成功 * @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