Android实现连连看游戏

来自:网络
时间:2022-08-07
阅读:

本文实例为大家分享了Android实现连连看游戏的具体代码,供大家参考,具体内容如下

本人用 android studio 实现的

源码

主活动 类:

package packageName;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;

import MaView;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ViewGroup.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        final MaView mainView = new MaView(this);
        addContentView(mainView, params);
        // 添加三个按钮执行相应的方法
        Button btn = new Button(this);
        btn.setText("新游戏");
        btn.setTextSize(20);
        btn.setX(40);
        btn.setY(40);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mainView.newGame();
            }
        });
        addContentView(btn, params);

        Button tipBtn = new Button(this);
        tipBtn.setText("提示");
        tipBtn.setTextSize(20);
        tipBtn.setX(380);
        tipBtn.setY(40);
        tipBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mainView.getTip();
            }
        });
        addContentView(tipBtn, params);

        Button resetBtn = new Button(this);
        resetBtn.setText("重置");
        resetBtn.setTextSize(20);
        resetBtn.setX(720);
        resetBtn.setY(40);
        resetBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mainView.reset();
            }
        });
        addContentView(resetBtn, params);
    }
}

MaView 类

package packageName;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Picture;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.PictureDrawable;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.transition.Explode;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;

import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;

public class MaView extends View {
    // 连连看规则: 在一个方形有若干个格子,当选择其中的两个格子时,存在以下几种连线关系时就会被消灭
    // 连线只有水平和垂直方向
    // 1, 其水平或垂直方向相同,并且两个格子之间无其它非空格子的直线关系
    // 2, 有一个折点的 一折点关系
    // 3, 有两个折点的 二折点关系

    // 左边距
    public static final int MARGINLEFT = 40;
    // 上边距
    public static final int MARGINTOP = 400;
    // 格子的行列数
    public static final int ROW = 10;
    // 格子的宽高
    public static final int W = 100;
    // 图片的宽高
    public static final int IMGW = 90;
    // 格子为空
    public static final int NULL = -1;
    // 连接状态为 直线
    public static final int STRAIGHT = 1;
    // 连接状态为 一折点
    public static final int ONELINK = 2;
    // 连接状态为 二折点
    public static final int TWOLINK = 3;
    // 格子的种类数
    public static final int L = 25;
    // 存放格子信息的地图
    private int[] map = new int[ROW * ROW];
    // 存放格子的图片
    private Bitmap[] imgs = new Bitmap[L];
    // 判断触屏事件是否为点击
    private boolean isMove;
    // 是否为第一次选择格子
    private boolean isFirstSelect;
    // 是否可以画连接线和格子的选中边框
    private boolean canDrawLine, canDrawRect;
    // 是否有提示,没有的话要重置当前格子位置
    private boolean canTip;
    // 是否可以选择格子
    private boolean canPlay;
    // 是否已经点击了提示
    private boolean firstTip;
    // 存储第一次和第二次选中方块的位置
    private int x1 = NULL, y1 = NULL, x2 = NULL, y2 = NULL;
    // 第一个折点和第二个折点的位置
    private int px1, py1, px2, py2;
    // 连接线的类别
    private int linkState;
    // 计数器,用于解决异步问题
    private int count = 0;

    public MaView(Context context) {
        super(context);
        // 初始化图片
        initImg();
        // 初始化游戏
        newGame();
        // 设置触屏事件
        setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                // 当前状态不可以点击,如画连接线时会有 0.5 秒的等待时间
                if (!canPlay) {
                    return false;
                }
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        isMove = false;
                        break;
                    case MotionEvent.ACTION_MOVE:
                        isMove = true;
                    case MotionEvent.ACTION_UP:
                        // 若为移动事件则不执行
                        if (!isMove) {
                            // 获取当前点击的位置在网格中的位置
                            int x = (int) event.getX() - MARGINLEFT;
                            int y = (int) event.getY() - MARGINTOP;
                            // 是否超出边界
                            if (x < 0 || x > W * ROW || y < 0 || y > W * ROW) {
                                return false;
                            }
                            // 转化为格子的坐标
                            // x 为列, y 为行
                            x = x / W % ROW;
                            y = y / W;
                            // 是否为第一次选择
                            if (isFirstSelect) {
                                // 点击的位置是否为空格子
                                if (map[y * ROW + x] == NULL) {
                                    return false;
                                }
                                // 存储第一个格子的位置
                                x1 = x;
                                y1 = y;
                                // 可以画边框
                                canDrawRect = true;
                                isFirstSelect = false;
                                // View 自带的方法,会异步执行 onDraw() 方法,起到更新视图的作用
                                invalidate();
                            } else {
                                if (map[y * ROW + x] == NULL) {
                                    return false;
                                }
                                // 点击的格子是是同一个则重置选择
                                if (x1 == x && y1 == y) {
                                    noDraw();
                                    // 更新视图
                                    invalidate();
                                    return false;
                                }
                                // 存储第二个格子的位置
                                x2 = x;
                                y2 = y;
                                // 判断两个格子是否相同
                                if (map[y1 * ROW + x1] == map[y2 * ROW + x2]) {
                                    // 是否可以连接
                                    if (isCanLink(x1, y1, x2, y2)) {
                                        canDrawLine = true;
                                        // 更新视图
                                        invalidate();
                                        // 计数器,防止视图效果不同步
                                        count++;
                                        waitLose();
                                    } else {
                                        noDraw();
                                        invalidate();
                                    }
                                } else {
                                    noDraw();
                                    invalidate();
                                }
                            }
                        }
                }
                return true;
            }
        });
    }
    
    // 判断是否赢了
    private boolean isWin() {
        for (int i = 0; i < ROW * ROW; i++) {
            if (map[i] != NULL) {
                return false;
            }
        }
        return true;
    }
    
    // 判断是否可以将消除的格子
    private void waitLose() {
        if (count == 2) {
            map[y2 * ROW + x2] = NULL;
            map[y1 * ROW + x1] = NULL;
            count = 0;
        }
    }

    // 开始新游戏
    public void newGame() {
        randomMap();
        noDraw();
        firstTip = true;
        canPlay = true;
        invalidate();
    }

    private boolean isCanLink(int x1, int y1, int x2, int y2) {
        // 要经过直线连接,一折点连接,二折点连接三个判断

        // 垂直直线连接, 其列坐标相同
        if (x1 == x2 && isVLink(x1, y1, y2)) {
            linkState = STRAIGHT;
            return true;
        }
        // 水平直线连接,其行坐标相同
        if (y1 == y2 && isHLink(y1, x1, x2)) {
            linkState = STRAIGHT;
            return true;
        }
        // 判断一个折点的连接
        if (isOneLink(x1, y1, x2, y2)) {
            linkState = ONELINK;
            return true;
        }
        // 判断两个折点的连接
        if (isTwoLink(x1, y1, x2, y2)) {
            linkState = TWOLINK;
            return true;
        }
        return false;
    }
    // 垂直直线判断

    private boolean isVLink(int x1, int y1, int y2) {
        // 保证 y1 永远不大于 y2
        if (y1 > y2) {
            int t = y1;
            y1 = y2;
            y2 = t;
        }
        // 遍历 x, y1 到 y2 的格子
        for (int i = y1 + 1; i < y2; i++) {
            // 有一个不为空则不能连接
            if (map[i * ROW + x1] != NULL) {
                return false;
            }
        }
        return true;
    }

    // 水平直线判断
    private boolean isHLink(int y1, int x1, int x2) {
        // 保证 x1 永远不大于 x2
        if (x1 > x2) {
            int t = x1;
            x1 = x2;
            x2 = t;
        }
        // 遍历 x1 到 x2, y 的格子
        for (int i = x1 + 1; i < x2; i++) {
            // 有一个不为空则不能连接
            if (map[y1 * ROW + i] != NULL) {
                return false;
            }
        }
        return true;
    }

    // 两个折点判断
    private boolean isTwoLink(int x1, int y1, int x2, int y2) {
        // 保证第一个坐标在左边,便于遍历
        if (x1 > x2) {
            int t = x1;
            x1 = x2;
            x2 = t;
            t = y1;
            y1 = y2;
            y2 = t;
        }
        // 有四个方向判断
        // top
        // 先将第一个折点上移,然后将第一个折点与第二个坐标用 矩形连接法 进行判断
        for (int i = y1 - 1; i >= -1; i--) {
            // 若到达了边界,则判断第二个坐标在这个方向是否也能到达边界,若能则可以连接
            if (i == -1) {
                // 第二个坐标是否能到达上边界
                if (isCanTop(x2, y2)) {
                    // 存储第一个和第二个折点
                    px1 = x2;
                    py1 = i;
                    px2 = x1;
                    py2 = i;
                    return true;
                }
                break;
            }
            if (map[x1 + i * ROW] != NULL) {
                break;
            }
            if (isOneLink(x1, i, x2, y2)) {
                // 存储第二个折点,第一个折点在 一折点连接 中存了
                px2 = x1;
                py2 = i;
                return true;
            }
        }
        // down
        for (int i = y1 + 1; i <= ROW; i++) {
            if (i == ROW) {
                if (isCanDown(x2, y2)) {
                    px1 = x2;
                    py1 = i;
                    px2 = x1;
                    py2 = i;
                    return true;
                }
                break;
            }
            if (map[x1 + i * ROW] != NULL) {
                break;
            }
            if (isOneLink(x1, i, x2, y2)) {
                px2 = x1;
                py2 = i;
                return true;
            }
        }
        // left
        for (int i = x1 - 1; i >= -1; i--) {
            if (i == -1) {
                if (isCanLeft(x2, y2)) {
                    px2 = i;
                    py2 = y1;
                    px1 = i;
                    py1 = y2;
                    return true;
                }
                break;
            }
            if (map[i + y1 * ROW] != NULL) {
                break;
            }
            if (isOneLink(i, y1, x2, y2)) {
                px2 = i;
                py2 = y1;
                return true;
            }
        }
        // right
        for (int i = x1 + 1; i <= ROW; i++) {
            if (i == ROW) {
                if (isCanRight(x2, y2)) {
                    px2 = i;
                    py2 = y1;
                    px1 = i;
                    py1 = y2;
                    return true;
                }
                break;
            }
            if (map[i + y1 * ROW] != NULL) {
                break;
            }
            if (isOneLink(i, y1, x2, y2)) {
                px2 = i;
                py2 = y1;
                return true;
            }
        }
        return false;
    }
    
    private boolean isCanTop(int x2, int y2) {
        // 遍历坐标与上边界之间的格子,若又不为空的则不能
        for (int i = y2 - 1; i >= -1; i--) {
            if (i == -1) {
                break;
            }
            if (map[i * ROW + x2] != NULL) {
                return false;
            }
        }
        return true;
    }

    private boolean isCanLeft(int x2, int y2) {
        for (int i = x2 - 1; i >= -1; i--) {
            if (i == -1) {
                break;
            }
            if (map[y2 * ROW + i] != NULL) {
                return false;
            }
        }
        return true;
    }

    private boolean isCanRight(int x2, int y2) {
        for (int i = x2 + 1; i <= ROW; i++) {
            if (i == ROW) {
                break;
            }
            if (map[y2 * ROW + i] != NULL) {
                return false;
            }
        }
        return true;
    }

    private boolean isCanDown(int x2, int y2) {
        for (int i = y2 + 1; i <= ROW; i++) {
            if (i == ROW) {
                break;
            }
            if (map[i * ROW + x2] != NULL) {
                return false;
            }
        }
        return true;
    }

    // 一个折点判断
    private boolean isOneLink(int x1, int y1, int x2, int y2) {
        // 保证第一个坐标在左边,便于遍历
        if (x1 > x2) {
            int t = x1;
            x1 = x2;
            x2 = t;
            t = y1;
            y1 = y2;
            y2 = t;
        }
        // 一个折点的用 矩形判断法, 两个坐标在对角处,折点在另外的对角处
        // 先判断这个折点是否为空,不为空就不能连接
        // 为空就将两个坐标点与这个折点用直线连接判断,若可以,则可以连接
        if (map[y1 * ROW + x2] == NULL) {
            if (isHLink(y1, x1, x2) && isVLink(x2, y1, y2)) {
                // 存储第一个折点的位置,便于画线
                px1 = x2;
                py1 = y1;
                return true;
            }
        }
        // 另外一个折点
        if (map[x1 + y2 * ROW] == NULL) {
            // 注意 x, y 的变换位置
            if (isHLink(y2, x1, x2) && isVLink(x1, y1, y2)) {
                // 存储第一个折点的位置,便于画线
                px1 = x1;
                py1 = y2;
                return true;
            }
        }
        return false;
    }

    private void initImg() {
        int id;
        for (int i = 0; i < imgs.length; i++) {
            id = getResources().getIdentifier("a" + (i + 1), "drawable", getContext().getPackageName());
            // 显示图片原尺寸
//            BitmapFactory.Options options = new BitmapFactory.Options();
//            options.inScaled = false;
            imgs[i] = BitmapFactory.decodeResource(getResources(), id);

            int w = imgs[i].getWidth();
            int h = imgs[i].getHeight();
            Matrix matrix = new Matrix();
            matrix.postScale(IMGW * 1.0f / w, IMGW * 1.0f / h);

            imgs[i] = Bitmap.createBitmap(imgs[i], 0, 0, w, h, matrix, true);
        }
    }

    private Bitmap getMyImg(Bitmap rootImg, int goalW, int goalH) {
        int rootW = rootImg.getWidth();
        int rootH = rootImg.getHeight();
        // graphics 包下的
        Matrix matrix = new Matrix();
        matrix.postScale(goalW * 1.0f / rootW, goalH * 1.0f / rootH);
        return Bitmap.createBitmap(rootImg, 0, 0, rootW, rootH, matrix, true);
    }

    private void randomMap() {
        // 初始化地图并将位置打乱
        int c = 0;
        // 每种格子有四个
        for (int i = 0; i < L; i++) {
            for (int j = 0; j < 4; j++) {
                map[c] = i;
                c++;
            }

        }
        // 循环 500 次打乱位置
        int a, b, t;
        Random random = new Random();
        for (int i = 0; i < 500; i++) {
            a = random.nextInt(ROW * ROW);
            b = random.nextInt(ROW * ROW);
            if (map[a] == NULL || map[b] == NULL) {
                continue;
            }
            t = map[a];
            map[a] = map[b];
            map[b] = t;
        }
    }

//    private void showMap() {
//        String s = "";
//        int c = 0;
//        for (int i = 0; i < ROW; i++) {
//            for (int j = 0; j < ROW; j++) {
//                s += map[c] + " ";
//                c++;
//            }
//            s = "";
//        }
//    }

    @Override
    protected void onDraw(Canvas canvas) {
        int x, y;
        int c = 0;
        // 画格子
        for (int i = 0; i < ROW; i++) {
            for (int j = 0; j < ROW; j++) {
                x = MARGINLEFT + j * W;
                y = MARGINTOP + i * W;
                if (map[c] != NULL) {
                    canvas.drawBitmap(imgs[map[c]], x, y, new Paint());
                }
                c++;
            }
        }
        // 画提示的格子边框
        if (canTip) {
            // 设置线条的样式
            Paint paint = new Paint();
            paint.setStrokeWidth(8);
            paint.setColor(Color.parseColor("#08ffc8"));
            paint.setStyle(Paint.Style.STROKE);
            x = x1 * W + MARGINLEFT;
            y = y1 * W + MARGINTOP;
            canvas.drawRect(x, y, x + W - 3, y + W - 3, paint);
            x = x2 * W + MARGINLEFT;
            y = y2 * W + MARGINTOP;
            canvas.drawRect(x, y, x + W - 3, y + W - 3, paint);
            canTip = false;
            noDraw();
        }
        // 画已选格子的边框
        if (canDrawRect) {
            Paint paint = new Paint();
            paint.setStrokeWidth(8);
            paint.setColor(Color.RED);
            paint.setStyle(Paint.Style.STROKE);
            // 第一个格子
            if (x1 != NULL) {
                x = x1 * W + MARGINLEFT;
                y = y1 * W + MARGINTOP;
                canvas.drawRect(x, y, x + W - 3, y + W - 3, paint);
                firstTip = true;
            }
            // 第二个格子
            if (x2 != NULL) {
                x = x2 * W + MARGINLEFT;
                y = y2 * W + MARGINTOP;
                canvas.drawRect(x, y, x + W - 3, y + W - 3, paint);
                count++;
                waitLose();
            }
        }
        // 画连接线
        if (canDrawLine) {
            Paint paint = new Paint();
            paint.setStrokeWidth(8);
            paint.setColor(Color.RED);
            paint.setStyle(Paint.Style.FILL);

            int sx1, sy1, sx2, sy2, zx1, zy1, zx2, zy2;
            // 第一个坐标
            sx1 = x1 * W + W + MARGINLEFT - W / 2;
            sy1 = y1 * W + W + MARGINTOP - W / 2;
            // 第二个坐标
            sx2 = x2 * W + W + MARGINLEFT - W / 2;
            sy2 = y2 * W + W + MARGINTOP - W / 2;
            switch (linkState) {
                case STRAIGHT:
                    // 画直线
                    canvas.drawLine(sx1, sy1, sx2, sy2, paint);
                    break;
                case ONELINK:
                    // 画一折点线
                    zx1 = px1 * W + MARGINLEFT + W / 2;
                    zy1 = py1 * W + MARGINTOP + W / 2;
                    canvas.drawLine(sx1, sy1, zx1, zy1, paint);
                    canvas.drawLine(zx1, zy1, sx2, sy2, paint);
                    break;
                case TWOLINK:
                    // 画二折点线
                    // 第二个折点
                    zx1 = px1 * W + MARGINLEFT + W / 2;
                    zy1 = py1 * W + MARGINTOP + W / 2;
                    // 第一个折点
                    zx2 = px2 * W + MARGINLEFT + W / 2;
                    zy2 = py2 * W + MARGINTOP + W / 2;
                    // 到边界了改变一下线条的位置
                    if (px1 == -1) {
                        zx1 += 30;
                        zx2 += 30;
                    } else if (px1 == ROW) {
                        zx1 -= 30;
                        zx2 -= 30;
                    }
                    // 有左右两种情况,上下两种情况,但第一个折点一定与第一个坐标的 x 或 y 相同
                    if (px1 == x1 || py1 == y1) {
                        int t = zx1;
                        zx1 = zx2;
                        zx2 = t;
                        t = zy1;
                        zy1 = zy2;
                        zy2 = t;
                    }
                    canvas.drawLine(sx1, sy1, zx2, zy2, paint);
                    canvas.drawLine(zx2, zy2, zx1, zy1, paint);
                    canvas.drawLine(zx1, zy1, sx2, sy2, paint);
            }
            noDraw();
            // 画线过程不能点击
            canPlay = false;
            // 开一个线程做连接效果
            new Timer().schedule(new TimerTask() {
                @Override
                public void run() {
                    // 这个方法用于转到主线程中执行,更新视图操作必须放到主线程中执行 其 invalidate() 跟新了视图
                    post(new Runnable() {
                        @Override
                        public void run() {
                            invalidate();
                            // 判断是否赢了没
                            if (isWin()) {
                                Toast.makeText(getContext(), "You Win! Please New Game", Toast.LENGTH_SHORT).show();
                            }
                            // 可以点击了
                            canPlay = true;
                        }
                    });
                }
            }, 500);
        }
    }

    // 重置当前图片的位置
    public void reset() {
        if (!canPlay) {
            return;
        }
        int a, b, t;
        Random random = new Random();
        for (int i = 0; i < 500; i++) {
            a = random.nextInt(ROW * ROW);
            b = random.nextInt(ROW * ROW);
            if (map[a] == NULL || map[b] == NULL) {
                continue;
            }
            t = map[a];
            map[a] = map[b];
            map[b] = t;
        }
        invalidate();
    }

    // 获取提示
    public void getTip() {
        if (!canPlay) {
            return;
        }
        // 不能连续点击
        if (!firstTip) {
            Toast.makeText(getContext(), "Alright Tip!", Toast.LENGTH_SHORT).show();
            return;
        }
        firstTip = false;
        int count = 0;
        int x1, y1, x2, y2;
        Tag:
        for (int i = 0; i < ROW * ROW; i++) {
            if (map[i] == NULL) {
                continue;
            }
            for (int j = i + 1; j < ROW * ROW; j++) {
                if (map[j] == NULL) {
                    continue;
                }
                if (map[i] == map[j]) {
                    x1 = i % ROW;
                    y1 = i / ROW;
                    x2 = j % ROW;
                    y2 = j / ROW;
                    if (isCanLink(x1, y1, x2, y2)) {
                        count++;
                        this.x1 = x1;
                        this.y1 = y1;
                        this.x2 = x2;
                        this.y2 = y2;
                        break Tag;
                    }
                }
            }
        }
        // 不为空则有可连接的格子
        if (count != 0) {
            canTip = true;
            invalidate();
        } else {
            Toast.makeText(getContext(), "No One! Please Click New Game", Toast.LENGTH_SHORT).show();
        }
    }

    // 重置选择格子
    private void noDraw() {
        canDrawRect = false;
        canDrawLine = false;
        isFirstSelect = true;
        x1 = NULL;
        x2 = NULL;
        y1 = NULL;
        y2 = NULL;
    }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

返回顶部
顶部