主頁 > 移動端開發 > ??用Android Studio做一個超好玩的拼圖游戲,0基礎Android小白也能包你學會,附送超詳細注釋的原始碼,建議收藏!??

??用Android Studio做一個超好玩的拼圖游戲,0基礎Android小白也能包你學會,附送超詳細注釋的原始碼,建議收藏!??

2021-08-20 08:37:46 移動端開發

文章目錄

  • 一、專案概述
  • 二、開發環境
  • 三、需求分析
  • 四、實作程序
    • 1、拼圖游戲布局繪制
    • 2、拼圖游戲時間計時
    • 3、拼圖游戲打亂顯示
    • 4、拼圖游戲碎片位置切換
    • 5、拼圖游戲成功的條件
    • 6、拼圖游戲重新開始
  • 五、運行效果
  • 六、專案總結
  • 七、專案原始碼

一、專案概述

之前有不少粉絲私信我說,能不能用Android原生的語言開發一款在手機上運行的游戲呢?

說實話,使用java語言直接開發游戲這個需求有點難,因為一些比較復雜的游戲都是通過cocos2D或者Unity3D等游戲引擎開發出來的,然后再移植到Android手機當中,使用完整的游戲引擎開發的程序比較簡單,而且界面比較流暢,觀感和體驗度都很好,

所以直接使用java開發的游戲并不多,當然,雖說不多但也有,簡單些的比如:2048、拼圖游戲、貪吃蛇、推箱子等,復雜點的比如:斗地主,這些都可以用java語言開發,因為這些游戲重繪界面次數比較少,是可以用java開發出來的,

所以在這篇博客里面,我們就來開發一款簡單的拼圖游戲,這款拼圖游戲就和我們小時候玩的游戲是一樣的,這里面的涉及到的演算法不多,可以很容易學會,是作為入門Android的一個非常好的實體,

二、開發環境

在這里插入圖片描述

三、需求分析

我們先來看下最終要實作的效果

可以看到游戲開始后,開始計時,然后下面是被打亂的九宮格圖片,最后一塊是空白的,因為要留出空間移動,中間是重新開始按鈕,點擊就會重新計時而且拼圖碎片重新打亂,最底下是原圖,方便大家對照著進行拼湊,當你拼圖完成后,上面的第九塊拼圖會立刻顯示出來補齊整張圖片,然后彈出對話框,告訴你拼圖成功,用時為多少多少秒,點擊確認即可,
在這里插入圖片描述
所以我們分為六個步驟來實作

  1. 拼圖游戲布局繪制
  2. 拼圖游戲時間計時
  3. 拼圖游戲打亂顯示
  4. 拼圖游戲碎片位置切換
  5. 拼圖游戲成功的條件
  6. 拼圖游戲重新開始

我們來看下需要準備的圖片素材

這里先是一張小熊的樣圖,命名就是yangtu,然后就是將它按九宮格裁剪成的九張圖片,命名格式我來解釋下:我們看第八張我選中的圖片,它的名字為img_xiaoxiong_02x01,這里解釋下為什么是02x01,這就可以看做一個三行三列的二維陣列,排列方式就和下面一樣,陣列行和列下標都是從0開始,所以第八張就是在第2行第1列,所以就是02x01,其他的也以此類推,
大家可以自己選圖片進行裁剪命名,當然也可以直接下載我的原始碼,里面就有這些圖片,
在這里插入圖片描述
下面我們就一起來實作這個拼圖游戲吧~

四、實作程序

1、拼圖游戲布局繪制

我們首先來分析下游戲的layout布局

再來看下最終實作的效果圖,先分析一下怎么繪制布局,實作一個專案的第一步是將布局按照自己期望的樣子完成,

因為這是一個上下結構,所以我們用一個線性布局(LinearLayout)來實作最合適,方向(orientation)設定為豎直方向(vertical),可以看到這個拼圖分為三行三列,所以我們直接將每一行分為一個小的LinearLayout,一共三個,然后在每個小的LinearLayout里面水平放三個圖片按鈕,這樣就實作了,思路有了,我們來繪制吧,
在這里插入圖片描述

我們來繪制游戲的layout布局

從上至下的第一個布局是顯示時間的TextView,我們將它的id設定為pt_tv_time,layout_width和layout_height都設定為wrap_content,就是適應內容大小,然后text文本內容設為“時間:0”,這個是方便測驗寫上文本的,因為邊寫代碼可以邊看旁邊的效果變化,

然后layout_gravity設定為"center",就是設定自己在父容器(頂層的LinearLayout)中居中,這里補充下知識點:

  • gravity是設定自身內部元素的對齊方式,比如一個TextView,則是設定內部文字的對齊方式,如果是ViewGroup組件如LinearLayout的話,則為設定它內部view組件的對齊方式,

  • layout_gravity是設定自身相當于父容器的對齊方式,比如,一個TextView設定layout_gravity屬性,則表示這TextView相對于父容器的對齊方式,

