您好,登錄后才能下訂單哦!
本篇文章為大家展示了怎么在Android中實現(xiàn)開心消消樂游戲,內(nèi)容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細(xì)介紹希望你能有所收獲。
布局
一開始設(shè)計的布局是xml里的,但是后來想了想有64個按鈕這樣復(fù)制下去實在是太不好了,所以就把按鈕那一部分用代碼布局了。
private void initBtn(int i,int j) { btn[8*i+j].setBackgroundDrawable (this.getResources().getDrawable(getStyle())); point p = new point(i,j,btn[8*i+j],id); btn[8*i+j].setTag(p); map[i][j] = num; } //…… LinearLayout vlayout = (LinearLayout)findViewById(R.id.vlayout); TableLayout tlayout = new TableLayout(this); TableRow row[] = new TableRow[8]; for(int i=0;i<8;i++){ row[i] = new TableRow(this); row[i].setGravity(Gravity.CENTER); for(int j=0;j<8;j++){ btn[8*i+j] = new ImageButton(this); btn[8*i+j].setLayoutParams(new TableRow.LayoutParams(38,38)); initBtn(i,j); btn[8*i+j].setOnClickListener(listener); row[i].addView(btn[8*i+j]); } tlayout.addView(row[i]); } //……
其中g(shù)etStyle()函數(shù)返回了按鈕的樣式id,該id是隨機(jī)生成的。
point p存儲了關(guān)于按鈕的信息,它在按鈕點擊事件中會被使用。
android中的按鈕有三種狀態(tài):點擊態(tài)、普通態(tài)、焦點態(tài)。最后一個的意思是用戶按方向鍵盤(或類似功能鍵)來控制哪個按鈕處于焦點,這樣就可以不通過鼠標(biāo)對按鈕進(jìn)行操作。當(dāng)然在這里并沒有用到這個狀態(tài),只用到了前面兩種,但是因為已經(jīng)有現(xiàn)成圖片了,就把三種狀態(tài)都寫入了btn?.xml(放在drawable文件夾下),使用的時候直接引用這個xml文件就可以了。
btn1.xml:
<?xml version="1.0" encoding="UTF-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/a1_2" android:state_pressed="true"/> <item android:drawable="@drawable/a1" android:state_focused="false" android:state_pressed="false"/> <item android:drawable="@drawable/a1_1" android:state_focused="true"/> <item android:drawable="@drawable/a1" android:state_focused="false"/> </selector>
判斷是否可消去
在點擊事件中,用兩個變量記錄先后點擊的按鈕信息,這兩個變量是滾動使用的,也就是說,第一次點擊存入變量1,第二次點擊存入變量2,第三次點擊存入變量1,第四次點擊存入變量2……額外有一個布爾變來控制信息存入哪個變量。
每次點擊后,都計算先后兩次點擊的按鈕是否相鄰。如果相鄰,那么交換它們的位置。
我們用二維數(shù)組mark來記錄每個方塊是否可以消去,初始化為0。交換后,先一行行地掃描一遍,檢查是否有相連的三個以上圖案相同的按鈕,如果有的話,把它們都做上標(biāo)記。然后再一列列掃描一遍,同樣檢查是否有相連的三個以上圖案相同的按鈕??傮w復(fù)雜度為O(2*n^2)
當(dāng)然可以有更快的算法,但是在這個代碼里,我們對計算的速度沒有要求,甚至希望它慢一些(因為動畫效果需要在這里停頓一下),所以就沒有必要進(jìn)行優(yōu)化了。
那么mark里存儲些什么呢?一開始我把mark設(shè)計為布爾量,可消去的設(shè)為true。后來寫到消去后更新按鈕后,我突然想到mark里不僅僅可以記錄是否可消去,還可以記錄消去后應(yīng)該更新為什么。
我們知道,消去一行按鈕后,上面的按鈕會掉下來補(bǔ)充空位,也就是說消去的這一行會被上面一行取代。所以我們把這些按鈕的mark賦值為1。
同樣,消去一列n個按鈕后,每個按鈕會被它上面第n個按鈕取代,所以我們把這些按鈕的mark賦值為n。
這樣,在更新的時候,我們只需要遍歷二維數(shù)組mark,根據(jù)它對應(yīng)的值,來采取相應(yīng)的操作就可以了。
在這里,要注意順序性的問題,首先,在賦值mark的時候,應(yīng)該先掃描橫行,再掃描豎列,因為可能會出現(xiàn)十字架或T字型的消去,這里橫縱有重疊,而更新的時候以縱為準(zhǔn),所以后掃描縱列來覆蓋橫行的值。
另外一個要注意的順序,就是在更新的時候,一定要從上往下掃描。如果從下往上掃描的話,下面圖案的更新可能會破壞到上面的圖,那么,上面原來存在的可以被消去的方塊就已經(jīng)被破壞了,但是mark還記錄著消去方塊的索引,這樣就會引起錯的消去。反之,上面圖案的消去是不會破壞下面的圖案的。
判斷地圖是否還存在解
每一輪消去后,我們都需要判斷地圖上是否還存在解,如果不存在,就要進(jìn)行更新
因為僅僅是判斷存在性,算法略有變化。
我們需要考慮到所有的交換情況,以及它們能否產(chǎn)生可行解,一旦找到一個,我們就可以返回true。所以外循環(huán)包含兩個,一個掃描橫向相鄰的所有方塊,一個掃描縱向相鄰的所有方塊。
對于特定的兩個方塊,我們先交換它們,然后對于兩個方塊,都計算它們的十字架區(qū)域是否存在可行解,之后再把它們交換回來:
(十字架區(qū)域)
總體的最壞時間復(fù)雜度是2*(n^2)*2*8。
多線程
在主線程里,按理來說應(yīng)該有消去 — 更新這樣的畫面,但是我發(fā)現(xiàn)android是直接把所有東西都計算了出來,然后再去顯示UI的,而不是邊計算邊顯示,所以我之前設(shè)置的那些一步步更新畫面的代碼一點兒用也沒有,然后我想了想估計是要用多線程來寫,在此之前我沒有寫過多線程的代碼,所以花了一天時間看了多線程并把這部分修正了。
大概的想法是這樣的:每次點擊了兩個相鄰按鈕后,先交換兩個按鈕,然后調(diào)用線程的start()方法。
在線程重寫的run()方法里,先計算是否存在解(find函數(shù)),如果可以消去,每隔0.03s發(fā)送一個消息,通知主線程,主線程設(shè)置按鈕的alpha值減?。ㄔ黾油该鞫龋?,反復(fù)10次,這樣就做出了按鈕慢慢消失的效果。
之后,再停頓0.1s,向主線程發(fā)送一個消息,主線程開始進(jìn)行更新按鈕操作。更新之后,因為掉落下來的按鈕還可能組成新的可消去的部分,所以繼續(xù)調(diào)用線程的start方法,直到不存在可消去的按鈕。
如果不存在解,那么,先判斷這是因為用戶點擊的兩個按鈕無法產(chǎn)生解,還是之前消去后掉落下來的按鈕不會產(chǎn)生解。我們用flag來記錄這一狀態(tài)。如果是前者,先停頓0.3s,再把兩個按鈕的位置交換回來;如果是后者,說明已經(jīng)消去所有按鈕,這時我們檢查是否還存在可行解,如果不存在,向主線程發(fā)送一個消息,通知它更新地圖。
總之就是重寫run,以及消息機(jī)制的使用。特別注意的就是run中絕對不能寫UI的操作,UI的事情只能交給主線程做,所以才需要不斷通知。
還有一個要注意線程之間的執(zhí)行順序,調(diào)試了很久的一個地方,后來發(fā)現(xiàn)是因為主線程那邊還沒計算出結(jié)果,但是次線程已經(jīng)開始新的計算了,所以此線程用到的是主線程沒有更新過的舊數(shù)據(jù)。后來想到的方法就是等主線程算完了,再回來調(diào)用次線程的函數(shù)。
@Override public void run() { if(find()){ flag = false; int n = 10; alpha = 255; while(n--!=0){ wait(30); mHandler.sendEmptyMessage(0); } wait(100); mHandler.sendEmptyMessage(1); } else if(flag==true){ swapMap(p1.x,p1.y,p2.x,p2.y); wait(300); mHandler.sendEmptyMessage(2); } else if(flag==false){ p1 = new point(-2,-2); p2 = new point(-2,-2); if(check()==false){ mHandler.sendEmptyMessage(3); } } }
代碼
main.xml
<?xml version="1.0" encoding="UTF-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:background="@drawable/background" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:id="@+id/vlayout"> <TextView android:id="@+id/text" android:layout_width="fill_parent" android:layout_height="40dip" android:textSize="18sp" android:autoText="true" android:textColor="#000000" android:capitalize="sentences" android:text="開心消消樂" /> </LinearLayout>
MainActivity.java
package com.example.android.market.licensing; import android.annotation.SuppressLint; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.TableLayout; import android.widget.TableRow; import android.widget.TextView; import android.widget.Toast; import android.view.Gravity; import android.view.View; @SuppressLint("NewApi") public class MainActivity extends Activity implements Runnable { private static final String TAG = "App"; private point p1 = new point(-2,-2); private point p2 = new point(-2,-2);; private boolean isPoint1 = true; private int map[][] = new int[8][8]; private int id; private int mark[][] = new int[8][8]; private Thread thread; private int num; private int alpha = 255; boolean flag = false; private int score = 0; private TextView text; ImageButton[] btn = new ImageButton[64]; private int getStyle(){ num = (int)(1+Math.random()*(7-1+1)); switch(num){ case 1:return id = R.drawable.btn1; case 2:return id = R.drawable.btn2; case 3:return id = R.drawable.btn3; case 4:return id = R.drawable.btn4; case 5:return id = R.drawable.btn5; case 6:return id = R.drawable.btn6; case 7:return id = R.drawable.btn7; default:return id = 0; } } public MainActivity() { } private void initBtn(int i,int j) { btn[8*i+j].setBackgroundDrawable (this.getResources().getDrawable(getStyle())); point p = new point(i,j,btn[8*i+j],id); btn[8*i+j].setTag(p); map[i][j] = num; } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); LinearLayout vlayout = (LinearLayout)findViewById(R.id.vlayout); TableLayout tlayout = new TableLayout(this); TableRow row[] = new TableRow[8]; for(int i=0;i<8;i++){ row[i] = new TableRow(this); row[i].setGravity(Gravity.CENTER); for(int j=0;j<8;j++){ btn[8*i+j] = new ImageButton(this); btn[8*i+j].setLayoutParams(new TableRow.LayoutParams(38,38)); initBtn(i,j); btn[8*i+j].setOnClickListener(listener); row[i].addView(btn[8*i+j]); } tlayout.addView(row[i]); } text = new TextView(this); text.setText("分?jǐn)?shù) : 0"); vlayout.addView(tlayout); vlayout.addView(text); while(find()){ updateState(); } thread = new Thread(this); } private void wait(int time) { try { Thread.sleep(time); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public void run() { if(find()){ flag = false; int n = 10; alpha = 255; while(n--!=0){ wait(30); mHandler.sendEmptyMessage(0); } wait(100); mHandler.sendEmptyMessage(1); } else if(flag==true){ swapMap(p1.x,p1.y,p2.x,p2.y); wait(300); mHandler.sendEmptyMessage(2); } else if(flag==false){ p1 = new point(-2,-2); p2 = new point(-2,-2); if(check()==false){ mHandler.sendEmptyMessage(3); } } } void swapImage() { p1.v.setBackgroundDrawable (MainActivity.this.getResources().getDrawable(p2.id)); p2.v.setBackgroundDrawable (MainActivity.this.getResources().getDrawable(p1.id)); int tmp =p1.id; p1.id = p2.id; p2.id = tmp; p1.v.setTag(p1); p2.v.setTag(p2); } private void updateBtn(int x1,int y1,int x2,int y2) { map[x1][y1] = map[x2][y2]; point p = (point)(btn[8*x1+y1].getTag()); p.id = ((point)(btn[8*x2+y2].getTag())).id; btn[8*x1+y1].setTag(p); btn[8*x1+y1].setBackgroundDrawable (MainActivity.this.getResources().getDrawable(p.id)); } private void updateBtn(int i,int j) { btn[8*i+j].setBackgroundDrawable (MainActivity.this.getResources().getDrawable(getStyle())); map[i][j] = num; point p = (point)(btn[8*i+j].getTag()); p.id = id; btn[8*i+j].setTag(p); } private void hideBtn(){ alpha -= 25; for(int i=0;i<8;i++){ for(int j=0;j<8;j++){ if(mark[i][j]!=0&&mark[i][j]!=2){ btn[8*i+j].getBackground().setAlpha(alpha); } } } } private void updateState() { for(int i=0;i<8;i++){ for(int j=0;j<8;j++){ btn[8*i+j].getBackground().setAlpha(255); } } for(int i=0;i<8;i++){ for(int j=0;j<8;j++){ if(mark[i][j]==1){ for(int k=i;k>0;k--){ updateBtn(k,j,k-1,j); } updateBtn(0,j); } else if(mark[i][j]>=3){ if(i-mark[i][j]>=0) { updateBtn(i,j,i-mark[i][j],j); updateBtn(i-mark[i][j],j); } else{ updateBtn(i,j); } } else if(mark[i][j]==2){ updateBtn(i,j); } } } } public Handler mHandler=new Handler() { public void handleMessage(Message msg) { switch(msg.what){ case 0:{ hideBtn(); break; } case 1:{ updateState(); text.setText("分?jǐn)?shù) " + ((Integer)score).toString()); thread.start(); break; } case 2:{ swapImage(); p1 = new point(-2,-2); p2 = new point(-2,-2); break; } case 3:{ Toast.makeText(MainActivity.this, "已自動生成新地圖", Toast.LENGTH_SHORT).show(); for(int i=0;i<8;i++){ for(int j=0;j<8;j++){ initBtn(i,j); } } while(find()){ updateState(); } break; } } super.handleMessage(msg); } }; private boolean find() { for(int i=0;i<8;i++){ for(int j=0;j<8;j++){ mark[i][j] = 0; } } boolean flag = false; // heng for(int i=0;i<8;i++){ int count = 1; for(int j=0;j<7;j++){ if(map[i][j]==map[i][j+1]){ count++; if(count==3){ flag = true; mark[i][j-1] = 1; mark[i][j] = 1; mark[i][j+1] = 1; score += 15; } else if(count>3){ mark[i][j+1] = 1; score += 5; } } else count = 1; } } //shu for(int j=0;j<8;j++){ int count = 1; for(int i=0;i<7;i++){ if(map[i][j]==map[i+1][j]){ count++; if(count==3){ flag = true; if(mark[i][j]==1)score+=10; else score +=15; mark[i-1][j] = 3; mark[i][j] = 3; mark[i+1][j] = 3; for(int k=i-5;k>=0;k--){ mark[k][j] = 2; } } else if(count>3){ mark[i+1][j] = count; for(int k=1;k<count;k++){ mark[i-k+1][j]++; } if(i-2*count+2>=0)mark[i-2*count+2][j] = 0; score += 5; } } else count = 1; } } return flag; } private boolean check(int i,int j) { //shu int count = 1; if(i>=1&&map[i-1][j]==map[i][j]){ count++; if(i>=2&&map[i-2][j]==map[i][j]){ count++; } } if(count>=3)return true; if(i+1<8&&map[i+1][j]==map[i][j]){ count++; if(i+2<8&&map[i+2][j]==map[i][j]){ count++; } } if(count>=3)return true; //heng count = 1; if(j>=1&&map[i][j-1]==map[i][j]){ count++; if(j>=2&&map[i][j-2]==map[i][j]){ count++; } } if(count>=3)return true; if(j+1<8&&map[i][j+1]==map[i][j]){ count++; if(j+2<8&&map[i][j+2]==map[i][j]){ count++; } } if(count>=3)return true; return false; } private void swapMap(int x1,int y1,int x2,int y2) { int tmp = map[x1][y1]; map[x1][y1] = map[x2][y2]; map[x2][y2] = tmp; } private boolean check() { //heng for(int i=0;i<8;i++){ for(int j=0;j<7;j++){ swapMap(i,j,i,j+1); if(check(i,j)){ swapMap(i,j,i,j+1); return true; } if(check(i,j+1)){ swapMap(i,j,i,j+1); return true; } swapMap(i,j,i,j+1); } } //shu for(int j=0;j<8;j++){ for(int i=0;i<7;i++){ swapMap(i,j,i+1,j); if(check(i,j)){ swapMap(i,j,i+1,j); return true; } if(check(i+1,j)){ swapMap(i,j,i+1,j); return true; } swapMap(i,j,i+1,j); } } return false; } ImageButton.OnClickListener listener = new ImageButton.OnClickListener(){//創(chuàng)建監(jiān)聽對象 @SuppressLint("NewApi") public void onClick(View v) { if(isPoint1){ p1 = (point)v.getTag(); isPoint1 = false; } else { p2 = (point)v.getTag(); isPoint1 = true; } if(((p1.x-p2.x==1||p1.x-p2.x==-1)&&p1.y==p2.y)|| (p1.y-p2.y==1||p1.y-p2.y==-1)&&p1.x==p2.x){ flag = true; swapMap(p1.x,p1.y,p2.x,p2.y); swapImage(); thread.start(); } } }; }
point.java
package com.example.android.market.licensing; import android.view.View; public class point { public int x; public int y; View v; int id; point(int _x,int _y,View _v,int _id){ x = _x; y = _y; v = _v; id = _id; } point(int _x,int _y){ x = _x; y = _y; } }
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2010 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.android.market.licensing" android:versionCode="2" android:versionName="1.1"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".MainActivity" android:label="@string/app_name" android:configChanges="orientation|keyboardHidden"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> <!-- Devices >= 3 have version of Android Market that supports licensing. --> <uses-sdk android:minSdkVersion="3" /> <!-- Required permission to check licensing. --> <uses-permission android:name="com.android.vending.CHECK_LICENSE" /> </manifest>
上述內(nèi)容就是怎么在Android中實現(xiàn)開心消消樂游戲,你們學(xué)到知識或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識儲備,歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。