安卓2048源码解析

安卓2048游戏解析
本文解析了一个安卓版2048游戏的源码,包括界面设计与游戏逻辑实现。详细介绍了自定义控件Card、AnimLayer和GameView的作用及实现方式,以及游戏初始化、移动操作和游戏结束判断等关键部分。
安卓2048源码解析
2014年4月24日联系商易上海电子商务网站建设,了解更多
 

2048游戏比来很火,想看下源码,却不会JavaScript。网上搜了搜安卓版的源码,测验测验下来进修。


 uberspot


在https://github.com/uberspot/2048-android 上方发了然一个安卓版的2048代码,于是筹办浏览。却发明源文件中只有一个Java类,MainActivity.java。打开大致看了一下:



// If there is a previous instance restore it in the webview

if (savedInstanceState != null) {

    mWebView.restoreState(savedInstanceState);

} else {

    mWebView.loadUrl("file:///android_asset/2048/index.html");

}


本来是用一个webview对底本的JavaScript进行了封装,应用安卓内部webkit浏览器进行了加载。相当于用浏览器玩网页版的游戏,只能再搜刮了。


 


极客学院


别的找到一个极客学院版本的源码,在网站上方还有视频教程。本文解析的首要内容就是极客学院版本的源代码了,作者是ime。


源码链接:https://github.com/plter/Android2048GameLesson


解析的目标为code\ide\ADT\Game2048Publish目次中的源码版本


 1 界面


界面斗劲简单了,打开activity_main.xml看看,几个TextView,一个按钮,还有三个自定义的控件GameView,AnimLayer,Card。游戏的截图如下:


image


标准控件就不介绍了,介绍一系三个自定义的控件。


 1.1 Card(后文混用Card 卡片 方块三个词语)

类Card持续了FrameLayout,目标是作为游戏中的卡片。卡片数字和样式的实现:



public void setNum(int num) {

        this.num = num;



        if (num<=0) {

            label.setText("");

        }else{

            label.setText(num+"");

        }



        switch (num) {

        case 0:

            label.setBackgroundColor(0 x00000000);//透明色

            break;

        case 2:

            label.setBackgroundColor(0 xffeee4da);

            break;

        case 4:

            label.setBackgroundColor(0 xffede0c8);

            break;

        case 8:

            label.setBackgroundColor(0 xfff2b179);

            break;

        case 16:

            label.setBackgroundColor(0 xfff59563);

            break;

            ……

             default:

            label.setBackgroundColor(0 xff3c3a32);

            break;

        }

}


num<=0注解是空白方格。当前地位上若是没有card,则应用num<=0的card进行调换。card 0没有label,同时底色为透明。除了card 0之外,card 2之后的卡片都有对应的色彩和数字。


 1.2 AnimLayer

类AnimLayer持续了FramLayout,用于动画显现。在极客学院安卓2048最首要由两个动画:卡片移动和卡片呈现。


a) 对于卡片呈现动画:



//目标卡片

public void createScaleTo1(Card target){

    //缩放

     ScaleAnimation sa = new ScaleAnimation(0.1f, 1, 0.1f, 1, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);

    sa.setDuration(100);

    target.setAnimation(null);

    target.getLabel().startAnimation(sa);

   }


b) 对于卡片移动动画:


应用ArrayList<Card> cards用于经管姑且卡片的创建和收受接管(避免每次创建姑且卡片时创建新的对象)

创建一个姑且卡片,从卡片移动到卡片to,当完成动画之后将姑且卡片设为不成见,并应用cards收受接管该卡片。

创建卡片:


private Card getCard(int num){

    Card c;

    if (cards.size()>0) {

        c = cards.remove(0);

    }else{

        c = new Card(getContext());

        addView(c);

    }

    c.setVisibility(View.VISIBLE);

    c.setNum(num);

    return c;

  }


创建卡片时,若是cards不为空,则从cards队首取出一张姑且卡片。(这里认为应用LinkedList<Card>加倍合适姑且卡片经管队列)


收受接管卡片:



private void recycleCard(Card c){

    c.setVisibility(View.INVISIBLE);

    c.setAnimation(null);

    cards.add(c);

  }


收受接管卡片将当前卡片设为不成见,并参加到cards中。


卡片移动:



public void createMoveAnim(final Card ,final Card to,int X,int toX,int Y,int toY){

    //姑且卡片

    final Card c = getCard(.getNum());



    //设置布局

    LayoutParams lp = new LayoutParams(Config.CARD_WIDTH, Config.CARD_WIDTH);

    lp.leftMargin = X*Config.CARD_WIDTH;

    lp.topMargin = Y*Config.CARD_WIDTH;

    c.setLayoutParams(lp);



    if (to.getNum()<=0) {

        to.getLabel().setVisibility(View.INVISIBLE);

    }

    //从卡片地位移动到to卡片

    TranslateAnimation ta = new TranslateAnimation(0, Config.CARD_WIDTH*(toX-X), 0, Config.CARD_WIDTH*(toY-Y));

    ta.setDuration(25);

    ta.setAnimationListener(new Animation.AnimationListener() {



        @Override

        public void onAnimationStart(Animation animation) {}



        @Override

        public void onAnimationRepeat(Animation animation) {}



        //动画停止,将姑且卡片收受接管

        @Override

        public void onAnimationEnd(Animation animation) {

            to.getLabel().setVisibility(View.VISIBLE);

            recycleCard(c);

        }

    });

    c.startAnimation(ta);



  }


1.3 GameView

GameView持续了GridLayout,包含了界面和游戏逻辑两个项目组。这里介绍界面。


界面中斗劲首要的内容就是手势辨认,用于操控格子的移动:



private void initGameView(){

    setColumnCount(Config.LINES);

    setBackgroundColor(0 xffbbada0);

    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://按下坐标

                    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;

            }

            return true;//listener已经处理惩罚了事务

        }

    });

     }


应用了View.OnTouchListener来侦听触摸事务:策画按下和抬起来时offsetX和offsetY,猜测手势的移动。


2 游戏逻辑


上一节介绍了根蒂根基的界面显现,本节介绍营业逻辑,即游戏实现道理。


2.1 游戏初始化

调用函数initGameView()完成游戏初始化:



private void initGameView(){

    setColumnCount(Config.LINES);//设置行数量

    setBackgroundColor(0 xffbbada0);





    setOnTouchListener(new View.OnTouchListener() {

        }

    });

}


设置控件的方格数量,随后设置了控件北京,最后注册了刚才解析过的触摸事务器。此时游戏已经筹办好了,正式开端。


 2.2 开端游戏

函数startGame();正式开端游戏,起首向方格内随机写入两个方块:



public void startGame(){



    MainActivity aty = MainActivity.getMainActivity();

    aty.clearScore();

    aty.showBestScore(aty.getBestScore());



    for (int y = 0; y < Config.LINES; y++) {

        for (int x = 0; x < Config.LINES; x++) {

            cardsMap[x][y].setNum(0);

        }

    }



    addRandomNum();

    addRandomNum();

}


这个函数addRandomNum()向游戏面板内随机参加两个方块,开端游戏:



private void addRandomNum(){

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

    emptyPoints.clear();



    //将所有空格子汇集起来

    for (int y = 0; y < Config.LINES; y++) {

        for (int x = 0; x < Config.LINES; x++) {

            if (cardsMap[x][y].getNum()<=0) {

                emptyPoints.add(new Point(x, y));

            }

        }

    }



    if (emptyPoints.size()>0) {

        //随机地位生成一个card

        Point p = emptyPoints.remove((int)(Math.random()*emptyPoints.size()));

        int num = Math.random()>0.1?2:4;

        cardsMap[p.x][p.y].setNum(num);

        MainActivity.getMainActivity().getAnimLayer().createScaleTo1(cardsMap[p.x][p.y]);

    }

}


函数addRandomNum()向面板中空的格子中随机生成一个卡片。起首汇集面板中所有空的地位,汇集到一个List中,最后生成随机数,随机生成一个数字,并完成天活泼画。


 2.3 移动