再來改變下字體大小,設定textSize為20sp,sp是像素,補充下單位的知識點:

  • dp: device independent pixels(設備獨立像素),不同設備有不同的顯示效果,和設備硬體有關,
  • px: pixels(像素).,不同設備顯示效果相同,這個用的比較多,
  • pt: point,是一個標準的長度單位,1pt=1/72英寸,用于印刷業,非常簡單易用,
  • sp: scaled pixels(放大像素),主要用于字體顯示best for textsize,

最后設定字體顏色為#FF0000,即紅色,一般是通過colors.xml資源來參考,這里因為紅色比較好表示就直接設定了,

TextView代碼如下:

<TextView
        android:id="@+id/pt_tv_time"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="時間 : 0"
        android:layout_gravity="center"
        android:textSize="20sp"
        android:textColor="#FF0000"/>

設定完成后,我們來看下效果圖:
在這里插入圖片描述
接著我們來繪制九宮格拼圖,先設定第一行這三個小圖片的外布局,依然是LinearLayout,設定它的id="@+id/pt_line1",就表示第一行,

orientation選擇的是水平方向,因為每一行是水平放置的,layout_gravity設定為"center",表示居中,代碼如下,

<LinearLayout
        android:id="@+id/pt_line1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_gravity="center">
        
</LinearLayout>

設定第一張圖片,選擇的控制元件是ImageButton,顧名思義:圖片按鈕,正常按鈕就規規矩矩的,而圖片按鈕就很好看,一張圖片也可以進行點擊,這里設定它的id="@+id/pt_ib_00x00",方便在MainActivity里面呼叫,

00x00不用我多說了吧,上面解釋過了,將九宮格看成3X3的二維陣列,那么行列下標就是0行0列,這里每行數和列數都用2位數字表示而已,

設定src="@mipmap/img_xiaoxiong_00x00",就是將我們剛剛準備的圖片資源復制到這個mipmap檔案夾中進行參考,每個id編號和圖片的名稱是對應的,
在這里插入圖片描述

再設定個onClick方法,方法名為"onClick",我們后面會在MainActivity里面進行撰寫點擊事件,第一張圖片的代碼如下:

<ImageButton
            android:id="@+id/pt_ib_00x00"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@mipmap/img_xiaoxiong_00x00"
            android:padding="0dp"
            android:onClick="onClick"/>

依次類推,第二張和第三張圖片,我只要改下id和src就可以了,所以直接放上第一個小LinearLayout的代碼:

<LinearLayout
        android:id="@+id/pt_line1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_gravity="center">
        <ImageButton
            android:id="@+id/pt_ib_00x00"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@mipmap/img_xiaoxiong_00x00"
            android:padding="0dp"
            android:onClick="onClick"/>
        <ImageButton
            android:id="@+id/pt_ib_00x01"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@mipmap/img_xiaoxiong_00x01"
            android:padding="0dp"
            android:onClick="onClick"/>
        <ImageButton
            android:id="@+id/pt_ib_00x02"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@mipmap/img_xiaoxiong_00x02"
            android:padding="0dp"
            android:onClick="onClick"/>
    </LinearLayout>

來看下顯示效果:
在這里插入圖片描述
那第二行和第三行是不是也一樣照葫蘆畫瓢,沒錯,直接復制第一行的代碼,然后修改id和src就行,這里直接給出三個LinearLayout的代碼:

<LinearLayout
        android:id="@+id/pt_line1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_gravity="center">
        <ImageButton
            android:id="@+id/pt_ib_00x00"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@mipmap/img_xiaoxiong_00x00"
            android:padding="0dp"
            android:onClick="onClick"/>
        <ImageButton
            android:id="@+id/pt_ib_00x01"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@mipmap/img_xiaoxiong_00x01"
            android:padding="0dp"
            android:onClick="onClick"/>
        <ImageButton
            android:id="@+id/pt_ib_00x02"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@mipmap/img_xiaoxiong_00x02"
            android:padding="0dp"
            android:onClick="onClick"/>
    </LinearLayout>

    <LinearLayout
        android:id="@+id/pt_line2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_gravity="center">
        <ImageButton
            android:id="@+id/pt_ib_01x00"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@mipmap/img_xiaoxiong_01x00"
            android:padding="0dp"
            android:onClick="onClick"/>
        <ImageButton
            android:id="@+id/pt_ib_01x01"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@mipmap/img_xiaoxiong_01x01"
            android:padding="0dp"
            android:onClick="onClick"/>
        <ImageButton
            android:id="@+id/pt_ib_01x02"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@mipmap/img_xiaoxiong_01x02"
            android:padding="0dp"
            android:onClick="onClick"/>
    </LinearLayout>

    <LinearLayout
        android:id="@+id/pt_line3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_gravity="center">
        <ImageButton
            android:id="@+id/pt_ib_02x00"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@mipmap/img_xiaoxiong_02x00"
            android:padding="0dp"
            android:onClick="onClick"/>
        <ImageButton
            android:id="@+id/pt_ib_02x01"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@mipmap/img_xiaoxiong_02x01"
            android:padding="0dp"
            android:onClick="onClick"/>
        <ImageButton
            android:id="@+id/pt_ib_02x02"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@mipmap/img_xiaoxiong_02x02"
            android:padding="0dp"
            android:onClick="onClick"
            android:visibility="invisible"/>
    </LinearLayout>

有一點需要注意的,不知道有沒有同學發現——第三行的第三張圖片,也就是右下角的那張圖片,它有個屬性,其他的圖片都沒有:visibility=“invisible”,這是干什么的呢?

這個其實就是設定控制元件是否可見,默認情況下控制元件都是可見的(visible),只有設定visibility="invisible"后,這個控制元件才不顯示出來,我們來看下整體效果:
在這里插入圖片描述
OK,九宮格完成后,下面是一個重新開始的Button,

這個比較簡單了,主要設定了onClick=“restart”,這個后面會在MainActivity里面撰寫重新開始游戲的邏輯,還設定了android:layout_marginTop=“20dp”,這是設定此控制元件與上面控制元件邊距相隔20dp,為了和九宮格保持一定間距,代碼如下:

<Button
        android:id="@+id/pt_btn_restart"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="restart"
        android:layout_gravity="center"
        android:text="重新開始"
        android:layout_marginTop="20dp"/>

顯示效果:
在這里插入圖片描述
最后就是我們的樣圖了,有了我們上面的經驗,這個應該很容易就畫出來了,放置圖片的控制元件我們一般使用ImageView,然后設定src="@mipmap/yangtu",就顯示了我們的樣圖,最后為了保持距離美,設定layout_marginTop=“20dp”,代碼如下:

    <ImageView
        android:id="@+id/pt_iv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:src="@mipmap/yangtu"
        android:layout_marginTop="20dp"/>

好了,我們來看下效果圖:
在這里插入圖片描述
至此,我們的布局就繪制完成了!

我們來撰寫下MainActivity的基本框架

可以先來看下什么都沒有的MainActivity,里面只有onClick()和restart()兩個新的方法,這是在上面布局中設定的方法,onClick是圖片按鈕的點擊事件,restart是重新開始按鈕的點擊事件,這兩個方法的具體實作邏輯會在下面講到,

public class MainActivity extends AppCompatActivity {
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		   super.onCreate(savedInstanceState);
		// 設定要顯示的視圖
		   setContentView(R.layout.activity_main);
		}
	// 圖片按鈕的點擊事件	    
	public void onClick(View view) {
	
	}
	/* 重新開始按鈕的點擊事件*/
    public void restart(View view) {
    
  	}
}	  	

這里我們要做的是把所有在布局中用到的控制元件定義好,然后初始化這些控制元件

先來定義九個圖片按鈕,命名方法也是00,01這樣的橫縱坐標,一個重啟按鈕和一個顯示時間的文本框

//  定義九個圖片按鈕,命名方法也是00,01這樣的橫縱坐標
    ImageButton ib00,ib01,ib02,ib10,ib11,ib12,ib20,ib21,ib22;
//   一個重啟按鈕
    Button restartBtn;
//  一個顯示時間的文本框
    TextView timeTv;

然后我們在onCreate中定義一個initView()方法,這個方法是用來初始化控制元件的

//      初始化layout控制元件的方法
        initView();

然后創建該方法,在該方法里面初始化定義的控制元件,通過findViewById()進行系結控制元件,將宣告的變數和layout中對應的控制元件進行系結,實作參考的效果,代碼如下:

/* 初始化控制元件:系結9個圖片按鈕,1個顯示時間的文本框,1個重啟按鈕*/
    private void initView() {
        ib00 = findViewById(R.id.pt_ib_00x00);
        ib01 = findViewById(R.id.pt_ib_00x01);
        ib02 = findViewById(R.id.pt_ib_00x02);
        ib10 = findViewById(R.id.pt_ib_01x00);
        ib11 = findViewById(R.id.pt_ib_01x01);
        ib12 = findViewById(R.id.pt_ib_01x02);
        ib20 = findViewById(R.id.pt_ib_02x00);
        ib21 = findViewById(R.id.pt_ib_02x01);
        ib22 = findViewById(R.id.pt_ib_02x02);
        timeTv = findViewById(R.id.pt_tv_time);
        restartBtn = findViewById(R.id.pt_btn_restart);
    }

