###前言
Android原生开发也可以实现一些简单的小游戏。今天给大家讲解一个Android实现拼图小游戏的知识。游戏拼图,在很多地方应该都是有讲解的了,不过,我今天要讲解的不同在于,我已经将这个拼图游戏功能封装都了一个类——Puzzle中,然后会讲解些在实现过程中需要注意的问题和心得。
今天设计内容:
1. `Puzzle`类的主要方法介绍
2. `Puzzle`在`MainActivity`中的应用
2.1 在游戏界面添加GridLayout布局
2.2 Puzzle在MainActivity中的使用
3. 需要注意的问题及心得
3.1 设置图片为方图,且不能太大
3.2 方块移动过程中不可操作
3.3 在图片上滑动手指,方块不移动
3.4 开局随机打乱图片时出现结束图
3.5 设置游戏生命周期
3.6 心得
4. 效果图和项目结构图
先来波效果图
![](/contentImages/image//oZrhcOnt7hMozkUP6UA.gif)
####一. Puzzle类的主要方法介绍
`Puzzle`作为一个拼图游戏封装类,具备以下几个重要方法:
```
/**初始化(所有参数设置完毕后才能执行)**/
public void init(Context context)
/**游戏结束的监听**/
public Puzzle setOnGameOverListener(OnGameOverListener listener)
/**设置拼图布局控件**/
public Puzzle setGridLayout(GridLayout gridLayout)
/**
* 设置拼图行列数
* @param count 行列数
* @return
*/
public Puzzle setCount(int count)
/**
* 设置拼图图片资源id
* @param imageId
* @return
*/
public Puzzle setImageId(int imageId)
/**是否响应手势玩游戏,默认不支持手势**/
public Puzzle setGestur(boolean gestur)
/**
* 游戏开局打乱次数
* @param difficulty 游戏开局打乱次数,需要大于等于MIN_DIFFICULTY_COUNT
* @return
*/
public Puzzle setDifficulty(int difficulty)
/**开始游戏**/
public void start()
/**
* 手势支持的方法,重写activity的onTouchEvent方法,并添加
* 到activity的dispatchTouchEvent方法中
* @param event 手势对象
*/
public boolean onTouchEvent(MotionEvent event)
```
####二. Puzzle在MainActivity中的应用
#####2.1 在游戏界面添加GridLayout布局
下面贴出`MainActivity`对应的布局文件`activity_main.xml`代码:
```
```
界面很简单,就是在其中布一个`GridLayout`,需要注意的是,这里`GridLayout`的行列不要做设置,应该在代码中动态设置(当然具体处理已经在`Puzzle`中实现,无需使用者操心),然后就是为了游戏体验更好,最好设置`GridLayout`宽度撑全屏,高度自适应。
#####2.2 Puzzle在MainActivity中的使用
下面让我们来看看`Puzzle`在`MainActivity`中的使用吧,废话不多讲,直接上`MainActivity`代码:
```
public class MainActivity extends AppCompatActivity{
private GridLayout mGridLayout;
private Puzzle mPuzzle;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mGridLayout=findViewById(R.id.grid);
mPuzzle=new Puzzle();
//初始化设置
mPuzzle.setGridLayout(mGridLayout)//设置拼图布局(必须设置)
.setCount(3)//设置行列(可选,默认行列3x3)
.setImageId(R.mipmap.ic_test)//设置拼图图片资源id(必须设置)
.setGestur(true)//设置是否支持手势,默认为false,即不支持(可选)
.setDifficulty(50)//游戏开局图片打乱次数(可选)
//游戏结束的监听
.setOnGameOverListener(new Puzzle.OnGameOverListener() {
@Override
public void over() {
LogUtil.i("======游戏结束,over======");
ToastUtil.shortShow("游戏结束,over");
}
})
.init(MainActivity.this);//
//开始游戏
mPuzzle.start();
LogUtil.i("==1===欧,我的天啦,开局了======");
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if(mPuzzle!=null){
return mPuzzle.onTouchEvent(event);
}
return false;
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
if(mPuzzle!=null){
mPuzzle.onTouchEvent(event);
}
return super.dispatchTouchEvent(event);
}
}
```
这里需要注意的是:
- `mPuzzle`的`init()`方法要在初始化时最后一个执行
- 记得重写`MainActivity`的`onTouchEvent(MotionEvent event)`和 `dispatchTouchEvent(MotionEvent event)`方法,为的是支持手势操作
- 要想增加游戏难度,可以通过修改`Puzzle`的两个方法`setCount(int)`和`setDifficulty(int)`的参数,前一个方法是将图片切成多少块的设置,后一个方法是打乱的步数
####三.需要注意的问题及心得
#####3.1 设置图片为方图,且不能太大
这里我实现图片设置和切割用的是`Bitmap`,然后其中用到`Bitmap.create()`方法,如果图片过大,会出现
```
IllegalArgumentException : y + height must be
```
错误,由于拼图游戏整体图片布局采用正方形,即宽高相等,则你设置的图片最好也是方块。由于时间问题,我们对`Bitmap`相关做优化,大家有时间的话可以去优化下。
我做这个demo时,采用的样图尺寸是`720x720`的,仅作参考。
`Puzzle`拼图的实现,需要注意一些问题,接着开讲。
#####3.2 方块移动过程中不可操作
由于为了游戏的可观性,在图片移动过程中做了动画处理,为了防止动画执行期间操作图片切换,需要在动画开始和结束时设置布尔值,当界面状态处在动画执行期间,则点击图片或滑动图片失效
#####3.3 在图片上滑动手指,方块不移动
这是由于`GestureDetector`控制的是界面手势,而`GridLayout`控件对手势操作造成干扰,为了解决这个问题,我们可以重写`Activity`的`dispatchTouchEvent(MotionEvent event)`方法,并重写如下:
```
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
if(mPuzzle!=null){
mPuzzle.onTouchEvent(event);
}
return super.dispatchTouchEvent(event);
}
```
这样在`GridLayout`上滑动时,小方块也可以移动了。
#####3.4 开局随机打乱图片时出现结束图
具体情况是,在开局的时候,我们会设置让图片小方块随机打乱,问题是有出现随机打乱后显示原图的情况。为了规避这个问题,我们需要在随机的时候做些处理:
- 随机时要生成正确的手势方向(图片不能划出越界)
- 随机生成的手势不能成对出现(即不能上次是向上滑动,第二次是向下滑动,这样会导致图片还原)
#####3.5 设置游戏生命周期
给游戏划分生命周期是为了更好的做逻辑处理(可不是为了装酷耍帅哦),我大体将游戏分为三个生命周期:
- 游戏未开始
- 游戏进行中
- 游戏已结束
在开局前,我们需要随机打乱顺序,所以这个段乱顺序的操作要在`游戏未开始`的生命周期中进行,打乱完毕后,将生命周期更新到`游戏进行中`状态。
然后在`游戏进行中`我们需要判断游戏是否结束,当然移动方块的动画操作包括禁止动画时操作界面的限制,都是在这个生命周期中处理。当判断出图片已经复原,则将游戏状态更新为`游戏已结束`
当`游戏已结束`时不需要做任何图片移动及数据交换的操作,在界面的表现就是游戏结束后点击和滑动都不起作用了。
#####3.6 心得
在处理游戏开局前随机打乱的问题上,弄了好一会,主要难点是要取一个随机方向,并且这个方向既要是有效的,又要不是成对出现的。若找不到就一直找,直到找到为止。
这里,我想了两种方式:
- 在集合中随机拿出一个item,然后递归找符合要求的,若不符合要求,则接着在剩余数据中随机找
- 直接打乱集合,然后遍历这个乱序集合,找到符合要求的item
####四.效果图和项目结构图
下面贴出游戏效果图和项目结构图
![](/contentImages/image//oZrhcOnt7hMozkUP6UA.gif)
![](/contentImages/image//ie172jsuDKB3FNWX95I.png)
ok,今天的内容就讲到这里了,谢谢大家。