android 拼图小游戏
16lz
2021-01-25
拼图虽是比较小的一个游戏,但涉及到的逻辑和代码也没那么简单,这里参考慕课网上的教程,采用一个二维数组来存储拼图的小方格,并将拼图的数据GameData(包括x,y坐标和正确的摆放位置)和视图(Bitmap)分离,并编写相应的游戏逻辑控制方法(控制层controller),很好地体现了MVC的思想。
视频网站:http://www.imooc.com/learn/683
效果图:
源代码:
package com.pintu;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.drawable.BitmapDrawable;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.GestureDetector;import android.view.MotionEvent;import android.view.View;import android.view.animation.Animation;import android.view.animation.TranslateAnimation;import android.widget.GridLayout;import android.widget.ImageView;import android.widget.Toast;public class MainActivity extends AppCompatActivity { //利用二位数组创建若干个游戏小方块 private ImageView[][] iv_game_arr = new ImageView[3][5]; //游戏主界面 private GridLayout gl_main_game ; //当前手势 private GestureDetector mDetector ; private boolean isGameStart; //保存当前空方块的实例 private ImageView iv_null ; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mDetector = new GestureDetector(this, new GestureDetector.OnGestureListener() { @Override public boolean onDown(MotionEvent e) { return false; } @Override public void onShowPress(MotionEvent e) { } @Override public boolean onSingleTapUp(MotionEvent e) { return false; } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { return false; } @Override public void onLongPress(MotionEvent e) { } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { int type = getDirByGes(e1.getX() , e1.getY() , e2.getX() , e2.getY()); changeByDir(type , true ); return false; } }); setContentView(R.layout.activity_main); //初始化游戏的若干个小方块 //获取一张大图 Bitmap bigBm = BitmapFactory.decodeResource(getResources(),R.drawable.pingtu); //每个游戏小方块的宽和高 int width = bigBm.getWidth()/5 ; for( int i = 0 ; i < iv_game_arr.length; i ++ ){ for( int j = 0 ; j < iv_game_arr[0].length ; j++ ){ //根据行和列切成若干个游戏小图片 Bitmap bm = Bitmap.createBitmap(bigBm , j * width , i * width , width , width) ; iv_game_arr[i][j] = new ImageView(this); iv_game_arr[i][j].setImageBitmap(bm); iv_game_arr[i][j].setPadding(2,2,2,2); iv_game_arr[i][j].setTag(new GameData(i , j , bm )); iv_game_arr[i][j].setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { boolean flag = isNearNullImageView((ImageView) v); if(flag){ changeDataByImageView((ImageView) v); } } }); } } //初始化游戏主界面,并添加若干个小方块 gl_main_game = (GridLayout) findViewById(R.id.gl_main_game); for( int i = 0 ; i < iv_game_arr.length; i ++ ){ for( int j = 0 ; j < iv_game_arr[0].length ; j++ ){ gl_main_game.addView(iv_game_arr[i][j]); } } //设置最后一个方块为空 setNullImageView(iv_game_arr[2][4]); //初始化随机打乱顺序的方块 randomMove(); isGameStart = true ; } @Override public boolean onTouchEvent(MotionEvent event) { return mDetector.onTouchEvent(event); } public void changeByDir( int type ){ changeByDir(type , true); } /** * 根据手势的方向,获取空方块相应的相邻位置,如果存在方块,那么进行数据交换 * @param type 1 : 上 , 2 : 下 , 3 : 左 , 4 : 右 * @param isAnim 是否有动画 */ public void changeByDir( int type , boolean isAnim ){ //获取当前空方块的位置 GameData mNullGameData = (GameData) iv_null.getTag(); //根据方向,设置相应的相邻位置的坐标 int new_x = mNullGameData.x ; int new_y = mNullGameData.y ; if( type == 1){ //要移动的方块在当前空方块的下边 new_x ++ ; }else if( type == 2 ){ new_x -- ; }else if( type == 3 ){ new_y ++ ; }else if( type == 4 ){ new_y -- ; } //判断这个新坐标是否存在 if( new_x >= 0 && new_x < iv_game_arr.length && new_y >= 0 && new_y < iv_game_arr[0].length){ //存在的话,开始移动 if( isAnim){ changeDataByImageView(iv_game_arr[new_x][new_y]); }else { changeDataByImageView(iv_game_arr[new_x][new_y],isAnim); } }else { //什么也不做 } } /** * 手势判断,向左滑还是向右滑 * @param start_x 手势的起始点x * @param start_y 手势的起始点y * @param end_x 手势的终止点x * @param end_y 手势的终止点y * @return 1 : 上 , 2 : 下 , 3 : 左 , 4 : 右 */ public int getDirByGes( float start_x , float start_y , float end_x , float end_y ){ //是否是左右方向 boolean isLeftOrRight = (Math.abs(start_x - end_x) > Math.abs(start_y - end_y)) ? true : false; if( isLeftOrRight ){//左右 boolean isLeft = start_x - end_x > 0 ? true : false ; if( isLeft ){ return 3 ; }else { return 4 ; } }else {//上下 boolean isUp = start_y - end_y > 0 ? true : false ; if( isUp ){ return 1 ; }else { return 2 ; } } } //随机打乱顺序 public void randomMove(){ //打乱的次数 for( int i = 0 ; i < 15; i ++ ){ //根据手势开始交换,无动画 int type = (int)(Math.random() * 4 ) + 1 ; changeByDir(type,false); } } public void changeDataByImageView(final ImageView imageView ){ changeDataByImageView(imageView,true); } /** * 动画结束之后,交换两个方块的数据 */ public void changeDataByImageView(final ImageView imageView , boolean isAnim){ if( !isAnim ){ GameData mGameData = (GameData) imageView.getTag(); iv_null.setImageBitmap(mGameData.bm); GameData mNullGameData = (GameData) iv_null.getTag(); mNullGameData.bm = mGameData.bm ; mNullGameData.p_x = mGameData.p_x ; mNullGameData.p_y = mGameData.p_y ; //设置当前点击的方块为空方块 setNullImageView(imageView); if( isGameStart){ isGameOver(); } return; } //创建一个动画,设置好方向,移动距离 TranslateAnimation translateAnimation = null ; if( imageView.getX() > iv_null.getX() ){//当前点击的方块在空方块下边 //往上移动 translateAnimation = new TranslateAnimation(0.1f , -imageView.getWidth(), 0.1f , 0.1f ); }else if ( imageView.getX() < iv_null.getX() ){ //当前点击的方块在空方块上边 //往下移动 translateAnimation = new TranslateAnimation(0.1f , imageView.getWidth(), 0.1f , 0.1f ); }else if ( imageView.getY() > iv_null.getY() ){ //当前点击的方块在空方块右边 //往左移动 translateAnimation = new TranslateAnimation(0.1f , 0.1f , 0.1f , -imageView.getWidth() ); } else if ( imageView.getY() < iv_null.getY() ){ //当前点击的方块在空方块左边 //往右移动 translateAnimation = new TranslateAnimation(0.1f , 0.1f , 0.1f , imageView.getWidth() ); } //设置动画的时长 translateAnimation.setDuration(70); //设置动画结束后是否停留 translateAnimation.setFillAfter(true); //动画结束后交换数据 translateAnimation.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { imageView.clearAnimation(); GameData mGameData = (GameData) imageView.getTag(); iv_null.setImageBitmap(mGameData.bm); GameData mNullGameData = (GameData) iv_null.getTag(); mNullGameData.bm = mGameData.bm ; mNullGameData.p_x = mGameData.p_x ; mNullGameData.p_y = mGameData.p_y ; //设置当前点击的方块为空方块 setNullImageView(imageView); if( isGameStart){ isGameOver(); } } @Override public void onAnimationRepeat(Animation animation) { } }); //执行动画 imageView.startAnimation(translateAnimation); } /** * 设置某个方块为空方块 * @param imageView 当前要设置为空的方块的实例 */ public void setNullImageView( ImageView imageView){ imageView.setImageBitmap(null); iv_null = imageView ; } /** * 判读当前点击的方块与空白方块是否相邻 * @param imageView 所点击的方块 * @return true 相邻 */ public boolean isNearNullImageView( ImageView imageView){ //分别获取当前空方块与点击方块的位置,通过想x,y两边都差1的方式判断 GameData mNullGameData = (GameData) iv_null.getTag(); GameData mGameData = (GameData) imageView.getTag(); if( mNullGameData.y == mGameData.y && mGameData.x+1 == mNullGameData.x){ //当前点击的方块在空方块上面 return true ; } else if( mNullGameData.y == mGameData.y && mGameData.x-1 == mNullGameData.x ){ //当前点击的方块在空方块下面 return true ; }else if( mNullGameData.y == mGameData.y + 1 && mGameData.x == mNullGameData.x ){ //当前点击的方块在空方块左边 return true ; }else if( mNullGameData.y == mGameData.y - 1 && mGameData.x == mNullGameData.x ){ //当前点击的方块在空方块右边 return true ; } return false ; } /** * 每个游戏小方块上要绑定的数据 */ class GameData{ //每个小方块的实际位置x public int x = 0 ; //每个小方块的实际位置y public int y = 0 ; //每个小方块的图片 public Bitmap bm ; //每个小方块的图片的位置x public int p_x = 0 ; //每个小方块的图片的位置y public int p_y = 0 ; public GameData(int x, int y, Bitmap bm) { this.x = x; this.y = y; this.bm = bm; this.p_x = x; this.p_y = y; } /** * 判断小方块的位置是否正确 * @return true:正确 false:不正确 */ public boolean isTrue() { if( x == p_x && y == p_y ){ return true; } return false; } } //判断游戏结束的方法 public void isGameOver(){ boolean isGameOver = true ; //遍历每个游戏小方块 for( int i = 0 ; i < iv_game_arr.length ; i ++ ){ for( int j = 0 ; j < iv_game_arr[0].length ; j ++ ){ //为空的方块不判断,跳过 if( iv_game_arr[i][j] == iv_null ){ continue; } GameData mGameData = (GameData) iv_game_arr[i][j].getTag(); if( !mGameData.isTrue()){ isGameOver = false ; break ; } } } if( isGameOver ){ Toast.makeText(this,"游戏结束",Toast.LENGTH_SHORT).show(); } }}
布局文件就是一个简单的GridLayout
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.pintu.MainActivity"> <GridLayout android:layout_width="match_parent" android:layout_height="match_parent" android:columnCount="5" android:rowCount="3" android:id="@+id/gl_main_game">GridLayout>RelativeLayout>
更多相关文章
- 关于Android中的位置服务解读
- Android(安卓)Studio 完美修改应用包名
- Android(安卓)GPS定位步骤
- 切换tab的时候recyclerview滑动到最底部
- Android(安卓)SDK Manager无法更新问题解决办法
- Android(安卓)GPS学习
- Android中广告条轮播(Banner)的实现
- Android滑动手势侦测方法
- Android下的RTSP客户端搭建