初始化的完整代碼,可以作為模板:

public class MainActivity extends AppCompatActivity {
//  定義九個圖片按鈕,命名方法也是00,01這樣的橫縱坐標
    ImageButton ib00,ib01,ib02,ib10,ib11,ib12,ib20,ib21,ib22;
//   一個重啟按鈕
    Button restartBtn;
//  一個顯示時間的文本框
    TextView timeTv;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		   super.onCreate(savedInstanceState);
		// 設定要顯示的視圖
		   setContentView(R.layout.activity_main);
		   initView();
		}
	private void initView() {
        ib00 = findViewById(R.id.pt_ib_00x00);
        ib01 = findViewById(R.id.pt_ib_00x01);
        ib02 = findViewById(R.id.pt_ib_00x02);
        ib10 = findViewById(R.id.pt_ib_01x00);
        ib11 = findViewById(R.id.pt_ib_01x01);
        ib12 = findViewById(R.id.pt_ib_01x02);
        ib20 = findViewById(R.id.pt_ib_02x00);
        ib21 = findViewById(R.id.pt_ib_02x01);
        ib22 = findViewById(R.id.pt_ib_02x02);
        timeTv = findViewById(R.id.pt_tv_time);
        restartBtn = findViewById(R.id.pt_btn_restart);
    }
	// 圖片按鈕的點擊事件	    
	public void onClick(View view) {
	
	}
	/* 重新開始按鈕的點擊事件*/
    public void restart(View view) {
    
  	}
}	  	

2、拼圖游戲時間計時

完成基本作業后,我們思考下——如何實作時間的計時操作,這就相當于計時器的功能,這里我們可以用Handler訊息機制來實作,補充下知識點:

  • Handler:作用就是發送與處理資訊
  • Message:Handler接收與處理的訊息物件

當我們的子執行緒想修改Activity中的UI組件時,我們可以新建一個Handler物件,通過這個物件向主執行緒發送資訊;而我們發送的資訊會先到主執行緒的MessageQueue進行等待,由Looper按先入先出順序取出,再根據message物件的what屬性分發給對應的Handler進行處理!

簡單來說:Handler就是用來發送訊息和處理訊息的一種機制,上面這段話可能聽起來有些懵,不過沒關系,其實沒有這么深奧,下面會讓大家明白怎么使用它來實作計時的,

先定義個時間變數,初值為0,因為從0開始計時

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

然后定義發送和處理訊息的物件handler,我們來重寫handleMessage方法,在方法里面我們進行了if判斷,如果這條訊息的what值為1,那么時間time就+1,然后timeTv顯示時間為time秒,然后繼續向自己發送訊息,

handler.sendEmptyMessageDelayed(1,1000)這句話的意思就是:延時1000毫秒后發送引數what為1的空資訊,這樣它自己就能回圈接收自己發的訊息,實作計時的功能了,就這么簡單,

當然最開始要發送它一條訊息,讓它這個方法運轉起來,我們在onCreate這個方法里面加上了一條
handler.sendEmptyMessageDelayed(1,1000); 這樣在游戲一開始過了1s,handler就發送了一條what為1的空訊息,然后它自己又立馬接收到了,進行時間加1,又自己發送給自己訊息,實作計時!

這是定義的handler的代碼:

//  定義發送和處理訊息的物件handler
    Handler handler = new Handler(){
        @Override
//      重寫handleMessage方法,根據msg中what的值判斷是否執行后續操作
        public void handleMessage(Message msg) {
            if (msg.what==1) {
                time++;
                timeTv.setText("時間 : "+time+" 秒");
//               指定延時1000毫秒后發送引數what為1的空資訊
                handler.sendEmptyMessageDelayed(1,1000);
            }

        }
    };

這是在onCreate方法里面定義的一條訊息

handler.sendEmptyMessageDelayed(1,1000);

我們來看下運行效果:
在這里插入圖片描述
除此之外,我們還需要在重新開始游戲后進行重新計時,這里又要怎么實作呢?

這里我們只需要在restart方法里面先停止handler的訊息發送,保證時間不會再繼續+1了,然后將時間重新歸0,顯示當前時間,最后每隔1s發送引數what為1的訊息msg,這樣就實作了重新開始計時,代碼如下:

/* 重新開始按鈕的點擊事件*/
    public void restart(View view) {
//       停止handler的訊息發送
        handler.removeMessages(1);
//       將時間重新歸0,并且重新開始計時
        time = 0;
        timeTv.setText("時間 : "+time+" 秒");
//      每隔1s發送引數what為1的訊息msg
        handler.sendEmptyMessageDelayed(1,1000);
    }

