繼續學習了極客學院的實戰路徑課程,講到了2048游戲的編寫過程,我在這里作個總結分享給大家(結果會附源代碼和我改寫后的代碼):
這里主要包括兩個方面:1.2048界面的繪制 2.2048算法邏輯的實現 3.添加隨機數 4.判斷游戲結束
先看效果圖(真機上模擬圖):
1.界面的繪制
界面的繪制相對還是比較簡單的。先新建一個card卡片類,這個類主要是描述在效果圖中那16個小方塊
/* * 這個類主要用來初始化2048游戲中的方塊 */ public class Card extends FrameLayout{ private TextView lable; public Card(Context context) { super(context); lable = new TextView(getContext()); lable.setTextSize(32); lable.setGravity(Gravity.CENTER); lable.setBackgroundColor(0X30FFFFFF); //30表示透明度,透明度范圍是00-ff,后六位是顏色值 //下面設置了Layout_wight和Layout_height分別為match_parent(-1代表match_parent,-2代表wrap_content) LayoutParams lp = new LayoutParams(-1,-1); lp.setMargins(10, 10, 10, 10); //設置card的間距 addView(lable,lp); setNum(0); } int num = 0; public int getNum(){ return num; } public void setNum(int num) { this.num = num; //當cardMap[][]<=0時,設為"" if (num<=0) { lable.setText(""); }else { lable.setText(num+""); } } public boolean equals(Card o) { return getNum()==o.getNum(); } }
然后新建一個GameView的主類,使它繼承GirdLayout,復寫其中的方法。再在此之前需要更改一下main_activity布局文件,包括計分的一個textView,和在外面實現的布局。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.example.game1024.MainActivity" > <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="center_vertical" android:textSize="20sp" android:text="score:"/> <TextView android:id="@+id/cvScore" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="20sp" /> </LinearLayout> <com.example.game1024.GameView android:background="#00ff00" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"> </com.example.game1024.GameView> </LinearLayout>
public class GameView extends GridLayout{ public GameView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initGame(); } public GameView(Context context, AttributeSet attrs) { super(context, attrs); initGame(); } public GameView(Context context) { super(context); initGame(); } public void initGame(){ setColumnCount(4); //設定規定行數為四行 setBackgroundColor(0XffF4A460); setOnTouchListener(new View.OnTouchListener() { private float startX,startY,offsetX,offsetY; @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: //取得點擊時的初始坐標 System.out.println("00000"); startX = event.getX(); startY = event.getY(); break; case MotionEvent.ACTION_UP: //求得各方向的偏移量 System.out.println("0--0"); offsetX = event.getX() - startX; offsetY = event.getY() - startY; /* * 比較在x軸和y軸的偏移量,可以判斷為上下滑動,還是左右滑動 * 接着判斷偏移量的正負,來判斷是具體向哪個方向的滑動 * 下面我寫移動的順序為:向左,右,上,下 */ if (Math.abs(offsetX)>Math.abs(offsetY)) { //當移動的距離大於5dp時才看作移動 if (offsetX<-5) { swipeLeft(); }else if (offsetX>5) { swipeRight(); } }else if (Math.abs(offsetX)<Math.abs(offsetY)) { if (offsetY<-5) { swipeUp(); }else if (offsetY>5) { swipeDown(); } } break; default: break; } return true; } }); } /* * (non-Javadoc) * @see android.view.View#onSizeChanged(int, int, int, int) * 當屏幕的寬高改變時,卡片所占的寬高會隨之改變 */ @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); int cardWidth =(Math.min(w, h)-10)/4; addCard(cardWidth, cardWidth); startGame(); } //建立一個數組用於存儲各個卡片的num private Card cardMap[][] = new Card[4][4]; /* * 添加card卡片的方法 */ private void addCard(int cardwidth, int cardheight){ Card c; for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { c = new Card(getContext()); c.setNum(0); addView(c,cardwidth,cardheight); cardMap[x][y]=c; } } }
2.游戲邏輯的算法
這個注釋比較清楚,大家看注釋吧 ,if(judge)是后面要判斷游戲結束時用的
/* * 移動方面的算法 */ private void swipeLeft(){ boolean judge = false; for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { for (int x1 = x+1; x1 < 4; x1++) { //先判斷在當前點的右邊是否存在num大於零的點,如果是,進行下一步,否的話繼續循環
//判斷當前點是否為空(小於等於零相當於空,在card類中已經設置為了不顯示),為空:將右面的點賦予當前點,同時自身置零。不為空的話判斷當前點是否和
//右面的點相等,相等將當前點的num乘2,並將右面的點置零
if (cardMap[x1][y].getNum()>0) { if (cardMap[x][y].getNum()<=0) { cardMap[x][y].setNum(cardMap[x1][y].getNum()); cardMap[x1][y].setNum(0); /* * 假如在第1個位置有一個數,它將移動到第0個位置,此時繼續循環,如果第2個位置也有數, * 此時因為第0個位置已經存在了數值,所以第2個位置的數不會移動到第一個空白位置,而是保持不變 * 因此需要將x減1,在比較一遍 */ x--; judge = true; }else if (cardMap[x][y].getNum()==cardMap[x1][y].getNum()) { cardMap[x][y].setNum(cardMap[x][y].getNum()*2); cardMap[x1][y].setNum(0); MainActivity.getMainActivity().addScore(cardMap[x][y].getNum()); judge = true; } break; } } } } if (judge) { checkGame(); addRandom(); } } private void swipeRight(){ boolean judge = false; for (int y = 0; y < 4; y++) { for (int x = 3; x >= 0; x--) { for (int x1 = x-1; x1 >=0; x1--) { if (cardMap[x1][y].getNum()>0) { if (cardMap[x][y].getNum()<=0) { cardMap[x][y].setNum(cardMap[x1][y].getNum()); cardMap[x1][y].setNum(0); x++; judge = true; }else if (cardMap[x][y].getNum()==cardMap[x1][y].getNum()) { cardMap[x][y].setNum(cardMap[x][y].getNum()*2); cardMap[x1][y].setNum(0); MainActivity.getMainActivity().addScore(cardMap[x][y].getNum()); judge = true; } break; } } } } if (judge) { checkGame(); addRandom(); } } private void swipeUp(){ boolean judge = false; for (int x = 0; x < 4; x++) { for (int y = 0; y <4; y++) { for (int y1 = y+1 ; y1 <4 ; y1++) { if (cardMap[x][y1].getNum()>0) { if (cardMap[x][y].getNum()<=0) { cardMap[x][y].setNum(cardMap[x][y1].getNum()); cardMap[x][y1].setNum(0); y--; judge = true; }else if (cardMap[x][y].getNum()==cardMap[x][y1].getNum()) { cardMap[x][y].setNum(cardMap[x][y].getNum()*2); cardMap[x][y1].setNum(0); MainActivity.getMainActivity().addScore(cardMap[x][y].getNum()); judge = true; } break; } } } } if (judge) { checkGame(); addRandom(); } } private void swipeDown(){ boolean judge = false; for (int x = 0; x < 4; x++) { for (int y = 3; y >=0; y--) { for (int y1 = y-1 ; y1 >=0; y1--) { if (cardMap[x][y1].getNum()>0) { if (cardMap[x][y].getNum()<=0) { cardMap[x][y].setNum(cardMap[x][y1].getNum()); cardMap[x][y1].setNum(0); y++; judge = true; }else if (cardMap[x][y].getNum()==cardMap[x][y1].getNum()) { cardMap[x][y].setNum(cardMap[x][y].getNum()*2); cardMap[x][y1].setNum(0); MainActivity.getMainActivity().addScore(cardMap[x][y].getNum()); judge = true; } break; } } } } if (judge) { checkGame(); addRandom(); } }
3.添加隨機出現的點:
同樣在GameView中添加方法,startGame()在onSizeChanged()方法中調用
/* * 游戲開始的初始化方法 */ private void startGame(){ MainActivity.getMainActivity().clearScore(); for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { cardMap[x][y].setNum(0); } } addRandom(); addRandom(); } private List<Point> emptyPoint =new ArrayList<Point>(); //添加隨機數的方法 private void addRandom(){ emptyPoint.clear(); for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { //將cardMap中小於0的點放在emptyPoint中 if (cardMap[x][y].getNum()<=0) { emptyPoint.add(new Point(x, y)); } } } //隨機移除emptyPoint中的一個單元 Point p = emptyPoint.remove((int)(Math.random()*emptyPoint.size())); cardMap[p.x][p.y].setNum(Math.random()>0.1?2:4); }
4.判斷游戲結束
/* * 結束游戲方法 * 當存在點和旁邊的數相等時,游戲就不會提示已經結束 */ private void checkGame(){ boolean check = true; ALL: for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { if (cardMap[x][y].getNum()==0|| x>0&&cardMap[x][y].equals(cardMap[x-1][y])|| x<3&&cardMap[x][y].equals(cardMap[x+1][y])|| y>0&&cardMap[x][y].equals(cardMap[x][y-1])|| y<3&&cardMap[x][y].equals(cardMap[x][y+1])) { check = false; break ALL; } } } // 提示游戲失敗 if (check) { new AlertDialog.Builder(getContext()).setTitle("提示").setMessage("游戲失敗").setPositiveButton("再來一次", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { startGame(); } }).show(); } }
我改進的2048:
1.增加了最高分顯示
2.增加了重新開始游戲按鈕
3.增加了顏色變化
Game1024原版:http://pan.baidu.com/s/1sjpCdIl
Game2048改進版地址:http://pan.baidu.com/s/1o6qyfJc