Android實戰開發——拼圖游戲


具體代碼的實現托管到了GitHub:https://github.com/ydd997/Android_pintu
下面介紹重要的幾個模塊。

時間改變的實現

MainActivity.java 中的 onCreate 函數中添加如下內容:

//一進來每隔1s就發一條空消息出去,接收到這個空消息並讓TextView發生改變,形成計數器的效果
//延遲1s發送一條空消息:發送消息的編號是1,延遲時間為1000ms=1s
handler.sendEmptyMessageDelayed(1,1000);

之后在 onCreate 函數外添加如下內容:

//定義計數時間的變量
int time=0;

//消息通信機制,重寫handleMessag方法用來接收消息,形成時間累計的效果
Handler handler=new Handler(){
    @Override
    public void handleMessage(@NonNull Message msg) {
        if(msg.what==1){
            time++;
            timeTv.setText("時間:"+time+"秒");
            handler.sendEmptyMessageDelayed(1,1000);
        }
    }
};

時間變化的情況有兩種,一是當拼圖游戲完成的時候時間要停止,二是重新開始的時候要重新計時,現在先實現點擊重新開始按鈕時重新計時:

public void restart(View view) {
    //中斷之前的消息
    handler.removeMessages(1);
    //將時間重新歸零並且重新開始計時
    time=0;
    timeTv.setText("時間:"+time+"秒");
    handler.sendEmptyMessageDelayed(1,1000);
}

拼圖打亂顯示

思路:通過數組的隨機排位來使圖片隨機排列

首先聲明一些變量:

//存放碎片的數組,便於統一管理
private int[]image={R.mipmap.img_00x00,R.mipmap.img_00x01,R.mipmap.img_00x02,R.mipmap.img_01x00,R.mipmap.img_01x01,R.mipmap.img_01x02,R.mipmap.img_02x00,R.mipmap.img_02x01,R.mipmap.img_02x02};

//聲明一個圖片數組的下標數組,隨機排列這個數組
private int[]imageIndex=new int[image.length];

接下來寫一個打亂的方法,使得 onCreate 一進入就顯示打亂的數組。
首先打亂角標,接着把亂序的角標設置到ImageButton中,保證一進入就是打亂的狀態,具體代碼如下:

    //隨機打亂數組中的元素,以不規則的形式進行圖片顯示
    private void disruptRandom() {
        for (int i = 0; i < imageIndex.length; i++) {
            imageIndex[i]=i;
        }
        //規定20次,隨機選擇兩個角標對應的值進行交換
        int rand1,rand2;
        for (int j = 0; j < 20; j++) {
            //隨機生成第一個角標,生成0—8之間的隨機數
            rand1=(int)(Math.random()*(imageIndex.length-1)); //Math.random()生成的是0—1之間的隨機數,再乘以最大值減去最小值(即8-0),最后整體加上最小值0
            //第二次隨機生成的角標不能和第一次相同,如果相同就不方便交換
            do {
                rand2=(int)(Math.random()*(imageIndex.length-1));
                if(rand1!=rand2)
                    break;
            }while (true);
            //交換數組兩個角標上對應的值
            swap(rand1,rand2);
        }
        //隨機排列到指定的控件上
        ib00.setImageResource(image[imageIndex[0]]);
        ib01.setImageResource(image[imageIndex[1]]);
        ib02.setImageResource(image[imageIndex[2]]);
        ib10.setImageResource(image[imageIndex[3]]);
        ib11.setImageResource(image[imageIndex[4]]);
        ib12.setImageResource(image[imageIndex[5]]);
        ib20.setImageResource(image[imageIndex[6]]);
        ib21.setImageResource(image[imageIndex[7]]);
        ib22.setImageResource(image[imageIndex[8]]);
    }

    //交換數組指定角標的數據
    private void swap(int rand1, int rand2) {
        int temp=imageIndex[rand1];
        imageIndex[rand2]=imageIndex[rand1];
        imageIndex[rand1]=temp;
    }

碎片位置切換

碎片移動的條件:空白區域只能和向同行或者相同列上並且是相鄰的圖片進行移動,否則不能完成。

需要判斷空白位置和其他圖的位置是否在同一行或同一列中。

移動的過程寫在 onClick 中:

    public void onClick(View view) {
        int id = view.getId();
        //九個按鈕執行點擊事件的邏輯是相同的:如果有空格在周圍則可以改變圖片的位置,否則點擊事件不響應
        switch (id){
            case R.id.pt_ib_00x00:
                move(R.id.pt_ib_00x00,0);
                break;
            case R.id.pt_ib_00x01:
                move(R.id.pt_ib_00x01,1);
                break;
            case R.id.pt_ib_00x02:
                move(R.id.pt_ib_00x02,2);
                break;
            case R.id.pt_ib_01x00:
                move(R.id.pt_ib_01x00,3);
                break;
            case R.id.pt_ib_01x01:
                move(R.id.pt_ib_01x01,4);
                break;
            case R.id.pt_ib_01x02:
                move(R.id.pt_ib_01x02,5);
                break;
            case R.id.pt_ib_02x00:
                move(R.id.pt_ib_02x00,6);
                break;
            case R.id.pt_ib_02x01:
                move(R.id.pt_ib_02x01,7);
                break;
            case R.id.pt_ib_02x02:
                move(R.id.pt_ib_02x02,8);
                break;
        }
    }