2048游戏经由过程游戏中所有的方格朝某个标的目标移动,归并雷同数字的方块。有四个函数负责移动,分别是高低阁下,这里只解析一个标的目标。



private void swipeLeft(){



    boolean merge = false;//是否归并卡片, 1空卡片和已有卡片归并 2两个数字雷同的卡片归并



    for (int y = 0; y < Config.LINES; y++) {//对所有列

        for (int x = 0; x < Config.LINES; x++) {

            //搜检当前点的右侧是否有非空卡片(非空:num>=2)

            for (int x1 = x+1; x1 < Config.LINES; x1++) {

                if (cardsMap[x1][y].getNum()>0) {//若是右边有非空卡片



                    if (cardsMap[x][y].getNum()<=0) {//当前坐标上没有格子(空卡片和已有卡片归并)



                        MainActivity.getMainActivity().getAnimLayer().createMoveAnim(cardsMap[x1][y],cardsMap[x][y], x1, x, y, y);



                        cardsMap[x][y].setNum(cardsMap[x1][y].getNum());

                        cardsMap[x1][y].setNum(0);



                        x--;//和空卡片归并,还须要从当前地位策画(不然:|0|2|2|2|左移之后变为|2|2|2|0|)

                        merge = true;



                    }else if (cardsMap[x][y].equals(cardsMap[x1][y])) {

                        MainActivity.getMainActivity().getAnimLayer().createMoveAnim(cardsMap[x1][y], cardsMap[x][y],x1, x, y, y);

                        cardsMap[x][y].setNum(cardsMap[x][y].getNum()*2);

                        cardsMap[x1][y].setNum(0);



                        MainActivity.getMainActivity().addScore(cardsMap[x][y].getNum());

                        merge = true;

                    }



                    break;

                }

            }

        }

    }



    //只要有随便率性一行产生过卡片移动,则须要产生新的卡片

    if (merge) {



        addRandomNum();

        checkComplete();//断定当前游戏是否失败

    }

}


左移,针对面板中所有列,将每行的方块向左移动。在两种景象产生卡片归并:


1 当前地位为空卡片,右侧为非空卡片,归并后当前地位卡片Num为右侧卡片,右侧卡片清零。


2 当前地位为非空卡片,右侧卡片数值和它相等,归并后当前地位卡片数量翻倍,右侧卡片清零。


从游戏角度来讲:1 对应卡片纯真的移动,2 对应两张雷同卡片的归并。是以,只要产生卡片本质上的移动,就应当随机再临盆一个卡片,调用addRandomNum()。


2.4 游戏停止的断定

每次产生卡片移动,都要搜检游戏还可否持续,是否已经停止。函数checkComplete()完成游戏失败(感触感染叫做checkFailure()更好)的搜检:



private void checkComplete(){



    boolean complete = true;



ALL:

    for (int y = 0; y < Config.LINES; y++) {

        for (int x = 0; x < Config.LINES; x++) {

            //满足随便率性两个前提,游戏就可以持续:1 有空的格子,2 有可以归并的卡片

            if (cardsMap[x][y].getNum()==0||//1 有多余空间

                    (x>0&&cardsMap[x][y].equals(cardsMap[x-1][y]))||//2 和左面相等

                    (x<Config.LINES-1&&cardsMap[x][y].equals(cardsMap[x+1][y]))|//2 和右面相等

                    (y>0&&cardsMap[x][y].equals(cardsMap[x][y-1]))||//2 和上方相等

                    (y<Config.LINES-1&&cardsMap[x][y].equals(cardsMap[x][y+1]))) {//2 和下面相等



                complete = false;

                break ALL;

        }

    }

    if (complete) {

        new AlertDialog.Builder(getContext()).setTitle("你好").setMessage("游戏停止").setPositiveButton("从头开端", new DialogInterface.OnClickListener() {



            @Override

            public void onClick(DialogInterface dialog, int which) {

                startGame();

            }

        }).show();

    }



}


游戏可以持续的两个前提:有空的格子,或者还有可以或许归并的卡片。


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值