點擊重新開始后的實作效果:
在這里插入圖片描述
至此,我們的計時功能就實作了!

3、拼圖游戲打亂顯示

首先定義一個image陣列,里面存放每張碎片(九宮格圖片)的id,int型陣列是可以存放圖片的id的,但是不能存放圖片,注意這個區別,

//    將每張碎片的id存放到陣列中,便于進行統一的管理,int型陣列存放的肯定是int型變數
    private int[]image = {R.mipmap.img_xiaoxiong_00x00,R.mipmap.img_xiaoxiong_00x01,R.mipmap.img_xiaoxiong_00x02,
        R.mipmap.img_xiaoxiong_01x00,R.mipmap.img_xiaoxiong_01x01,R.mipmap.img_xiaoxiong_01x02,
       R.mipmap.img_xiaoxiong_02x00,R.mipmap.img_xiaoxiong_02x01,R.mipmap.img_xiaoxiong_02x02};

再宣告一個imageIndex陣列,它來存放上面圖片陣列的下標,一共九張圖片,所以下標為0-8,它存盤的也就是0-8,我們為了讓上面九張圖片被打亂,所以,這里的下標等下會被打亂,

//    宣告上面圖片陣列下標的陣列,隨機排列這個陣列,九張圖片,下標為0-8
    private int[]imageIndex = new int[image.length];
下面我們寫一個函式disruptRandom( ),來實作進入游戲拼圖就打亂顯示的效果

先給下標陣列每個元素賦值,下標是i,值就為i,就是imageIndex[i] = i,

//      給下標陣列每個元素賦值,下標是i,值就為i
        for (int i = 0; i < imageIndex.length; i++) {
            imageIndex[i] = i;
        }

然后進行20次for回圈,隨機選擇兩個角標對應的值進行交換,先定義兩個角標rand1和rand2,
rand1 = (int)(Math.random()*(imageIndex.length-1));這里我來重點解釋一下:
Math.random()產生的亂數為0~1之間的小數 此處說的0~1是包含左不包含右,即包含0不包含1!

ps:我在這里卡了2h至少,因為這個小細節點沒注意到,所以一定不能想當然,要查資料以求準確,

Math.random()的值域為[0,1),然后imageIndex.length-1就是8其實,*8那就是[0,8),再int取整最終值域為{0,1,2,3,4,5,6,7},因為int取整只會取整數位,不會四舍五入!

再用do-while回圈實作了rand2的生成,之所以在do-while里面生成rand2,是為了判斷二次生成的角標和第一次是否相同,不同則break立刻跳出回圈,執行swap交換;若第二次生成的與第一次相同,則重新進入do-while回圈生成rand2,這部分代碼如下:

//        規定20次,隨機選擇兩個角標對應的值進行交換
        int rand1,rand2;
        for (int j = 0; j < 20; j++) {
//            隨機生成第一個角標
//            Math.random()產生的亂數為0~1之間的小數 此處說的0~1是包含左不包含右,即包含0不包含1
//            Math.random()的值域為[0,1),然后*8就是[0,8),再int取整最終值域為{0,1,2,3,4,5,,6,7}
            rand1 = (int)(Math.random()*(imageIndex.length-1));
//            第二次隨機生成的角標,不能和第一次隨機生成的角標相同,如果相同,就不方便交換了
            do {
                rand2 = (int)(Math.random()*(imageIndex.length-1));
//             判斷第一次和第二次生成的角標是否相同,不同則break立刻跳出回圈,執行swap交換
                if (rand1!=rand2) {
                    break;
                }
//             若第二次生成的與第一次相同,則重新進入do-while回圈生成rand2
            }while (true);
            swap(rand1,rand2);
    }

這里的swap方法很簡單,就是交換兩個數的值,只不過這里引數是陣列的下標:

//  交換陣列指定角標(0-7這八個自然數)上的資料
    private void swap(int rand1, int rand2) {
        int temp = imageIndex[rand1];
        imageIndex[rand1] = imageIndex[rand2];
        imageIndex[rand2] = temp;
    }

這里有個整個游戲的一個核心點:我們打亂的拼圖下標是{0,1,2,3,4,5,6,7}這八個,第九張拼圖的下標是不參與打亂的,有同學問為什么?是因為第九張圖片是不顯示出來的,而且不會參與到拼圖中,所以我們是將第九個圖片按鈕就設定成第九張圖片,然后invisible,

最后我們將每個圖片按鈕設定圖片,這時候 imageIndex[i]就是被打亂的下標,有可能是這樣的順序:{2,6,5,4,1,7,0,3,8},也有可能是這樣的順序{1,3,0,5,2,7,4,6,8}等等,不管怎么樣, imageIndex[8]一直是8,上面解釋過,代碼如下:

