1. 程式人生 > >android 拼圖小遊戲

android 拼圖小遊戲

拼圖雖是比較小的一個遊戲,但涉及到的邏輯和程式碼也沒那麼簡單,這裡參考慕課網上的教程,採用一個二維陣列來儲存拼圖的小方格,並將拼圖的資料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>