首页 > 代码库 > Android拼图-变形金刚

Android拼图-变形金刚

开篇


学了几个月的Android开发,动手做了一个简单的拼图小游戏,没有使用游戏框架,名字也纯属娱乐,比较粗糙请大家一笑别骂。

游戏界面是一张图片切割的6*6的小图片,并将其中一块拿走,玩家通过不同的操作将所有小图片归到正确的位置便算过关,玩家可以进行的操作有

    • 空位所在行和列的小图片都可以左右上下移动,如图(2,3)为空位,那么第2行的所有图片都可以左右移动,如把(2,0)往右移动,那么(2,0),(2,1),(2,2)就会往右顺移一格,(2,0)变为空位。列位置同理
    • 如果一行没有空位,那么这行可以左右旋转,比如往右旋转那么所有小图片都会往右移动,右边出界的图片会移动带左边

有图有真相,下面是前两关的截图,取了正反派两位大佬擎天柱和威震天的英姿,我自己对起来过,但太费时间,所以写了一个菜单函数一键还原

               image                       image 

               image                        image

游戏的实现思路


  1. 游戏界面的布局使用了AbsoluteLayout,以便与自己控制移动和对齐,整个界面可以想象成一个6*6的单元格,每次移动或旋转后,每块小图片肯定在一个单元格内。每块小的图片都是一个button。button是动态添加到layout里的,每一关游戏开始时都会切割图片并将其设置为button的背景图片。切割图片的代码如下:
    切割图片 yValue = http://www.mamicode.com/i * pieceHeight;"color: #0000ff;">return pieces;    }}

    计算每个Button的width和height,使生成的6*6个button填满屏幕。这里屏幕可能无法完整填满,由于屏幕宽度除以button的宽度会有余数,另外高度方面程序启动时没有办法获取layout区域的高度

    计算Button的width和height

    每一关初始界面的代码,里面用了一个生成不重复的随机数的算法,将切割后的图片放到不同的位置

    初始代码

     

  2. 使用SharedPreferences保存当前是哪一关,当程序重新打开时可以显示正确的图片
  3. 移动和旋转的逻辑
    • 移动:要判断哪个方向移动,代码里的isMoveTo***函数,以isMoveToLeft为例,Touch事件ACTION_MOVE发生时横坐标的移动距离大于一个自己设定的常量并且往左移动的距离大于上下方向的移动就认为是往左移动,这个方法不够精确,大家有好的方法在评论里告诉我. MoveToLeft函数就会把空位与当前button之间的所有button往左一起移动,当弹起手指出发生ACTION_UP事件时就会把移动的位置放置到相应的对齐的位置,可以把整个布局想像成一个grid,每次移动后Button的位置都会在格子内,保证对齐效果
    • 旋转:本行没有空位时可以旋转这一行以改变每个button的列值,功能函数是rotationRow
      移动和旋转;               }                break;            }        }        return true;    }    private boolean isMoveToLeft(MotionEvent event) {        return event.getX() < LEFT_LIMIT && Math.abs(event.getX()) > Math.abs(event.getY());    }    //移到左边空位    //pos:button现在的位置    private void moveLeft(Button btn) {        Point pos = getRowAndColumn(btn);        int blankCol=getLeftBlankColumn(pos);        if(blankCol == -1)            return;       for(int i=blankCol+1;i<=pos.y;i++) {           Button button = grid[pos.x][i];           int x = (i- 1) * layoutXUnit;           int y = pos.x * layoutYUnit;           AbsoluteLayout.LayoutParams params = new AbsoluteLayout.LayoutParams(ctrlWidth, ctrlHeight, x, y);           button.setLayoutParams(params);           grid[pos.x][i- 1] = button;       }        grid[pos.x][pos.y] = null;    }    private boolean isMoveToRight(MotionEvent event) {        return event.getX() > RIGHT_LIMIT && Math.abs(event.getX()) > Math.abs(event.getY());    }    //移到右边空位    //pos:button现在的位置    private void moveRight(Button btn) {        Point pos = getRowAndColumn(btn);        int blankCol= getRightBlankColumn(pos);        if(blankCol == -1)            return;        for(int i=blankCol-1;i>=pos.y;i--) {            Button button = grid[pos.x][i];            int x = (i+1) * layoutXUnit;            int y = pos.x * layoutYUnit;            AbsoluteLayout.LayoutParams params = new AbsoluteLayout.LayoutParams(ctrlWidth, ctrlHeight, x, y);            button.setLayoutParams(params);            grid[pos.x][i + 1] = button;        }        grid[pos.x][pos.y] = null;    }    private int getLeftBlankColumn(Point pos)    {        for(int i=0;i<pos.y;i++)        {            if(grid[pos.x][i] == null)                return  i;        }        return  -1;    }    private int getRightBlankColumn(Point pos)    {        for(int i=pos.y;i< COL;i++)        {            if(grid[pos.x][i] == null)                return  i;        }        return  -1;    }    private int getTopBlankRow(Point pos)    {        for(int i=0;i<pos.x;i++)        {            if(grid[i][pos.y] == null)                return  i;        }        return  -1;    }    private int getDownBlankRow(Point pos)    {        for(int i=pos.x;i< ROW;i++)        {            if(grid[i][pos.y] == null)                return  i;        }        return  -1;    }    private boolean isMoveToTop(MotionEvent event) {        return event.getY() < TOP_LIMIT && Math.abs(event.getY()) > Math.abs(event.getX());    }    //移到上边空位    //pos:button现在的位置    private void moveTop(Button btn) {        Point pos = getRowAndColumn(btn);        int blankRow = getTopBlankRow(pos);        if(blankRow == -1)            return;       for(int i=blankRow+1;i<=pos.x;i++) {           Button button=grid[i][pos.y];           int x = pos.y * layoutXUnit;           int y = (i-1) * layoutYUnit;           AbsoluteLayout.LayoutParams params = new AbsoluteLayout.LayoutParams(ctrlWidth, ctrlHeight, x, y);           button.setLayoutParams(params);           grid[i-1][pos.y] = button;       }        grid[pos.x][pos.y] = null;    }    private boolean isMoveToDown(MotionEvent event) {        return event.getY() > DOWN_LIMIT && Math.abs(event.getY()) > Math.abs(event.getX());    }    //移到下边空位    //pos:button现在的位置    private void moveDown(Button btn){        Point pos = getRowAndColumn(btn);        int rowBlank = getDownBlankRow(pos);        for(int i=rowBlank-1;i>=pos.x;i--) {            Button button=grid[i][pos.y];            int x = pos.y * layoutXUnit;            int y = (i + 1) * layoutYUnit;            AbsoluteLayout.LayoutParams params = new AbsoluteLayout.LayoutParams(ctrlWidth, ctrlHeight, x, y);            button.setLayoutParams(params);            grid[i+1][pos.y] = button;        }        grid[pos.x][pos.y] = null;    }    private boolean isBlank(int x, int y) {        if (x >= ROW || y >= COL || x < 0 || y < 0) {            return false;        }        return grid[x][y] == null;    }    //设置一个按钮的位置,x表示行,y表示列    private void addButtonLayout(Button button, int x, int y){        AbsoluteLayout.LayoutParams layoutParams = new AbsoluteLayout.LayoutParams(ctrlWidth, ctrlHeight, y * layoutXUnit, x * layoutYUnit);        absoluteLayout.addView(button, layoutParams);        grid[x][y]=button;    }    //该行是否有空位    private boolean isFillEntireRow(int row){        for(int j=0;j< COL;j++)        {            if(grid[row][j] == null)                return false;        }        return true;    }    //手指抬起时,确保按钮所在的位置是ctrlWidth的倍数    //btn:事件作用的按钮    private HashMap<Button, Point> adjustButtonLayoutAfterRotation(Button eventButton) {        //pos:按钮的横坐标和纵坐标        Point pos=getRowAndColumn(eventButton);        HashMap<Button, Point> map = new HashMap<Button, Point>();        //本行中每一列的按钮都要重设位置        for (int j = 0; j < COL; j++) {            Button eachButton = grid[pos.x][j];            if (eachButton == null)                continue;            Point point = new Point();            if (isRightRotation) {                point = getRowAndColumn(eachButton);            }            if (isLeftRotation) {                point = getRowAndColumnForLeftRotation(eachButton);           }            //对纵坐标小于大于col的情况进行处理            point.y = point.y < 0 ? point.y + COL : point.y;            point.y = point.y % COL;            int x = point.y * layoutXUnit;            int y = point.x * layoutYUnit;            map.put(eachButton, new Point(point.x, point.y));            Log.e(eachButton.getText() + ": After adjust Y:" + Integer.toString(y) + " X:" + Integer.toString(x), " Row:" + Integer.toString(point.x) + " Column:" + Integer.toString(point.y));            AbsoluteLayout.LayoutParams params = new AbsoluteLayout.LayoutParams(ctrlWidth, ctrlHeight, x, y);            eachButton.setLayoutParams(params);        }        isLeftRotation = false;        isRightRotation =false;        return map;    }    //button:事件作用于的button    private void rotationRow(MotionEvent event, Button button) {        Point pos = getRowAndColumn(button);        for (int j = 0; j < COL; j++) {            Button btn = grid[pos.x][j];            if (btn == null) {                continue;            }            btn.offsetLeftAndRight((int) event.getX());        }    }    //往左旋转时,取right的位置 判定card应在哪个单元格    private Point getRowAndColumnForLeftRotation(View view) {        //取card的右上角坐标        int x, y;        x = view.getRight();        y = view.getTop();        Point point = new Point();        point.x = y / layoutYUnit;        point.y = x / layoutXUnit;        if (x % layoutXUnit == 0 || x < 0) {            point.y--;        }        return point;    }    //判断控件在第几行,第几列    private Point getRowAndColumn(Button view) {        int x = view.getLeft();        int y = view.getTop();        Point point = new Point();        point.x = y / layoutYUnit;        point.y = x / layoutXUnit;        return point;    }
  4. 每一关开始时都会使用一个线程监控本关是否完成,如果完成则进入下一关
    监控线程
  5. ActionBar上添加了一个帮助菜单用于查看整张图片完整的样子,另外让ActionBar能显示OverflowMenu

         image                    image

显示overflowmenu

 

菜单命令

 

Android拼图-变形金刚