• 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

  • 相关阅读:
    luogu P2852 [USACO06DEC]Milk Patterns G
    FZOJ 4267 树上统计
    CF1303G Sum of Prefix Sums
    luogu P5311 [Ynoi2011]成都七中
    luogu P5306 [COCI2019] Transport
    SP34096 DIVCNTK
    luogu P5325 【模板】Min_25筛
    luogu P1742 最小圆覆盖
    求两直线交点坐标
    1098: 复合函数求值(函数专题)
  • 原文地址:https://www.cnblogs.com/mercuryli/p/4868380.html
Copyright © 2020-2023  润新知