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