Android 之 2048 的游戲邏輯分析


 

繼續學習了極客學院的實戰路徑課程,講到了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

 


免責聲明!

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



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