//       ib00是系結的第一塊圖片按鈕,設定圖片資源,
//       imageIndex[i]就是被打亂的下標,然后image[x]就表示對應下標為x的圖片的id
        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]]);

綜上,disruptRandom()的整體邏輯代碼如下:

//  隨機打亂陣列當中元素,以不規則的形式進行圖片顯示
    private void disruptRandom() {
//      給下標陣列每個元素賦值,下標是i,值就為i
        for (int i = 0; i < imageIndex.length; i++) {
            imageIndex[i] = i;
        }
//        規定20次,隨機選擇兩個角標對應的值進行交換
        int rand1,rand2;
        for (int j = 0; j < 20; j++) {
//            隨機生成第一個角標
//            Math.random()產生的亂數為0~1之間的小數 此處說的0~1是包含左不包含右,即包含0不包含1
//            Math.random()的值域為[0,1),然后*8就是[0,8),再int取整最終值域為{0,1,2,3,4,5,,6,7}
            rand1 = (int)(Math.random()*(imageIndex.length-1));
//            第二次隨機生成的角標,不能和第一次隨機生成的角標相同,如果相同,就不方便交換了
            do {
                rand2 = (int)(Math.random()*(imageIndex.length-1));
//             判斷第一次和第二次生成的角標是否相同,不同則break立刻跳出回圈,執行swap交換
                if (rand1!=rand2) {
                    break;
                }
//             若第二次生成的與第一次相同,則重新進入do-while回圈生成rand2
            }while (true);
//            交換兩個角標上對應的值
            swap(rand1,rand2);
        }
//        隨機排列到指定的控制元件上
//        ib00是系結的第一塊圖片按鈕,設定圖片資源,imageIndex[i]就是被打亂的圖片陣列下標,然后image[x]就表示對應下標為x的圖片的id
        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]]);

    }

實作效果:
在這里插入圖片描述

4、拼圖游戲碎片位置切換

我們完成亂序后,這時候拼圖碎片還不能移動,所以我們要設定點擊事件,來移動拼圖,

拼圖移動的規則也要注意一下:只有和空白區域在同一行或者同一列相鄰的拼圖才能移動,只要知道了這個邏輯,實作起來就不難了,

我們來撰寫九個圖片按鈕的onClick()方法

這里因為九個id不同的imagebutton點擊事件的邏輯相同,所以我們使用switch 陳述句來撰寫,根據它們的id來執行移動,按照從左到右、從上到下的順序進行了case設定,移動我們定義了move()函式,將它單獨封裝成了一個方法,下面就會講到,點擊事件的代碼如下:

 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()方法

先定義變數,imageX是每行的圖片個數,imageY是每列的圖片個數,imgCount是圖片的總數目,也就是9個,blankSwap是空白區域的位置,就是8,這里的位置我們還是按照從左到右、從上到下的順序排列的,第一張圖片的位置是0,對照九宮格應該理解了吧,

blankImgid就是空白區域的按鈕id,我們這里直接固定了R.id.pt_ib_02x02,就是第九個圖片按鈕,它一直是空白區域!

//     每行的圖片個數
    private int imageX = 3;
//     每列的圖片個數
    private int imageY = 3;

//    圖片的總數目
    private int imgCount = imageX*imageY;
//    空白區域的位置
    private int blankSwap = imgCount-1;
//    初始化空白區域的按鈕id
    private int blankImgid = R.id.pt_ib_02x02;