其中 move 函數具體實現過程:

    /*表示移動指定位置的按鈕的函數:將圖片和空白區域進行交換*/
    private void move(int imageButtonId, int site) {

        //判斷選中的圖片在第幾行,取整來判斷
        int sitex=site / imageX;

        //判斷選中的圖片在第幾列,趨於來判斷
        int sitey=site % imageY;

        //獲取空白區域的坐標
        int blackx=blackSwap / imageX;
        int blacky=blackSwap % imageY;

        //可以移動的條件
        //1.在同一行,列數相減絕對值為1,可以移動;2.在同一列,行數相減絕對值為1,可以移動。
        int x=Math.abs(sitex-blackx);
        int y=Math.abs(sitey-blacky);
        if((x==0&&y==1)||(x==1&&y==0)){
            //通過id查找到這個可以移動的按鈕
            ImageButton clickButton=findViewById(imageButtonId);
            //該可移動按鈕不在顯示圖片
            clickButton.setVisibility(View.INVISIBLE);
            //查找空白區域的按鈕
            ImageButton blackButton=findViewById(blackImgid);
            //將空白按鈕設置為顯示圖片
            blackButton.setImageResource(image[imageIndex[site]]);
            //移動之前是不可見的,移動之后將控件設置為可見
            blackButton.setVisibility(View.VISIBLE);

            //上面的交換並沒有存在數組之中,要調用swap函數,將改變角標的過程記錄在存儲圖片位置的數組當中
            swap(site,blackSwap);

            //新的空白區域位置更新
            blackSwap=site;
            blackImgid=imageButtonId;
        }
    }

目前為止效果展示:

拼圖成功的條件

每次拼圖結束之后,要判斷一下當前拼圖順序是否滿足正確順序。
這里是判斷存放圖片角標的數組的順序。

    /*判斷拼圖是否成功*/
    private void judgeGameOver() {
        boolean loop=true; //定義標志位
        //對存放圖片角標的數組imageIndex進行判斷
        for (int i = 0; i < imageIndex.length; i++) {
            if (imageIndex[i]!=i) {
                loop=false;
                break;
            }
        }
        if (loop) {
            //拼圖成功
            //停止計時
            handler.removeMessages(1); //移除消息
            //禁止玩家繼續移動按鈕
            ib00.setClickable(false);
            ib01.setClickable(false);
            ib02.setClickable(false);
            ib10.setClickable(false);
            ib11.setClickable(false);
            ib12.setClickable(false);
            ib20.setClickable(false);
            ib21.setClickable(false);
            ib22.setClickable(false);
            //顯示之前隱藏的拼圖
            ib22.setImageResource(image[8]);
            ib22.setVisibility(View.VISIBLE);
            //彈出對話框
            AlertDialog.Builder builder=new AlertDialog.Builder(this);
            builder.setMessage("拼圖成功啦!您用的時間是"+time+"秒!")
                    .setPositiveButton("確認",null);
            AlertDialog dialog = builder.create();
            dialog.show();
        }
    }

游戲重新開始

現在需要將拼圖重新打亂,重新打亂的函數 disruptRandom() 在前面已經寫好,直接引用即可。
但是之前游戲成功已經禁止拼圖繼續移動,所以將打亂之前要恢復按鈕可以移動的功能,同時還要還原被點擊圖片初始化的樣子,具體代碼如下:

    /*重新開始按鈕的點擊事件*/
    public void restart(View view) {
        //將狀態還原
        restore();

        //將拼圖重新打亂
        disruptRandom();

        //中斷之前的消息
        handler.removeMessages(1);
        //將時間重新歸零並且重新開始計時
        time=0;
        timeTv.setText("時間:"+time+"秒");
        handler.sendEmptyMessageDelayed(1,1000);
    }

    private void restore() {
        //拼圖游戲重新開始,允許玩家重新觸碰按鈕
        ib00.setClickable(true);
        ib01.setClickable(true);
        ib02.setClickable(true);
        ib10.setClickable(true);
        ib11.setClickable(true);
        ib12.setClickable(true);
        ib20.setClickable(true);
        ib21.setClickable(true);
        ib22.setClickable(true);

        //還原被點擊的圖片按鈕變成初始化的模樣
        //最后一次選中的空白區域顯示出來
        ImageButton clickButton=findViewById(blackImgid);
        clickButton.setVisibility(View.VISIBLE);

        //定義一個新的圖片按鈕,設置為第九個,讓其隱藏(默認隱藏第九張圖片)
        ImageButton blackBtn=findViewById(R.id.pt_ib_02x02);
        blackBtn.setVisibility(View.INVISIBLE);

        //初始化空白區域的按鈕id
        blackImgid=R.id.pt_ib_02x02;
        blackSwap=imageCount-1;
    }
}

最終效果:

思考

整個過程不是很難理解,只有一個頁面,布局也比較簡單,采用了最基本的線性布局。感覺比較適合入門。在打亂九個碎片的順序時候采用了兩個數組,第一先將九個碎片按照正確的順序存儲在一個數組中,第二用一個數組存儲碎片存放的位置,通過打亂放置位置的數組來打亂圖片。接着是切換點擊,能夠將碎片和空白位置進行交換,之后是要判斷何時游戲成功,判斷完成點擊重新開始又可以重新打亂順序進行游戲。


免責聲明!

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



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