【Android 2D 游戏开发(5)】——九宫格拼图(苍老师版)


效果:


开始界面:
这里写图片描述

3x3:
这里写图片描述

4x4:
这里写图片描述

5x5:
这里写图片描述

拼图成功后苍老师对你表示爱意。。
这里写图片描述

图片切割方法

有些朋友看到这篇文章可能会想知道怎么切割图片(虽然我做这个游戏并没有切割图片。。),我就把切割图片的方法放在下面给大家一个参考。

//这是带索引的图片类
public class ImagePiece {

    public int index = 0;

    public Bitmap bitmap = null;
}

//这个切割图片的类
public class ImageSplitter {

    public static List<ImagePiece> split(Bitmap bitmap, int xPiece, int yPiece) {

        List<ImagePiece> pieces = new ArrayList<ImagePiece>(xPiece * yPiece);
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        int pieceWidth = width / 3;
        int pieceHeight = height / 3;
        for (int i = 0; i < yPiece; i++) {
            for (int j = 0; j < xPiece; j++) {
                ImagePiece piece = new ImagePiece();
                piece.index = j + i * xPiece;
                int xValue = j * pieceWidth;
                int yValue = i * pieceHeight;
                piece.bitmap = Bitmap.createBitmap(bitmap, xValue, yValue,
                        pieceWidth, pieceHeight);
                pieces.add(piece);
            }
        }

        return pieces;
    }
}

制作思路:

  • 不切割图片实现切割效果:

如果你看了我前面制作的几个小游戏,你应该已经能猜到我的游戏基本框架了,如果你说你没看过,,,那你还不快去看。。。

Canvas有一个画图的方法: drawBitmap(Bitmap bitmap,Rect srcRect,Rect
desRect,Paint paint);

srcRect是图片的区域,desRect是画布的区域,功能就是把图片的srcRect区域画在画布的desRect区域.

我们就要用这个方法来实现图片切割的效果,虽然看着像把图片切割了,但是实际上我们只是把完整图片的某个区域画出来了而已,这样做的好处显而易见,我们可以节省很多内存,因为我们只需要在内存里存放一张图片,虽然这个小游戏也不用很多内存。。。

  • 万能画笔:
  • -

我们在前面的游戏里实现了自己的可以检测手势的输入事件NextEvent,现在我们再实现一个可以像神笔马良那支笔一样想画什么就画什么的“神笔”

NextGraphics.java

package com.next.puzzle;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;

/**
 * Created by Next on 2016/4/8 0008.
 */
public class NextGraphics {
    private Canvas canvas;
    private Paint paint;
    private Rect srcRect,desRect;

    public NextGraphics(Canvas canvas){
        this.canvas = canvas;
        paint = new Paint();
        paint.setColor(Color.BLACK);
        paint.setAntiAlias(true);//笔迹平滑
        paint.setTextAlign(Paint.Align.CENTER);//文字中间对齐
        srcRect = new Rect();
        desRect = new Rect();
    }

    public NextGraphics(){
        paint = new Paint();
        paint.setColor(Color.BLACK);//默认黑色
        paint.setAntiAlias(true);//笔迹平滑
        paint.setTextAlign(Paint.Align.CENTER);//文字中间对齐
        srcRect = new Rect();
        desRect = new Rect();
    }
    public void setCanvas(Canvas canvas){

        this.canvas = canvas;
    }

    public void drawBitmapTo(Bitmap bitmap,int left,int top,int right,int bottom){
        srcRect.left = 0;
        srcRect.top = 0;
        srcRect.right = bitmap.getWidth();
        srcRect.bottom = bitmap.getHeight();
        desRect.left = left;
        desRect.top = top;
        desRect.right = right;
        desRect.bottom = bottom;
        canvas.drawBitmap(bitmap, srcRect, desRect, null);
    }
    public void drawBitmapFromTo(Bitmap bitmap,int srcLeft,int srcTop,int srcRight,int srcBottom,int desLeft,int desTop,int desRight,int desBottom){
        srcRect.left = srcLeft;
        srcRect.top = srcTop;
        srcRect.right = srcRight;
        srcRect.bottom = srcBottom;
        desRect.left = desLeft;
        desRect.top = desTop;
        desRect.right = desRight;
        desRect.bottom = desBottom;
        canvas.drawBitmap(bitmap, srcRect, desRect, null);
    }
}

