在作這個遊戲以前,咱們先定一些小目標列出來,一個一個的解決,這樣,一個小遊戲就不知不覺的完成啦。咱們的目標以下: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