首页 分享 消消乐android代码 android studio消消乐

消消乐android代码 android studio消消乐

来源:花匠小妙招 时间:2026-02-02 00:04

转载

最近发现了一个超好用的IDE!!免费体验全球最强旗舰模型 Claude Opus 4.5,会自己找网络接口,会调试跨系统 bug,还会操作桌面应用、Excel、浏览器。还能读懂复杂代码库、拆解多系统交互。划重点!它能模糊指令下自动找出 bug,再也不怕review 的时候被 diss 写的不优美了!链接如下(新用户免费体验噢) https://click.aliyun.com/m/1000409013/

游戏效果图(今天更新成动图,但是看起来卡卡的,我程序很流畅的被弄成了这样):

消消乐android代码 android studio消消乐_数组

当然你也可以在这个链接里面下载我的apk试玩
apk要求:Android version 6.0
链接: https://pan.baidu.com/s/1eTkO9mq 密码:w48l

##设计思路
一、实体类,封装一个动物头像。包含x,y坐标,图片(bitmap),一个id用于匹配,宽、高
二、布局方面一个Activity和一个自定义的View,主角当然是我们这个View。
三、在构造方法里面初始化游戏相关数据

public GameView(Context context, AttributeSet attr) { super(context, attr); screenHeight = DisplayUtil.getScreenHeight(context); screenWidth = DisplayUtil.getScreenWidth(context); // 音乐相关初始化 bgMedia = MediaPlayer.create(this.getContext(), R.raw.bg_game); bgMedia.setOnCompletionListener(this); bgMedia.start(); // swap = MediaPlayer.create(this.getContext(), R.raw.swap); // int ave = screenWidth / (row + 2); // 将屏幕宽度分为 row + 2 等份 int ave = 0; int size = (screenWidth - ave * 2) / row; // 其它两分为:舞台距离屏幕左右边的像素 ZooUtil.initZooData(size, size, this.getResources()); // 初始化动物头像数据 // 背景图片 background = BitmapFactory.decodeResource(this.getResources(), R.mipmap.game_bg); floorBg = BitmapFactory.decodeResource(this.getResources(), R.mipmap.floor_bg); /* * 计算出舞台距离左边屏幕的距离 * 计算方式为: * (屏幕总宽度 - 人物头像的宽 * 总行数) / 2 */ int leftSpan = (screenWidth - ZooUtil.getAnimalWidth() * row) / 2; int topSpan = (screenHeight - ZooUtil.getAnimalHeight() * col) / 3; // 将游戏舞台的坐标、高宽保存起来 StageUtil.initStage(leftSpan, topSpan, leftSpan + ZooUtil.getAnimalWidth() * row, topSpan + ZooUtil.getAnimalHeight() * col); // 实例化画笔 paint = new Paint(); paint.setFlags(Paint.ANTI_ALIAS_FLAG); paint.setAntiAlias(true); // 消除锯齿 initGamePoint(); }/** * 生成游戏坐标 */ private void initGamePoint() { currScore = 0; // 清空当前得分 bitmaps = new FlashBitmap[row][col]; // 生成背景图片的坐标(仅背景图片,后续可考虑将特效也加进来) for (int i = 0; i < row; ++i) { for (int j = 0; j < col; ++j) { do { // 计算头像坐标 FlashBitmap bitmap = ZooUtil.getAnimal(); bitmap.setX(StageUtil.getStage().getX() + i * ZooUtil.getAnimalWidth()); bitmap.setY(StageUtil.getStage().getY() + j * ZooUtil.getAnimalHeight()); transBitmap[i][j] = bitmap.clone(); bitmap.setY(0); // 在顶部慢慢下落 bitmaps[i][j] = bitmap; } while(StageUtil.checkClearPoint(bitmaps)); } } }1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.

四、重写该View的onDraw方法,在这个方法里面进行动物的绘制

@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 绘制背景图片 Bitmap bgBitmap = DisplayUtil.resizeBitmap(background, screenWidth, screenHeight); canvas.drawBitmap(bgBitmap, 0, 0, paint); Bitmap floor = DisplayUtil.resizeBitmap(floorBg, ZooUtil.getAnimalWidth(), ZooUtil.getAnimalHeight()); // 每一个小头像背后的背景 for (int i = 0; i < row; ++i) { for (int j = 0; j < col; ++j) { if(transBitmap[i][j] != null){ int x = (int) transBitmap[i][j].getX(); int y = (int) transBitmap[i][j].getY(); canvas.drawBitmap(floor, x, y, paint); } } } // 舞台中的所有动物头像 FlashBitmap bitmap; for (int i = 0; i < row; ++i) { for (int j = 0; j < col; ++j) { bitmap = bitmaps[i][j]; if(bitmap != null // 不为空并且坐标点要进入舞台 && StageUtil.inStage(bitmap.getX(), bitmap.getY() + bitmap.getHeight() / 2)){ canvas.drawBitmap(bitmap.getBitmap(), bitmap.getX(), bitmap.getY(), paint); } } } // 是否需要加载消除 synchronized (this){ if (load) { pool.execute(new Runnable() { @Override public void run() { clearBitmap(); } }); load = false; } } paint.setColor(Color.WHITE); paint.setTextSize(32); paint.setTypeface(Typeface.create(Typeface.DEFAULT_BOLD , Typeface.BOLD)); canvas.drawText("当前关卡:" + level, 10, StageUtil.getStage().getHeight() + 70, paint); canvas.drawText("当前得分:" + currScore, 10, StageUtil.getStage().getHeight() + 120, paint); canvas.drawText("通关分数:" + accessScore[level - 1], 10, StageUtil.getStage().getHeight() + 170, paint); // 刷新屏幕的频率(理论上小于25,人就会感觉物体是在移动) postInvalidateDelayed(1); }1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.

五、重写onTouchEvent方法监听该View的按下、抬起、移动等相关事件

@Override public boolean onTouchEvent(MotionEvent event) { // 判断交换状态是否完毕 if(swapState){ return false; } // 如果正在做下落动画不允许操作 if(loadAnimalState){ return false; } // 获取当前触控位置 float ex = event.getX(); float ey = event.getY(); switch (event.getAction()) { // 按下 case MotionEvent.ACTION_DOWN: // 判断是否该点是按在舞台上 if (!isDown && StageUtil.inStage(ex, ey)) { p1.setX(ex); p1.setY(ey); isDown = true; } break; // 移动 case MotionEvent.ACTION_MOVE: // 判断是否该点是按在舞台上 if (!isDown && StageUtil.inStage(ex, ey)) { p1.setX(ex); p1.setY(ey); isDown = true; } break; // 抬起 case MotionEvent.ACTION_UP: if (isDown) { p2.setX(ex); p2.setY(ey); isDown = false; prepSwap(); // 预处理交换 } break; } // 使系统响应事件,返回true return true; }1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.

基本上到了这里,这个游戏的主体就已经完成了。
下面主要介绍交换和下落以及如何制作交换和下落的动画
##1:交换
我的整个游戏舞台都是用数组保存的,所以交换直接两个坐标交换一下就算完成了

// 两个方块的交换状态 private boolean swapState = false; // 载入动物头像动画是否结束状态 private boolean loadAnimalState = false; // 是否要加载舞台消除动画(程序运行时立即加载) private boolean load = true; // 虚拟背景 private FlashBitmap[][] transBitmap = new FlashBitmap[row][col]; // 动物头像以及游戏坐标 private FlashBitmap[][] bitmaps = new FlashBitmap[row][col]; // 背景音乐 private MediaPlayer bgMedia; // , clearMedia, swap; // 线程池 ExecutorService pool = Executors.newFixedThreadPool(5); // private BlockingQueue queue = new LinkedBlockingQueue(); // private ThreadPoolExecutor pool = new ThreadPoolExecutor(3, // 10, // 10, // TimeUnit.SECONDS, // queue);1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.

###动画效果的思路如下:
1.1:定义两个线程,分别计算出两个头像要交换到的终点,这个终点是通过计算出来的
例如:a[0][0] -> a[0][1], 那么a[0][0]肯定是向右,a[0][1]肯定是向左,只要稍加判断就可以得出a[0][0]是x轴到达 a[0][1],这里大家不要误解,我的真实坐标是一个数组,但是画在地图上的坐标是这个数组所对应的x,y ,所以交换的时候先让真实数组交换,然后再根据真实数组的坐标,计算出x或y需要到达的位置.
a[0][1]所对应的 x 的真实坐标应为:0 * 动物宽度 + 舞台左边界距离(0是数组里面的)
a[0][1]所对应的 y 的真实坐标应为:1 * 动物高度 + 舞台上边界距离(1是数组里面的)
1.2:计算出这两个值,判断一下大小,然后定义两个线程同时启动就ok了,因为是两个线程不停的交互移动,所以肉眼是感觉不出来的
这是我的swap方法里面计算交换后x, y位置的代码

// 判断是横着交换还是竖的交换 final int px1 = (int) StageUtil.getStage().getX() + x1 * ZooUtil.getAnimalWidth(); final int py1 = (int) StageUtil.getStage().getY() + y1 * ZooUtil.getAnimalHeight(); final int px2 = (int) StageUtil.getStage().getX() + x2 * ZooUtil.getAnimalWidth(); final int py2 = (int) StageUtil.getStage().getY() + y2 * ZooUtil.getAnimalHeight(); // 取到交换后的两个坐标对象 final FlashBitmap one = bitmaps[x1][y1]; final FlashBitmap two = bitmaps[x2][y2];1.2.3.4.5.6.7.8.

##2:动物下落
下落我之前设想一个思路,是用多线程实现,但是大部分内存杀手估计就是像我这种的程序员吧,当然用多线程那肯定是方便多了,消除几个后,直接把每个要下落的动物都建立一个线程,慢慢的加这个动物的y轴,只要判断一下这个动物头像的最后落点在哪里(计算方式同swap里面的),当然要考虑一下动物头像的下落坐标,不要超过它的下面那个动物头像,因为线程,有的快,有的慢, 这个是没法保证的。
这种方案完成后,我找群里的人帮我测试了一下,大部分人都说反应慢,效果不好,动画不流畅。之后脑子里一直想着用一个线程处理掉这个下落的动画,而下落又是好多头像同时进行的,继而我又想起如何在每个动物之间快速切换,最后想了很久,想到了一种方案,两个for循环控制着所有的动物头像,每次循环只将一个动物的y轴加一步,然后就结束,在两个for之外再加一个while循环,这个while检测到所有的动物头像都到达终点后才退出,这样的话,用一个线程达到了我想要的效果。
代码片断:

// 移动其它头像 boolean updateFlag = false; boolean canDoWhile; int[] index = new int[col]; do {canDoWhile = false;// 开始进行下落处理for(int j = col - 1; j >= 0; --j){for(int i = 0; i < row; ++i){if(bitmaps[i][j] != null){if(!moveStageAnimal(i, j)){canDoWhile = true;}} else {// 只要检测到有任意一个空位就要进行更新updateFlag = true;// 如果是空,检测从当前位置往上是否还有其它动物头像boolean hasPoint = false;for(int k = j - 1; k >= 0; --k){// 如果检测到有一个点的话就不搞事情了if(bitmaps[i][k] != null){hasPoint = true;}}// 如果没有就直接生成一个新点在这个位置// 真实坐标是这个位置,但显示在地图上的坐标要给个到 (x, 0)if(!hasPoint) {FlashBitmap bitmap = ZooUtil.getAnimal();bitmap.setX(StageUtil.getStage().getX() + i * ZooUtil.getAnimalWidth());bitmap.setY(StageUtil.getStage().getY() - (index[i] + 1) * ZooUtil.getAnimalHeight());bitmaps[i][j] = bitmap;index[i]++;}}}}// 动画停留间隔DisplayUtil.sleep(time); } while(canDoWhile);1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.

最后再看一个下落的移动动物头像方法

/** * 移动舞台动物 * @param x x * @param y y * @return 成功true 失败false */ private boolean moveStageAnimal(int x, int y) {// 记录当前的点int j;float currY = bitmaps[x][y].getY();// 寻找最佳底部的空位for(j = col - 1; j >= 0; --j){// 不能小于以前的位置if(j <= y){break;}// 从底往上找,找到的第一个空位就为要到的位置if(bitmaps[x][j] == null){break;}}// 有最新点时才进行交换if(j != y){FlashBitmap temp = bitmaps[x][y];bitmaps[x][y] = null;bitmaps[x][j] = temp;}// 不允许在这之上的方块提前下落到下一个方块后面if(j < col - 1 && bitmaps[x][j + 1] != null){if(currY + ZooUtil.getAnimalHeight() >= bitmaps[x][j + 1].getY()){return true;}}// 到达指定高度停止if(currY >= j * ZooUtil.getAnimalHeight() + StageUtil.getStage().getY()){return true;}// 大于舞台高度直接停止if(currY + bitmaps[x][j].getHeight() >= StageUtil.getStage().getHeight()){return true;}// 自增currY += speed;bitmaps[x][j].setY(currY);return false; }1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.

做这个游戏大概花了两天半的时间,主要是为了更深刻的认识多线程,另外也是听说安卓6.0变动很大,所以用安卓做,其实安卓里面嵌入H5也比较合适的,我用h5做过坦克大战,基本不用考虑性能问题,嵌入到安卓里面就更完美了。

最近发现了一个超好用的IDE!!免费体验全球最强旗舰模型 Claude Opus 4.5,会自己找网络接口,会调试跨系统 bug,还会操作桌面应用、Excel、浏览器。还能读懂复杂代码库、拆解多系统交互。划重点!它能模糊指令下自动找出 bug,再也不怕review 的时候被 diss 写的不优美了!链接如下(新用户免费体验噢) https://click.aliyun.com/m/1000409013/

本文章为转载内容,我们尊重原作者对文章享有的著作权。如有内容错误或侵权问题,欢迎原作者联系我们进行内容更正或删除文章。

相关文章

相关知识

消消乐
花朵消消乐小游戏
花朵消消乐游戏
开心消消乐关卡花在哪里更换
花园消消乐官方下载
RPG+三消+Boss《小鱼消消乐》的融合创新
花儿消消乐
种花消消乐
花园消消乐下载
花儿消消乐2.1

网址: 消消乐android代码 android studio消消乐 https://www.huajiangbk.com/newsview2553899.html

所属分类:花卉
上一篇: 消消乐的开发需要多少周期,类似这
下一篇: 宾果消消消

推荐分享