定義完要用到的變數,我們來寫move方法,這里我每句都寫上了注釋,這里就不再贅述了,
強調幾點:

  1. 可以移動的條件有兩個:
    1.在同一行,列數相減,絕對值為1,可移動
    2.在同一列,行數相減,絕對值為1,可以移動
  2. 兩個引數: imagebuttonId是被選中的圖片的id,site是該圖片在9宮格的位置(0-8)
  3. 將移動后的圖片按鈕設為不可見的,即顯示為空白區域
  4. 移動之前是不可見的,移動之后將圖示按鈕設定為可見
  5. 進行移動后將改變角標的程序記錄到存盤圖片位置的陣列當中
    /*表示移動指定位置的按鈕的函式,將圖片和空白區域進行交換*/
    //imagebuttonId是被選中的圖片的id,site是該圖片在9宮格的位置(0-8)
    private void move(int imagebuttonId, int site) {
//        判斷選中的圖片在第幾行,imageX為3,所以進行取整運算
        int sitex = site / imageX;
//        判斷選中的圖片在第幾列,imageY為3,所以進行取模運算
        int sitey = site % imageY;
//        獲取空白區域的坐標,blankx為行坐標,blanky為列坐標
        int blankx = blankSwap / imageX;
        int blanky = blankSwap % imageY;
//        可以移動的條件有兩個
//        1.在同一行,列數相減,絕對值為1,可移動   2.在同一列,行數相減,絕對值為1,可以移動
        int x = Math.abs(sitex-blankx);
        int y = Math.abs(sitey-blanky);
        if ((x==0&&y==1)||(y==0&&x==1)){
//            通過id,查找到這個可以移動的按鈕
            ImageButton clickButton = findViewById(imagebuttonId);
//            將這個選中的圖片設為不可見的,即顯示為空白區域
            clickButton.setVisibility(View.INVISIBLE);
//            查找到空白區域的按鈕
            ImageButton blankButton = findViewById(blankImgid);
//            將空白區域的按鈕設定為圖片,image[imageIndex[site]就是剛剛選中的圖片,因為這在上面disruptRandom()設定過
            blankButton.setImageResource(image[imageIndex[site]]);
//            移動之前是不可見的,移動之后將控制元件設定為可見
            blankButton.setVisibility(View.VISIBLE);
//            將改變角標的程序記錄到存盤圖片位置的陣列當中
            swap(site,blankSwap);
//            新的空白區域位置更新等于傳入的點擊按鈕的位置
            blankSwap = site;
//            新的空白圖片id更新等于傳入的點擊按鈕的id
            blankImgid = imagebuttonId;
        }

    }

運行效果:

在這里插入圖片描述
在這里插入圖片描述

5、拼圖游戲成功的條件

上面我們已經實作了拼圖碎片進行移動的效果,但是并沒有拼圖游戲成功的效果和提示,所以,我們要在剛剛的move方法的最后加上一個判斷的方法judgeGameOver();顧名思義:判斷游戲結束,

我們來實作一下判斷游戲結束的邏輯

在方法里面先定義一個loop標志位,然后要遍歷下標陣列,判斷是否它的imageIndex[i]==i,就是說所有拼圖的下標全部對應正確的位置,比如:第1張圖片的下標是0,imageIndex[0]的值也是0,顯示第一張圖片,所有圖片都滿足,也就是說此時拼圖成功,如果一個不滿足,則未成功,所有loop置為false,繼續判斷,

	boolean loop = true;   //定義標志位loop
    for (int i = 0; i < imageIndex.length; i++) {
         if (imageIndex[i]!=i) {
                loop = false;
                break;
            }
      }

如果拼圖成功了,則handler.removeMessages(1)進行停止計時,
而且設定ib00.setClickable(false)禁止玩家繼續移動按鈕,
還有就是第九塊空白區域顯示出圖片,即下標為8的第九張拼圖,

  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);
//            拼圖成功后,第九塊空白顯示出圖片,即下標為8的第九張圖片
            ib22.setImageResource(image[8]);
            ib22.setVisibility(View.VISIBLE);
我們再來實作一下游戲結束時的對話框

對話框要用到AlertDialog.Builder物件,它的使用就是固定套路,我來補充知識點:

  1. 第一步:創建AlertDialog.Builder物件
  2. 第二步:設定對話框的內容:setMessage()方法來指定顯示的內容
  3. 第三步:呼叫setPositive/Negative/NeutralButton()設定:確定,取消,中立按鈕
  4. 第四歩:呼叫create()方法創建這個物件
  5. 第五歩:呼叫show()方法來顯示我們的AlertDialog對話框

非常簡單,按照上面的流程,我們來設定下對話框:

//            彈出提示用戶成功的對話框,并且設定確實的按鈕

//           第一步:創建AlertDialog.Builder物件
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
//          呼叫setIcon()設定圖示,setTitle()或setCustomTitle()設定標題
//           第二步:設定對話框的內容:setMessage()方法來指定顯示的內容
            builder.setMessage("恭喜,拼圖成功!您用的時間為"+time+"秒")
//           第三步:呼叫setPositive/Negative/NeutralButton()設定:確定,取消,中立按鈕
                    .setPositiveButton("確認",null);
//           第四歩:呼叫create()方法創建這個物件
            AlertDialog dialog = builder.create();
//           第五歩:呼叫show()方法來顯示我們的AlertDialog對話框
            dialog.show();

實作效果:
在這里插入圖片描述

6、拼圖游戲重新開始

我們在上面實作了拼圖游戲成功的條件和提示了,現在到了最后一步——如何讓游戲重新開始?

我們來看下拼圖成功后,點擊重新開始,目前只能重新計時,拼圖并沒有打亂,而且第九塊還沒有隱藏,所以,接下來我們的思路很明確,在重新開始的restart方法中撰寫打亂和隱藏圖片的邏輯,
在這里插入圖片描述

我們來實作重新開始游戲時的按鈕狀態還原

