[Android]自己動手做個拼圖游戲


目標

在做這個游戲之前,我們先定一些小目標列出來,一個一個的解決,這樣,一個小游戲就不知不覺的完成啦。我們的目標如下:

  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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM