Android实现连连看游戏
作者:hellolxb
这篇文章主要为大家详细介绍了Android实现连连看游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
本文实例为大家分享了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; } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。