1. 程式人生 > >學Android--實現2048小遊戲

學Android--實現2048小遊戲

1、遊戲佈局(activity_main.xml)
首先在xml檔案中實現遊戲的整體佈局
(1)新增兩個TextView用來顯示分數

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height
="wrap_content" android:text="@string/score" />
<TextView android:id="@+id/tvScore" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </LinearLayout>

(2)2048遊戲介面的佈局,遊戲介面是通過程式碼來實現的,所以應該用包名加類名的方式來引用

    <com
.jxl.game2048.GameView android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:id="@+id/gameView"> </com.jxl.game2048.GameView>

2、新建遊戲執行的主要類(GameView.java),該類繼承GridLayout
(1)在構造方法中,需要對遊戲進行初始化( 呼叫initGameView()方法 )
initGameView()主要用來初始化遊戲,該方法中需要實現OnTouchListener,通過座標值判斷使用者滑動的方式,響應不同的事件
在initGameView()指定列數,設定背景:
setColumnCount(4);
setBackgroundColor(0xffbbada0);

setOnTouchListener(new OnTouchListener() {       
            private float startX,startY,offsetX,offsetY;        
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                // TODO Auto-generated method stub
                switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    startX = event.getX();
                    startY = event.getY();
                    break;
                case MotionEvent.ACTION_UP:
                    offsetX = event.getX()-startX;
                    offsetY = event.getY()-startY;

                    //在水平方向上移動
                    if(Math.abs(offsetX)>Math.abs(offsetY)){
                        //向左滑動
                        if(offsetX<-5){
                            swipeLeft();
                        //向右滑動
                        }else if(offsetX>5){
                            swipeRight();
                        }
                    }else{
                        //向上滑動
                        if(offsetY<-5){
                            swipeUp();
                        //向下滑動
                        }else if(offsetY>5){
                            swipeDown();
                        }
                    }
                    break;
                default:
                    break;
                }
                return true;
            }
        });

3、將2048的介面看做由4*4的小方塊組成,將這些方塊抽象為Card類,該類繼承自FrameLayout
(1)遊戲進行時小方塊中的數字為2、4、8、16.……初始化所有小方塊,將數字設為0,即數字為0時表示這一格為空
setNum(0);

    private int num = 0;

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }

(2)在構造方法中,需要對小方塊進行初始化,為小方塊新增TextView儲存數字,並設定字型大小,數字對應的顏色等

    private TextView label;
    public Card(Context context) {
        super(context);
        label = new TextView(getContext());
        label.setTextSize(32);
        label.setGravity(Gravity.CENTER);
        setNum(0);
    }
    public void setBGColor(int num){
        switch (num) {
        case 2:
            label.setBackgroundColor(0xffFFF68F);
            break;
        case 4:
            label.setBackgroundColor(0xffFFEC8B);
            break;
        case 8:
            label.setBackgroundColor(0xffFFD700);
            break;
        case 16:
            label.setBackgroundColor(0xffFFC125);
            break;
        case 32:
            label.setBackgroundColor(0xffFF890F);
            break;
        case 64:
            label.setBackgroundColor(0xffFFA500);
            break;
        case 128:
            label.setBackgroundColor(0xffFF8C00);
            break;
        case 256:
            label.setBackgroundColor(0xffFF7F24);
            break;
        case 512:
            label.setBackgroundColor(0xffFF4500);
            break;
        case 1024:
            label.setBackgroundColor(0xffFF0000);
            break;
        case 2048:
            label.setBackgroundColor(0xff7FF00);
            break;
        case 4096:
            label.setBackgroundColor(0xff68228B);
            break;
        default:
            label.setBackgroundColor(0x33ffffff);
            break;
        }
    }

(3)新增一個佈局引數,寬高為-1,表示填充滿整個父類容器:

        LayoutParams lp = new LayoutParams(-1,-1);
        //設定小方塊之間的間距
        lp.setMargins(10, 10, 0, 0);
        //然後將label新增進佈局中:
        addView(label, lp)

(4)在setNum()方法中,將數字轉換成字串並新增到對應的TextView中

    public void setNum(int num) {
        this.num = num;
        if(num<=0){
            label.setText("");
        }else{
            label.setText(num+"");
        }
    }

(5)新增equals()方法判斷兩個卡片上的數字是否相等:

    public boolean equals(Card o) {
        // TODO Auto-generated method stub
        return getNum()==o.getNum();
    }

4、將16個方塊抽象化為4*4的二維陣列,並將這些小方塊新增到佈局中

    private Card[][] cardsMap = new Card[4][4];
    private void addCards(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);
                c.setBGColor(c.getNum());
                addView(c, cardWidth, cardHeight);

                cardsMap[x][y] = c;
            }

        }
    }

5、新建emptyPoint List來存放空的小方塊

private List<Point> emptyPoints = new ArrayList<Point>();

