android (拼图游戏)数字推盘的简单实现

android (拼图游戏)数字推盘的简单实现看了徐宜生android群英传的拼图例子,也想参照他的写一个拼图游戏。本文的拼图游戏中对图片处理的方式和书中的基本是一直的,但是游戏方法却与之不同。书中的拼图是通过GridView的点击事件,交换两张图片,达成拼图目的,但是个人认为这样的拼图过于简单,很容易就能达成目标,于是收到小时候玩的数字推盘的游戏的启发,将数字替换成文字,大大增加了游戏的难度(ps:本人目前没玩出来过)。主要使用recyclerView与ontouch结合,实现拼图效果,当然判断拼图是否有解的算法还没给出,敬请期待后续更新。

大家好,欢迎来到IT知识分享网。

看了徐宜生android群英传的拼图例子,也想参照他的写一个拼图游戏。本文的拼图游戏中对图片处理的方式和书中的基本是一直的,但是游戏方法却与之不同。
书中的拼图是通过GridView的点击事件,交换两张图片,达成拼图目的,但是个人认为这样的拼图过于简单,很容易就能达成目标,
于是收到小时候玩的数字推盘的游戏的启发,将数字替换成文字,大大增加了游戏的难度(ps:本人目前没玩出来过)。主要使用recyclerView与ontouch结合,实现拼图效果,当然判断拼图是否有解的算法还没给出,敬请期待后续更新。 
首先简单介绍一下布局:

<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  xmlns:tool="http://schemas.android.com/tools"  android:id="@+id/activity_main"  android:layout_width="match_parent"  android:layout_height="match_parent"  tool:context=".activity.GameActivity">   <android.support.v7.widget.Toolbar  android:id="@+id/toolbar"  android:layout_width="match_parent"  android:layout_height="?actionBarSize"  android:popupTheme="@style/Theme.AppCompat.Light.NoActionBar">   </android.support.v7.widget.Toolbar>   <LinearLayout  android:id="@+id/lay_top"  android:layout_width="match_parent"  android:layout_height="wrap_content"  android:layout_alignParentTop="true"  android:orientation="horizontal">   <LinearLayout  android:layout_width="match_parent"  android:layout_height="wrap_content"  android:layout_weight="1"  android:orientation="vertical">   <TextView  android:layout_width="match_parent"  android:layout_height="wrap_content"  android:layout_gravity="center"  android:layout_margin="@dimen/margin_8dp"  android:ellipsize="end"  android:gravity="center"  android:maxLines="1"  android:text="@string/jigsaw_time"  android:textSize="@dimen/sp_middle" />   <TextView  android:id="@+id/jigsaw_time"  android:layout_width="match_parent"  android:layout_height="wrap_content"  android:layout_gravity="center"  android:layout_margin="@dimen/margin_8dp"  android:ellipsize="end"  android:gravity="center"  android:maxLines="1"  android:textSize="@dimen/sp_middle" />   </LinearLayout>   <LinearLayout  android:layout_width="match_parent"  android:layout_height="wrap_content"  android:layout_weight="1"  android:orientation="vertical">   <TextView  android:layout_width="match_parent"  android:layout_height="wrap_content"  android:layout_gravity="center"  android:layout_margin="@dimen/margin_8dp"  android:ellipsize="end"  android:gravity="center"  android:maxLines="1"  android:text="@string/jigsaw_step"  android:textSize="@dimen/sp_middle" />   <TextView  android:id="@+id/jigsaw_step"  android:layout_width="match_parent"  android:layout_height="wrap_content"  android:layout_gravity="center"  android:layout_margin="@dimen/margin_8dp"  android:ellipsize="end"  android:gravity="center"  android:maxLines="1"  android:textSize="@dimen/sp_small" />  </LinearLayout>  </LinearLayout>   <LinearLayout  android:id="@+id/lay_bottom"  android:layout_width="match_parent"  android:layout_height="wrap_content"  android:layout_alignParentBottom="true"  android:orientation="horizontal">   <Button  android:id="@+id/btn_image"  android:layout_width="match_parent"  android:layout_height="match_parent"  android:layout_margin="@dimen/margin_8dp"  android:layout_weight="1"  android:text="@string/image"  android:textSize="@dimen/sp_middle" />   <Button  android:id="@+id/btn_reset"  android:layout_width="match_parent"  android:layout_height="match_parent"  android:layout_margin="@dimen/margin_8dp"  android:layout_weight="1"  android:text="@string/reset"  android:textSize="@dimen/sp_middle" />   <Button  android:id="@+id/btn_back"  android:layout_width="match_parent"  android:layout_height="match_parent"  android:layout_margin="@dimen/margin_8dp"  android:layout_weight="1"  android:text="@string/back"  android:textSize="@dimen/sp_middle" />  </LinearLayout>   <android.support.v7.widget.RecyclerView  android:id="@+id/recycleView"  android:layout_width="match_parent"  android:layout_height="match_parent"  android:layout_above="@id/lay_bottom"  android:layout_below="@id/lay_top"  android:layout_centerInParent="true"  android:layout_margin="@dimen/margin_8dp"></android.support.v7.widget.RecyclerView> </RelativeLayout> 