有了这个类之后,我们只需要把canvas传入,就可以用自己的画笔画图了,因为这个游戏只有一个画图需要,所以我只在里面添加了两个画图方法,如果你有别的需要也可以扩展自己的绘图方法。

我也不知道我说了这么多有没有人看,我还是直接放代码吧。。。

MainActivity.java

package com.next.puzzle;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.Window;
import android.view.WindowManager;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        setContentView(new GameView(this, getAssets()));
    }
}

NextEvent.java

package com.next.puzzle;

/**
 * Created by Administrator on 2016/4/8 0008.
 */
public class NextEvent {
    public static final int LEFT = 1;
    public static final int RIGHT = 2;
    public static final int UP = 3;
    public static final int DOWN = 4;
    public static final int QUIET = -1;//没有滑动
    private int dir;
    private int downX,downY,upX,upY;
    public NextEvent()
    {
        init();
    }
    public void init(){
        this.dir = QUIET;
        this.downX = -1;
        this.downY = -1;
        this.upX = -1;
        this.upY = -1;
    }
    public int getDir(){
        float offsetX,offsetY;
        offsetX = upX - downX;
        offsetY = upY - downY;
        if (Math.abs(offsetX) > Math.abs(offsetY)) {
            if (offsetX > 5 ) {
                dir = RIGHT;
            }else if (offsetX < -5) {
                dir = LEFT;
            }
        }else {
            if (offsetY > 5) {
                dir = DOWN;
            }else if (offsetY < -5) {
                dir = UP;
            }
        }
        return dir;
    }

    public int getDownX() {
        return downX;
    }

    public void setDownX(int downX) {
        this.downX = downX;
    }

    public int getDownY() {
        return downY;
    }

    public void setDownY(int downY) {
        this.downY = downY;
    }

    public int getUpX() {
        return upX;
    }

    public void setUpX(int upX) {
        this.upX = upX;
    }

    public int getUpY() {
        return upY;
    }

    public void setUpY(int upY) {
        this.upY = upY;
    }
}

Tile.java

package com.next.puzzle;

/**
 * Created by Administrator on 2016/4/8 0008.
 */
public class Tile {
    public static int width;
    int x,y,index;
    public Tile(int x,int y,int index){
        this.x = x;
        this.y = y;
        this.index = index;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

    public int getIndex() {
        return index;
    }

    public void setIndex(int index) {
        this.index = index;
    }
}

GameView.java

package com.next.puzzle;
import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import java.util.Random; 

/** Created by Next on 2016/4/8 0008. */

public class GameView extends SurfaceView implements SurfaceHolder.Callback,Runnable,View.OnTouchListener{
    enum GameState{ Menu, Playing, Over; }
    Bitmap picOver = null;
    Bitmap picCang = null;
    Bitmap picMenu = null;
    Bitmap picGame = null;
    Canvas canvas;/*画布*/
    GameState gameState;/*游戏状态*/
    Thread thread;/*游戏线程*/
    Boolean flag;/*游戏循环控制标志*/
    SurfaceHolder holder;/*SurfaceView控制句柄*/
    public static Random random;/*随机数*/
    NextEvent nextEvent;/*游戏输入事件*/
    int scrW,scrH;/*屏幕的宽和高*/
    int cutNum;/*游戏图片分隔的数量(单行,行列数相等)*/
    int offsetY;/*用于调整游戏区域居中*/
    public static Tile[][] tiles;
    int score;
    Tile touchTile;//当前触摸的图片块
    NextGraphics graphics;
    public GameView(Context context, AssetManager attrs) {
        super(context);
        gameState = GameState.Menu;
        flag = true;
        graphics = new NextGraphics();
        thread = new Thread(this);
        random = new Random();
        nextEvent = new NextEvent();
        holder = this.getHolder();
        holder.addCallback(this);
        this.setOnTouchListener(this);
        setKeepScreenOn(true);
        scrW = ((MainActivity)context).getWindowManager().getDefaultDisplay().getWidth();
        scrH = ((MainActivity)context).getWindowManager().getDefaultDisplay().getHeight();
        picOver = BitmapFactory.decodeResource(this.getResources(), R.drawable.bg_over);
        picCang = BitmapFactory.decodeResource(this.getResources(),R.drawable.cang);
        picMenu = BitmapFactory.decodeResource(this.getResources(),R.drawable.bg_menu);
        picGame = BitmapFactory.decodeResource(this.getResources(),R.drawable.bg_game);
    }