遊戲中新生成的數字在這些空的小方塊中隨機出現

    private void addRandomNum(){

        emptyPoints.clear();

        for (int y = 0; y < 4; y++) {
            for (int x = 0; x < 4; x++) {
                if(cardsMap[x][y].getNum()<=0){
                    emptyPoints.add(new Point(x,y));
                }
            }

        }

        Point p = emptyPoints.remove((int)(Math.random()*emptyPoints.size()));
        //隨機產生24,生成2的概率大於4
        cardsMap[p.x][p.y].setNum(Math.random()>0.1?2:4);
        cardsMap[p.x][p.y].setBGColor(cardsMap[p.x][p.y].getNum());
    }

6、開始遊戲,實現startGame()方法

    private void startGame(){

        MainActivity.getMainActivity().clearScore();

        for (int y = 0; y < 4; y++) {
            for(int x = 0; x < 4; x++){
                cardsMap[x][y].setNum(0);
            }           
        }
        //隨機生成兩個數字
        addRandomNum();
        addRandomNum();
    }  

7、實現遊戲中最重要的部分—使用者滑動後需要呼叫的方法,以向左滑動為例

    private void swipeLeft(){

        boolean merge = false;
        //從上至下的第一行開始
        for (int y = 0; y < 4; y++) {
        //從左至右
            for(int x = 0; x < 4; x++){

                for(int x1 = x+1; x1 < 4; x1++){
                    //當前位置上的值不為0
                    if(cardsMap[x1][y].getNum()>0){
                        //當前方塊左邊的方塊為空,則將當前方塊的值傳到左邊,直到左邊是不為空的方塊為止
                        if(cardsMap[x][y].getNum()<=0){
                            cardsMap[x][y].setNum(cardsMap[x1][y].getNum());
                            cardsMap[x][y].setBGColor(cardsMap[x][y].getNum());
                            cardsMap[x1][y].setNum(0);
                            cardsMap[x1][y].setBGColor(cardsMap[x1][y].getNum());
                            x--;
                            merge = true;
                        //左邊卡片的值不為空且與當前值當等
                        }else if(cardsMap[x][y].equals(cardsMap[x1][y])){
                            cardsMap[x][y].setNum(cardsMap[x][y].getNum()*2);
                            cardsMap[x][y].setBGColor(cardsMap[x][y].getNum());
                            cardsMap[x1][y].setNum(0);
                            cardsMap[x1][y].setBGColor(cardsMap[x1][y].getNum());

                            MainActivity.getMainActivity().addScore(cardsMap[x][y].getNum());
                            merge = true;
                        }
                        break;
                    }
                }
            }           
        }

        if(merge){
            //完成後再新增一個隨機數字
            addRandomNum();
            //並判斷遊戲是否結束
            checkComplete();
        }
    }
    private void checkComplete(){

        boolean complete = true;

        ALL:
        for (int y = 0; y < 4; y++) {
            for(int x = 0; x < 4; x++){
                if(cardsMap[x][y].getNum()==0||
                        (x>0&&cardsMap[x][y].equals(cardsMap[x-1][y]))||
                        (x<3&&cardsMap[x][y].equals(cardsMap[x+1][y]))||
                        (y>0&&cardsMap[x][y].equals(cardsMap[x][y-1]))||
                        (y<3&&cardsMap[x][y].equals(cardsMap[x][y+1]))){

                    complete = false;
                    break ALL;
                }
            }
        }

        if(complete){
            new AlertDialog.Builder(getContext()).setTitle("2048").
            setMessage("遊戲結束").setPositiveButton("重來", new DialogInterface.OnClickListener() {

                @Override
                public void onClick(DialogInterface arg0, int arg1) {
                    // TODO Auto-generated method stub
                    startGame();
                }
            }).show();
        }
    }

8、在GameView中重寫onSizeChanged()方法,動態地計算每個方塊的寬、高:
在AndroidManifest.xml中修改屏幕布局為垂直:
android:screenOrientation=”portrait”

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        // TODO Auto-generated method stub
        super.onSizeChanged(w, h, oldw, oldh);

        //對寬、高求最小值,然後求出每一個小方塊的寬度
        int cardWidth = (Math.min(w,h)-10)/4;
        //將小方塊新增到佈局中
        addCards(cardWidth, cardWidth);
        startGame();
    }

9、在MainActivity中新增addScore()、showScore()、clearScore()方法,實現計分功能

public class MainActivity extends Activity {

    private TextView tvScore;
    private int score = 0;

    public MainActivity(){
        mainActivity = this;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tvScore = (TextView) findViewById(R.id.tvScore);
    }

    public void clearScore(){
        score = 0;
        showScore();
    }

    public void showScore(){
        tvScore.setText(score+"");
    }

    public void addScore(int s){
        score+=s;
        showScore();
    }

    private static MainActivity mainActivity = null;

    public static MainActivity getMainActivity(){
        return mainActivity;

    }

}

注:該文章根據“極客學院”中2048遊戲視訊教程總結而來