美文网首页
2048游戏开发

2048游戏开发

作者: 努力努力再努力_姜姜 | 来源:发表于2016-05-27 12:04 被阅读516次
    游戏布局的设计
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout    
    xmlns:android="http://schemas.android.com/apk/res/android"    
    android:layout_width="match_parent"    
    android:layout_height="match_parent"    
    android:orientation="vertical"    >    
    <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/show_score"            
    android:layout_width="wrap_content"            
    android:layout_height="wrap_content" />   
     </LinearLayout>   
     <!-- 铺满剩余空间-->
       <!-- 将类和xml绑定到一起-->    
    <com.jiangjiang.game2048.GameView
     android:layout_width="match_parent" 
    android:layout_height="0dp"      
    android:layout_weight="1"     
    android:id="@+id/gameView">    
    </com.jiangjiang.game2048.GameView>
    </LinearLayout>
    
    实现2048游戏的主类
    • 新建一个类GridView,继承自GridLayout
      从xml资源中能够访问,添加能够传入的构造方法,三个构造方法。
    • 初始化的方法,从哪个构造方法中都能执行到。
    • 绑定xml,拷贝类的全路径到xml文件中
    public class GameView extends GridLayout {    
            public GameView(Context context) {        
                        super(context);        
                        initGameView();    
            }    
             public GameView(Context context, AttributeSet attrs) 
              {
                   super(context, attrs);
                    initGameView();
                } 
             public GameView(Context context, AttributeSet attrs, int defStyleAttr)
             {
                      super(context, attrs, defStyleAttr); 
                       initGameView();
              }
    }
    
    
    android平台的触控交互设计
    • 纪录用户手指按下和离开的位置,即可判断用户的意图
    • onTouch()方法的返回值一定是True;如果是false的话,只会侦听到一个TouchDown的事件。TouchMove和TouchUp事件是侦听不到的。返回false,是通知系统我的TouchDown没有触发成功,然后后续事件是不会触发的。
        private float startX,startY,offsetX,offsetY;//纪录x、y 轴的偏移
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            //纪录用户手指按下和离开的位置,即可判断用户的意图
            switch (event.getAction())
            {
                case MotionEvent.ACTION_DOWN:
                    startX = event.getX();//纪录x的位置
                    startY = event.getY();//纪录y的位置
                    break;
                case MotionEvent.ACTION_UP:
                    offsetX = event.getX()-startX;//计算x的偏移量
                    offsetY = event.getY()-startY;//计算y的偏移量
                    if (Math.abs(offsetX)>Math.abs(offsetY))
                    //判断x的绝对值大还是y的绝对值,x>y说明是水平方向
                     {
                        if(offsetX<-5)//向左,给一定的误差范围5
                        {
                            swipeLeft();
                        }else if (offsetX > 5)//向右
                         {
                             swipeRight();
                        }
                    }else{
                        if (offsetY<-5)//向上
                        {
                            swipeUp();
                        }else if(offsetY > 5)//向下
                        {
                            swipeDown();
                        }
                    }
                    break;
            }
            return true;//一定要返回true
        }});
    
    游戏卡片类的实现
    • 将游戏的方格抽象成卡片
    public class Card extends FrameLayout {
        public Card(Context context) {
            super(context);
            label = new TextView(getContext());
            label.setTextSize(32);
            label.setBackgroundColor(0x33ffffff);
            label.setGravity(Gravity.CENTER);
            LayoutParams lp = new LayoutParams(-1,-1);
            lp.setMargins(10,10,0,0);//设置字之间的间隔
            addView(label,lp);
            setNum(0);//默认情况设置0
        }
        private int num = 0;//与卡片绑定的number
        public int getNum() {
            return num;
        }
        public void setNum(int num) {
            this.num = num;
            if(num <= 0 )
            {
                label.setText("");
            }else {
                label.setText(num + "");//setText的参数是字符串
            }
        }
        public boolean equals(Card c) {
            return getNum() == c.getNum();//判断卡片是否相同
        }
        private TextView label;
    }
    
    添加卡片类

    将方块抽象成卡片

    public class Card extends FrameLayout {
        public Card(Context context) {
            super(context);
            label = new TextView(getContext());//初始化呈现文字的textview
            label.setTextSize(32);
            label.setBackgroundColor(0x33ffffff);//设置文本框的背景
            label.setGravity(Gravity.CENTER);
            LayoutParams lp = new LayoutParams(-1,-1);
            //填充满整个父级容器
            lp.setMargins(10,10,0,0);//设置卡片之间的间隔
            addView(label,lp);//添加进
            setNum(0);//默认的number
        }
        private int num = 0;//与卡片绑定的number
        public int getNum() {
            return num;
        }
        public void setNum(int num) {
            this.num = num;
            if(num <= 0 )
            {
                label.setText("");
            }else {
                label.setText(num + "");
                //setText的参数是字符串。所以不能直接传num进去
            }
        }
        //判断卡片是否相同
        public boolean equals(Card c) {
            return getNum() == c.getNum();
        }
        private TextView label;
    }
    
    添加卡片
    • 每一种手机的宽高都不相同,为了让卡片的大小适应手机,所以需要动态的计算宽高,这样子在任何手机看起来卡片都铺满整个手机
    • 动态计算,需要重写父类的方法 onSetChanged()
    • 为了不让手机屏幕发生水平直立的变化,需要设置一下:

    android:screenOrientation="portrait"//屏幕是竖直的,这样子onSetChanged()方法就只会在布局创建的时候被执行。

    //动态的计算卡片的宽高
    @Overrideprotected
     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();
    }
    
    • 添加卡片
    private void addCard(int cardWidth,int cardHeight){
        Card c ;
        //添加16张卡片
        for(int y = 0;y<4;y++)//四列
        {
            for(int x = 0;x<4;x++)//四行
            {
                c = new Card(getContext());
                c.setNum(0);//初始时全部添加0
                addView(c,cardWidth,cardHeight);//添加到当前的GridVIew中
                cardsMap[x][y] = c;//将创建的卡片纪录到二维数组中
            }
        }
    }
    
    • 需要显示4列:

    setColumnCount(4);//说明GridLayout 是4列的

    在游戏中添加随机数
    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()));//随机移除一个点
        //将这个点对应的值设置成2,或者4,比例是9:1
        cardsMap[p.x][p.y].setNum(Math.random()>0.1?2:4);//2:4 = 9:1
    }
    
    • 开始游戏
      首先是清理元素。将卡片中的元素清除
      然后是添加随机数
    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();
    }
    
    实现2048 的逻辑
    • 往左滑的效果
    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++)
                {
                    if (cardsMap[x1][y].getNum()>0)//如果右边不为空
                    {
                        if (cardsMap[x][y].getNum()<=0)//如果当前位置的卡片为空
                        {
                            //将右边的值放到右边去
                            cardsMap[x][y].setNum(cardsMap[x1][y].getNum()) ;
                            cardsMap[x1][y].setNum(0);
                            x--;//再遍历一次
                           merge = true;
                        }else if(cardsMap[x][y].equals(cardsMap[x1][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();
        }
    }
    
    游戏的计分
    • 有合并就有添加分数。再有合并的完成之后,数字是几就加几分。
    • 在游戏开始的时候要清零
    public MainActivity(){
        mainActivity = this;
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        showScore = (TextView)findViewById(R.id.show_score);
    }
    public  void clearScore(){//将score重新归0
        score = 0;
        showScore();
    }
    private void showScore(){//呈现分数
        showScore.setText(score+"");
    }
    public void addScore(int s){//添加score
        score += s;
        showScore();
    }
    private int score = 0;
    private TextView showScore;
    
    检查游戏的结束
    • 卡片没有空位置
    • 没有任意两个相邻的位置上的数字是相同的
    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("你好").setMessage("游戏结束").setPositiveButton("重来", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    startGame();
                }
            }).show();
        }
    }
    

    相关文章

      网友评论

          本文标题:2048游戏开发

          本文链接:https://www.haomeiwen.com/subject/svzxdttx.html