    private void newGame(int colNum){
        this.cutNum = colNum;
        Tile.width = scrW/cutNum;
        offsetY = (scrH-Tile.width*cutNum)/2;
        tiles = new Tile[cutNum][cutNum];
        for (int x = 0;x < cutNum;x++){
            for (int y = 0;y < cutNum;y++){
                tiles[x][y] = new Tile(x,y,y*cutNum+x);
            }
        }
        randomPiece();
    }
    private void randomPiece(){
       // move(tiles[p % cutNum][p / cutNum], judgeMove(tiles[p % cutNum][p / cutNum]));
        for (int i = 0;i < 1000;i++){
            int x = random.nextInt(cutNum);
            int y = random.nextInt(cutNum);
            move(tiles[x][y],judgeMove(tiles[x][y]));
        }
    }
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            nextEvent.setDownX((int) event.getX());
            nextEvent.setDownY((int) event.getY());
        } else if (event.getAction() == MotionEvent.ACTION_UP) {
            nextEvent.setUpX((int) event.getX());
            nextEvent.setUpY((int) event.getY()); }
        return true;
    }

    private void mLogic(){
        switch (gameState){
            case Menu:
                menuLogic();
                break;
            case Playing:
                playingLogic();
                break;
            case Over: overLogic();
                break;
        }
        nextEvent.init();/*每次逻辑循环后,清空事件*/
    }

    private void menuLogic(){
        if (nextEvent.getDownX() > 0 && nextEvent.getDownX() < scrW/3){
            gameState = GameState.Playing;
            newGame(3);
        }else if (nextEvent.getDownX() > scrW/3 && nextEvent.getDownX()<scrW/3*2){
            gameState = GameState.Playing;
            newGame(4);
        }else if (nextEvent.getDownX() > scrW/3*2){
            gameState = GameState.Playing;
            newGame(5);
        }
    }

    private void playingLogic(){
        if (nextEvent.getDownY()>offsetY&&nextEvent.getDownY()<(offsetY+Tile.width*cutNum)){
            touchTile = tiles[nextEvent.getDownX()/Tile.width][(nextEvent.getDownY()-offsetY)/Tile.width];
            move(touchTile, judgeMove(touchTile));
            if(judgeOver()){
                gameState = GameState.Over;
            }
        }else if(nextEvent.getDownY()>offsetY+Tile.width*cutNum+Tile.width/2){
            if (nextEvent.getDownX() < scrW/2){
                gameState = GameState.Menu;
            }else {
                randomPiece();
            }
        }
    }

    private boolean judgeOver(){
        for (int x = 0;x < cutNum;x++){
            for (int y = 0;y < cutNum;y++){
                if (tiles[x][y].getIndex() != (y*cutNum+x))
                    return false;
            }
        }
        return true;
    }
    /*
    * 判断tile是否可以移动
    * 返回可移动的方向,0左1上2右3下-1不能移动
    */
    private int judgeMove(Tile tile) {
        if (tile.getX() != 0 && tiles[tile.getX()-1][tile.getY()].getIndex() == cutNum*cutNum-1){
            return 0;
        }
        if (tile.getY() != 0 && tiles[tile.getX()][tile.getY()-1].getIndex() == cutNum*cutNum-1){
            return 1;
        }
        if (tile.getX() != cutNum-1 && tiles[tile.getX()+1][tile.getY()].getIndex() == cutNum*cutNum-1){
            return 2;
        }
        if (tile.getY() != cutNum-1 && tiles[tile.getX()][tile.getY()+1].getIndex() == cutNum*cutNum-1){
            return 3;
        }
        return -1;
    }

    private void move(Tile tile,int dir){
        switch (dir){
            case -1:
                return;
            case 0:
                tiles[tile.getX()-1][tile.getY()].setIndex(tile.getIndex());
                tile.setIndex(cutNum*cutNum-1);
                break;
            case 1:
                tiles[tile.getX()][tile.getY()-1].setIndex(tile.getIndex());
                tile.setIndex(cutNum*cutNum-1);
                break;
            case 2:
                tiles[tile.getX()+1][tile.getY()].setIndex(tile.getIndex());
                tile.setIndex(cutNum*cutNum-1);
                break;
            case 3:
                tiles[tile.getX()][tile.getY()+1].setIndex(tile.getIndex());
                tile.setIndex(cutNum*cutNum-1);
                break;
        }
    }

    private void overLogic(){
        if (nextEvent.getUpX() > 0){
            gameState = GameState.Menu;
        }
    }

    private void mDraw(){
        try {
            canvas = holder.lockCanvas();
            graphics.setCanvas(canvas);
            canvas.drawColor(Color.BLACK);
            switch (gameState){
                case Menu:
                    menuDraw();
                    break;
                case Playing:
                    playingDraw();
                    break;
                case Over: overDraw();
                    break;
            }
        } catch (Exception e){
            e.printStackTrace();
        } finally {
            holder.unlockCanvasAndPost(canvas);
        }
    }
    private void menuDraw(){
        graphics.drawBitmapTo(picMenu,0,0,scrW,scrH);


    }

    private void playingDraw(){

        graphics.drawBitmapTo(picGame,0,0,scrW,scrH);
        for (int x = 0;x < cutNum;x++){
            for (int y = 0;y < cutNum;y++){
            //最后一张图片不画,看做可以移动的区域
                if (tiles[x][y].getIndex() == cutNum*cutNum-1){
                    continue;
                }

/*下面这么长的一串其实就是一道算术题,实现了根据tile的index将其画在指定位置的功能*/         graphics.drawBitmapFromTo(picCang,picCang.getWidth()/cutNum*(tiles[x][y].getIndex()%cutNum),picCang.getHeight()/cutNum*(tiles[x][y].getIndex()/cutNum),
                        picCang.getWidth()/cutNum*((tiles[x][y].getIndex()%cutNum)+1),picCang.getHeight()/cutNum*((tiles[x][y].getIndex()/cutNum)+1),
                        x*Tile.width+2,offsetY+y*Tile.width+2,
                        (x+1)*Tile.width-2,offsetY+(y+1)*Tile.width-2);
            }
        }
    }

    private void overDraw(){
        graphics.drawBitmapTo(picOver,0,0,scrW,scrH);
    }

    @Override
    public void run() {
        while(flag){
            mLogic();
            mDraw();
            try {
                Thread.sleep(200);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        thread.start();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        flag = false;
    }
}

欢迎关注我的公众号:xinshouit 更新会在里面通知。

我还有一句话要说。。。。并不是画图方法有问题,也不是我的审美问题,昨天写这个游戏的时候断网了,手头没有素材,所以就找了一张分辨率很小的背景图片,所以看着有点渣。。。凑和看吧。。。
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 10
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值