Android实现连连看游戏
短信预约 -IT技能 免费直播动态提醒
本文实例为大家分享了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;
}
}
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程网。
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341