首先,這些按鈕已經被設定成不可點擊了,所以我們先要將它們設定為可以點擊,就是設定ib00.setClickable(true),因為這部分代碼都是一樣的,所以我們將它單獨封裝成一個restore方法,

另外,還要還原被點擊的圖片按鈕變成初始化的模樣, ImageButton clickBtn = findViewById(blankImgid)其實就是系結最后一次被隱藏的那塊拼圖,然后clickBtn.setVisibility(View.VISIBLE)將它顯示出來,ImageButton blankBtn = findViewById(R.id.pt_ib_02x02)就是系結的第九塊拼圖,blankBtn.setVisibility(View.INVISIBLE)設定為不可見,最后blankImgid = R.id.pt_ib_02x02來初始化空白區域的按鈕id,

restore()的代碼如下:

//       狀態還原函式,我們把它封裝起來
    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 clickBtn = findViewById(blankImgid);
        clickBtn.setVisibility(View.VISIBLE);
//        默認隱藏第九張圖片
        ImageButton blankBtn = findViewById(R.id.pt_ib_02x02);
        blankBtn.setVisibility(View.INVISIBLE);
//        初始化空白區域的按鈕id
        blankImgid = R.id.pt_ib_02x02;
        blankSwap = imgCount - 1;
    }
最后,我們在restart()中實作重新開始的邏輯
  1. 將狀態還原 將拼圖重新打亂
  2. 停止handler的訊息發送
  3. 將時間重新歸0,并且重新開始計時
  4. 每隔1s發送引數what為1的訊息msg
    /* 重新開始按鈕的點擊事件*/
    public void restart(View view) {
//        將狀態還原
         restore();
//       將拼圖重新打亂
        disruptRandom();
//       停止handler的訊息發送
        handler.removeMessages(1);
//       將時間重新歸0,并且重新開始計時
        time = 0;
        timeTv.setText("時間 : "+time+" 秒");
//      每隔1s發送引數what為1的訊息msg
        handler.sendEmptyMessageDelayed(1,1000);
    }

重新開始游戲后的效果:
在這里插入圖片描述
至此,拼圖游戲的所有功能已經實作完畢,我先休息下,手腕已經打酸了,如果你看到這里,我真的很欣慰,說明你是個很有耐心而且熱愛Android的學生,有熱情有耐心,再困難的東西都可以學會,

五、運行效果

<iframe id="QLMQ1fxh-1629202507261" src="https://live.csdn.net/v/embed/175404" allowfullscreen="true" data-mediaembed="csdn"></iframe>

Android Studio實作拼圖游戲

六、專案總結

這次實作的拼圖游戲,說它簡單,其實它實作起來也并不是那么簡單,還是會有很多比較難的邏輯點,需要思考才能寫出來;說它難,其實也不算難,比起來我前面發的那些專案【天氣預報】、【飲食搭配】來說邏輯實作還是比較簡單的,畢竟它只有一個MainActivity和一個layout,所以,說一個專案的難易得看你選的參照物了,

這篇文章一共25000多個字,820行,我寫這篇文章,不連上寫代碼時間,前后一共11個小時,前面構思和注釋了4個小時,然后具體寫了7個小時,中間只有喝水and上廁所,可以說我完全是按照開發這款拼圖游戲的邏輯順序來寫下這篇教程,就是我們平時怎么開發Android專案,這篇博客就是怎么寫的,

我之所以寫的這么詳細,也是因為現在網上缺少一個從頭到尾講實作程序的Android專案的教程,因為這實在太花時間了,我深有體會,極少有人一步一步地去把實作程序寫出來,但是我還是決定寫下這篇教程,為了讓更多的人喜歡上Android,讓更多的人對Android不再陌生,讓小白們不再望而卻步,讓小白們有個很好的實作案例,這是我的想法,

當然,我也是正在學習Android的選手之一,才疏學淺,知識淺薄,文章中難免會有紕漏和錯誤,還希望大佬們批評指正,

七、專案原始碼

這次的拼圖游戲專案是一個非常好的Android實作案例,涉及到很多常用的控制元件和知識點,希望大家拿到原始碼后,能對照著教程和注釋好好學習掌握,

原始碼幾乎每條陳述句我都加上了注釋,這么良心的博主,點個三連支持下吧,原始碼就送你啦,祝大家身體健康,學習愉快~

鏈接:拼圖游戲原始碼
提取碼:wpqk

面向陽光時,陰影在你背后,背向陽光時,陰影在你眼前,世界從未改變,改變的只是我們面對世界的方向!加油!你值得更好!

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/294980.html

標籤:其他

上一篇:記一次APK反編譯,拿到自己想要的API程序及應用

下一篇:Unity粒子特效系列-龍卷風預制體做好了,unitypackage包直接用!

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more