• [Android]自己动手做个拼图游戏


    目标

    在做这个游戏之前,我们先定一些小目标列出来,一个一个的解决,这样,一个小游戏就不知不觉的完成啦。我们的目标如下:

    1. 游戏全屏,将图片拉伸成屏幕大小,并将其切成若干块。
    2. 将拼图块随机打乱,并保证其能有解。
    3. 在屏幕上留出一个空白块,当点空白块旁边的块,将这块移动到空白块。
    4. 判断是否已经拼好。

    动图

    实现目标

    1.将图片拉伸成屏幕大小,并将其切成若干块。

    想拉伸成屏幕大小,首先要知道屏幕的大小,Android获得屏幕大小的代码如下:

    DisplayMetrics metrics =new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getRealMetrics(metrics);//sdk17+
    int screenWidth = metrics.widthPixels;//屏幕宽
    int screenHeight = metrics.heightPixels;//屏幕高
    

    将图片拉伸到屏幕大小

    Bitmap back=Bitmap.createScaledBitmap(bitmap,
    MainActivity.getScreenWidth(),
    MainActivity.getScreenHeight(),
    true);
    

    将图片切成若干块

     private final int COL=3;//列,默认3列
     private final int ROW=3;//行,默认3行
     int tileWidth=back.getWidth()/COL;//每一块的宽
     int tileHeight=back.getHeight()/ROW;//每一块的高
     Bitmap[] bitmapTiles =new Bitmap[COL*ROW];
     int idx=0;
     for(int i=0;i<ROW;i++)
     {
         for(int j=0;j<COL;j++)
         {
             bitmapTiles[idx++]=Bitmap.createBitmap(back,
             j*tileWidth,
             i*tileHeight,
    		tileWidth,tileHeight);
         }
     }
    

    2. 将拼图块随机打乱,并保证其能有解。

    这个问题应该是这个小游戏的核心了,有些人在做拼图的时候就随便乱摆,最后发现拼不回来,超级尴尬。要想打乱了还能拼回来,我们呢,就想到了模拟人打乱拼图的方法,就是将空白块与旁边的非空白块交换位置,与旁边哪个非空白块交换是随机的,然后将这个过程重复若干次,重复的次数也是随机的,这样一来,保证了图块的随机,又保证了能拼回来。在这里我们用数字0到N-1(N为块的数量)表示每一块,并用二维数组存储他们。

        private void createIntegerArray(int row,int col)
        {
            array=new int[row][col];
            int idx=0;
            for(int i=0;i<row;i++)
            for(int j=0;j<col;j++)
                array[i][j]=idx++;
        }
    

    下面是打乱块的算法,最后一块是空白块,让它随机与旁边的某一块进行交换,这个过程中要检查数组边界,不要让它越界。

        //四个方向
       private int[][] dir={
            {0,1},//下
            {1,0},//右
            {0,-1},//上
            {-1,0}//左
        };
        /**
         * 移动块的位置
         * @param srcX 初始x位置
         * @param srcY 初始y位置
         * @param xOffset x偏移量
         * @param yOffset y偏移量
         * @return 新的位置,错误返回new Point(-1,-1);
         */
        private Point move(int srcX,int srcY,int xOffset,int yOffset)
        {
            int x=srcX+xOffset;
            int y=srcY+yOffset;
            if(x<0||y<0||x>=col||y>=row)
                return new Point(-1,-1);
    
            int temp=array[y][x];
            array[y][x]=array[srcY][srcX];
            array[srcY][srcX]=temp;
    
            return new Point(x,y);
        }
    
        /**
         * 得到下一个可以移动的位置
         * @param src 初始的点
         * @return
         */
        private  Point getNextPoint(Point src)
        {
            Random rd=new Random();
            int idx=rd.nextInt(4);//,因为有4个方向,所以产生0~3的随机数
            int xOffset=dir[idx][0];
            int yOffset=dir[idx][1];
            Point newPoint=move(src.getX(),src.getY(),xOffset,yOffset);
            if(newPoint.getX()!=-1&&newPoint.getY()!=-1) {
                return newPoint;//找到了新的点
            }
    
           return getNextPoint(src);//没有找到,继续
        }
    
        /**
         * 生成拼图数据
         * @param row
         * @param col
         * @return
         */
        public int[][] createRandomBoard(int row,int col)
        {
            if(row<2||col<2)
                throw new IllegalArgumentException("行和列都不能小于2");
            this.row=row;
            this.col=col;
            createIntegerArray(row,col);//初始化拼图数据
            int count=0;
            Point tempPoint=new Point(col-1,row-1);//最后一块是空白块
            Random rd=new Random();
            int num=rd.nextInt(100)+20;//产生20~119的随机数,表示重复的次数
            while (count<num)
           {
               tempPoint=getNextPoint(tempPoint);//获得下个点,并更新空白块位置
               count++;
            }
            return  array;
        }
    
    

    3. 在屏幕上留出一个空白块,当点空白块旁边的块,将这块移动到空白块。

    留出空白块很简单,由于上面我们将最后一块作为空白块。当我们绘图时,略过它即可。代码实现如下:

        @Override
        protected void onDraw(Canvas canvas) {
            canvas.drawColor(Color.GRAY);
            for(int i=0;i<ROW;i++) {
                for (int j = 0; j < COL; j++) {
                    int idx=dataTiles[i][j];
                    if(idx==ROW*COL-1&&!isSuccess)
                        continue;
                    canvas.drawBitmap(bitmapTiles[idx],
                    j*tileWidth,
                    i*tileHeight,paint);
                }
            }
        }
    

    移动块也很简单,当点击屏幕时,计算其在拼图数据中对应的索引。当计算到点击非空白块就寻找它旁边有没有空白块,有,则将拼图数据中表示空白块和非空白块的数据交换,并刷新View即可

        /**
         * 将屏幕上的点转换成,对应拼图块的索引
         * @param x
         * @param y
         * @return
         */
        private Point xyToIndex(int x,int y)
        {
            int extraX=x%tileWidth>0?1:0;
            int extraY=x%tileWidth>0?1:0;
            int col=x/tileWidth+extraX;
            int row=y/tileHeight+extraY;
    
            return new Point(col-1,row-1);
        }
        /**
        *点击屏幕时发生
        */
    	 @Override
        public boolean onTouchEvent(MotionEvent event) {
            if(event.getAction()==MotionEvent.ACTION_DOWN) {
                Point point = xyToIndex((int) event.getX()
                , (int) event.getY());
    
                for(int i=0;i<dir.length;i++)
                {
                    int newX=point.getX()+dir[i][0];
                    int newY=point.getY()+dir[i][1];
    
                    if(newX>=0&&newX<COL&&newY>=0&&newY<ROW){
                        if(dataTiles[newY][newX]==COL*ROW-1)
                        {
                            int temp=dataTiles[point.getY()][point.getX()];
                            dataTiles[point.getY()][point.getX()]=dataTiles[newY][newX];
                            dataTiles[newY][newX]=temp;
                            invalidate();
                        }
                    }
                }
            }
            return true;
        }
    
    

    4. 判断是否已经拼好

    我们初始化数据时,是从0开始,依次增加作为拼图数据。当拼好的时候,拼图数据也应该是一样的,所以我们比较数组中每一个数据与它的下一个数据,如果每一个数据都小于它的下一个数据,说明数组里面的数据已经从小到大排列好。

    /**
         * 判断是否拼图成功
         * @param arr
         * @return
         */
        public boolean isSuccess(int[][] arr)
        {
            int idx=0;
    
            for(int i=0;i<arr.length;i++)
            {
                for(int j=0;j<arr[i].length&&idx<row*col-1;j++)
                {
    
                    if(arr[idx/row][idx%col]>arr[(idx+1)/row][(idx+1)%col])
                    {
    
                        return false;
    
                    }
                    idx++;
                }
    
            }
            return  true;
        }
    

    拼图游戏技巧

    拼图技巧觉得也有必要说一下,不然有些人就会说:你的算法有问题,这根本拼不好!我也是超级无奈啊!拼图的技巧是,我们先把上面的第一行拼好,然后再把第二行拼好,这样,一直下去~就能完全拼好了。

    总结

    这个小游戏简单,可以拿来练手,还可以拿来装(liao)逼(mei),如果不会,何乐而不看呢。这个小游戏也是将视图和数据分开,代码容易移植。

    项目地址

    https://github.com/luoyesiqiu/PuzzleGame

  • 相关阅读:
    fatal error C1083: 无法打开包括文件:“iostream.h”: No such file or directory
    Dan Saks
    '=' : left operand must be lvalue 左值和右值
    sizeof使用
    stream.js :一个新的JavaScript数据结构
    Kibo:键盘事件捕捉高手
    c中不能用引用的办法
    分布式版本控制工具:git与Mercurial
    非常好的BASH脚本编写教程
    Handler让主线程和子线程进行通信
  • 原文地址:https://www.cnblogs.com/luoyesiqiu/p/10052236.html
Copyright © 2020-2023  润新知