android (拼图游戏)数字推盘的简单实现

上面通过相对布局来规划控件位置,上边是对局信息区,中间是游戏区,下边是功能键。(ps:界面简陋,请担待些)。

然后是代码结构:

android (拼图游戏)数字推盘的简单实现

因为只是简单的实现拼图功能,并没有做过多的优化,所以代码结构并不复杂。基本上通过对包和class的命名就能知道其代表的功能。

那么,接下来对功能实现的几个要点进行简单的解释:

第一:图片处理(具体的代码里面有详细的注释):

package com.example.jigsawgame.unilts; /**  * Created by Administrator on 2016/12/27 0027.  */  import android.graphics.Bitmap; import android.graphics.Matrix;  import com.example.jigsawgame.AppContacts; import com.example.jigsawgame.MyAppcalition; import com.example.jigsawgame.bean.ImageBean; import com.example.jigsawgame.infaceView.GameViewListener;  import java.util.ArrayList; import java.util.Collections; import java.util.List;  /**  * @author zhoukeda on 2016/12/27 0027 20:51  * @email:1009973227@qq.com 实现图片分割与自适应  */ public class ImageUnilt {
    private ImageBean imageBean;  private GameViewListener viewListener;  private List<ImageBean> bitmapList = new ArrayList<>();  private List<ImageBean> firstBeanList = new ArrayList<>();   public ImageUnilt(GameViewListener viewListener) {
        this.viewListener = viewListener;  }

    public void reStartBitmap() {
        viewListener.initBitmapView(bitmapList);  }

    public void totalBitmap() {
        viewListener.initBitmapView(firstBeanList);  }

    /**  * 对图片随机排序(新的一局)  */  public void randomBitmap() {
        Collections.shuffle(bitmapList);  for (int i = 0; i <bitmapList.size() ; i++) {
            bitmapList.get(i).setmItemId(i);  }
        viewListener.initBitmapView(bitmapList);  }

    /**  * 切图  * @param type(拼图游戏的类型如38数字推盘415数字推盘)  * @param bitmapSelect(传入要进行拼图的图片)  */  public void creatInitImage(int type, Bitmap bitmapSelect) {
        Bitmap bitmap = null;  int widthItem = bitmapSelect.getWidth() / type;//裁剪后图片的宽度  int heightItem = bitmapSelect.getHeight() / type;//裁剪后图片的高度  //创建图片  for (int i = 0; i < type; i++) {
            for (int j = 0; j < type; j++) {
                bitmap = Bitmap.createBitmap(bitmapSelect,  j * widthItem, i * heightItem, widthItem, heightItem);  //createBitmap(图片左上角x,y坐标,图片宽高)  imageBean = new ImageBean(i * type + j, i * type + j, reSize(ScreenUtil.getScreenWidth(MyAppcalition.getmContext()) / type,  ScreenUtil.getScreenWidth(MyAppcalition.getmContext()) / type, bitmap),(i * type + j)%type,(i * type + j)/type);  //保存图片信息  bitmapList.add(imageBean);  firstBeanList.add(imageBean);  }
        }
        //将被隐藏图片保存下来  AppContacts.lastBitmap = bitmapList.get(type * type - 1);  bitmapList.remove(type * type - 1);  Collections.shuffle(bitmapList);//随机打乱图片  viewListener.initBitmapView(bitmapList);  }

    /**  * 处理图片大小,缩放,放大到合适的位置  *  * @param newWidth  * @param newHeight  * @param bitmap  */  public Bitmap reSize(float newWidth, float newHeight, Bitmap bitmap) {
        Matrix matrix = new Matrix();  matrix.postScale(newWidth / bitmap.getWidth(), newHeight / bitmap.getHeight());  //matrix.postScale(0.5f, 0.5f);// 缩小为原来的一半  return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);  }
}


补充:本demo使用的是mvc模式,在activity中调用uilts的方法后,并不直接用return返回数据,而是通过接口将数据返回到activity中。

第二步:通过recyclerview,将分割好,并随机打乱的图片显示出来:

这个相对简单,由于篇幅,简单解释一下,就不贴代码了(本文最下方有demo的源码提供下载)

GridLayoutManager gridLayoutManager = new GridLayoutManager(this, type);
gameAdapter = new GameAdapter(0, null);
recycleView.setLayoutManager(gridLayoutManager);
recycleView.setAdapter(gameAdapter);

通过recyclerview的Gridmanger可以实现gridview的效果,而且它的item移动的效果,和显示的效果的更加的美观。

@Override
public void initBitmapView(List<ImageBean> bitmaps) {
    if(bitmaps!=null&&!bitmaps.isEmpty()){
        beanList.clear();
        beanList.addAll(bitmaps);
        if (beanList.size() < type * type) {
            beanList.add(new ImageBean(type * type - 1, type * type - 1, null, type - 1, type - 1));
        }
        for (int i = 0; i < beanList.size(); i++) {
            beanList.get(i).setSelectionX(i % type);
            beanList.get(i).setSelectionY(i / type);
            Log.i("###", "initBitmapView: " + beanList.get(i).toString());
        }
        gameAdapter.setNewData(beanList);
        gameAdapter.openLoadAnimation(2);
    }
    recycleView.setOnTouchListener(this);
}

这里,我们是先初始化一个数据是空的的recyclerview,然后,初始化图片操作,当图片获取成功的时候,更新recyclerview,实现触摸事件。这样在我们的图片是从网络获取的时候,出现突发情况(如:网络链接异常),不至于闪退,提高程序的健壮性。

第三:recyclerview触摸事件:

这个触摸事件只有在recyclerview的控件范围内才有效果。

@Override
public boolean onTouch(View v, MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            x = event.getX();
            y = event.getY();
            break;
        case MotionEvent.ACTION_UP:
            int nullX = 0, nullY = 0;
            for (ImageBean imageBean : gameAdapter.getData()) {
                if (imageBean.getmBitmap() == null) {
                    nullX = imageBean.getSelectionX();
                    nullY = imageBean.getSelectionY();
                    break;
                }
            }
            if (Math.abs(event.getX() - x) > 100 || Math.abs(event.getY() - y) > 100) {
                Log.i("###", "onTouch: " + "在滑动了");
                if (Math.abs(event.getX() - x) > Math.abs(event.getY() - y)) {
                    Log.i("###", "onTouch: " + "水平滑动");
                    if (x > event.getX()) {
                        step++;
                        jigsawstep.setText(step+"");
                        gameUnilt.selectionChange(TouthType.right_To_Left, gameAdapter, nullX, nullY, type);
                    } else {
                        step++;
                        jigsawstep.setText(step+"");
                        gameUnilt.selectionChange(TouthType.left_To_Right, gameAdapter, nullX, nullY, type);
                    }
                } else {
                    if (y > event.getY()) {
                        step++;
                        jigsawstep.setText(step+"");
                        gameUnilt.selectionChange(TouthType.bottom_To_Up, gameAdapter, nullX, nullY, type);
                    } else {
                        step++;
                        jigsawstep.setText(step+"");
                        gameUnilt.selectionChange(TouthType.up_To_Bottom, gameAdapter, nullX, nullY, type);
                    }
                    Log.i("###", "onTouch: " + "竖直滑动");
                }
            }
            break;
        case MotionEvent.ACTION_MOVE:
            break;
    }
    return false;
}

实现逻辑:在按下,抬起的时候记录位置,当同一坐标的两个位置的绝对值差值大于100时,判定有滑动事件,然后判定x,y轴上差值的绝对值大的为主要滑动方向(竖直or水平)。最后判定上下左右。

第四:根据滑动方向,决定recyclerview的位置移动策略主要逻辑在Gameunit(这个看下源码稍微想一下,就能理解,没什么技术含量,就不多解释了):

public class GameUnilt {
    private String TAG = "GameUnilt";
    public void selectionChange(TouthType type, GameAdapter gameAdapter, int nullX, int nullY,int range) {
        switch (type) {
            case left_To_Right:
                for (int i = 0; i < gameAdapter.getData().size(); i++) {
                    if (gameAdapter.getData().get(i).getSelectionY() == nullY) {
                        if (gameAdapter.getData().get(i).getSelectionX() == nullX - 1) {
                            ImageBean imageBean = gameAdapter.getData().get(i);
                            gameAdapter.getData().set(i,gameAdapter.getData().get(i+1));
                            gameAdapter.getData().get(i).setSelectionX(i%range);

                            gameAdapter.getData().set(i+1,imageBean);
                            gameAdapter.getData().get(i+1).setSelectionX((i+1)%range);

                            gameAdapter.notifyItemChanged(i);
                            gameAdapter.notifyItemChanged(i + 1);
                            break;
                        }
                    }
                }
                break;
            case right_To_Left:
                for (int i = 0; i < gameAdapter.getData().size(); i++) {
                    if (gameAdapter.getData().get(i).getSelectionY() == nullY) {
                        if (gameAdapter.getData().get(i).getSelectionX() == nullX + 1) {
                            ImageBean imageBean = gameAdapter.getData().get(i-1);
                            gameAdapter.getData().set(i-1,gameAdapter.getData().get(i));
                            gameAdapter.getData().get(i-1).setSelectionX((i-1)%range);

                            gameAdapter.getData().set(i,imageBean);
                            gameAdapter.getData().get(i).setSelectionX((i)%range);

                            gameAdapter.notifyItemChanged(i-1);
                            gameAdapter.notifyItemChanged(i);
                            break;
                        }
                    }
                }
                break;
            case up_To_Bottom:
                for (int i = 0; i < gameAdapter.getData().size(); i++) {
                    if (gameAdapter.getData().get(i).getSelectionX() == nullX) {
                        if (gameAdapter.getData().get(i).getSelectionY() == nullY - 1) {

                            ImageBean imageBean = gameAdapter.getData().get(i);
                            gameAdapter.getData().set(i,gameAdapter.getData().get(i+range));
                            gameAdapter.getData().get(i).setSelectionY(i/range);

                            gameAdapter.getData().set(i+range,imageBean);
                            gameAdapter.getData().get(i+range).setSelectionY((i+range)/range);

                            gameAdapter.notifyItemChanged(i);
                            gameAdapter.notifyItemChanged(i + range);
                            Log.i(TAG, "selectionChange: "+i+"--->"+gameAdapter.getData().get(i).toString());
                            Log.i(TAG, "selectionChange: "+i+"--->"+gameAdapter.getData().get(i+range).toString());
                            break;
                        }
                    }
                }
                break;
            case bottom_To_Up:
                for (int i = 0; i < gameAdapter.getData().size(); i++) {
                    if (gameAdapter.getData().get(i).getSelectionX() == nullX) {
                        if (gameAdapter.getData().get(i).getSelectionY() == nullY + 1) {

                            ImageBean imageBean = gameAdapter.getData().get(i-range);
                            gameAdapter.getData().set(i-range,gameAdapter.getData().get(i));
                            gameAdapter.getData().get(i-range).setSelectionY((i-range)/range);

                            gameAdapter.getData().set(i,imageBean);
                            gameAdapter.getData().get(i).setSelectionY(i/range);

                            gameAdapter.notifyItemChanged(i-range);
                            gameAdapter.notifyItemChanged(i);
                            Log.i(TAG, "selectionChange: "+gameAdapter.getData().get(i-range).toString());
                            Log.i(TAG, "selectionChange: "+gameAdapter.getData().get(i).toString());
                            break;
                        }
                    }
                }
                break;
        }
    }
}

当然,个人认为这里的GameAdapter可以不用传入,而是通过接口将要调整位置的元素位置传递到activity中,会更为规范些...

那么,到这里为止,一个简单的类似数字推盘的拼图游戏就实现出来了,当然还有很多可以优化的地方,如充网络获取更多的图片,拼图成功后的动画,难度关卡的选择,初始化的图片是否有解等等,各位可以自己试试,我后续也会继续更新。谢谢观看

源码下载链接:点击打开链接



                                                        

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/9977.html

(0)

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

关注微信