学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;

    }

}

源码:
https://github.com/jiangxingling/Game2048

注:该文章根据“极客学院”中2048游戏视频教程总结而来

  • 2
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值