这是一个微信小程序项目,是类似开心消消乐的休闲小游戏,老少皆宜,游戏互动里面的图片是用的任何图片素材,根据自己的需求更换图片即可。想要做游戏不知道怎么做,建议从这个小程序入手,花时间研究学习,很快就拥有属于自己的小程序。

准备

  • 会使用微信开发工具
  • 有游戏图片素材
  • 有游戏背景音效

打开微信开发工具,选择一个小程序项目,点+号新建,依次选择

创建小程序

  • 不使用云服务
  • JavaScript - 基础模板

修改项目名,点确定,就可以开始做了

页面布局

首先,开始做项目的时候,设计稿或者效果图是准备好的,这样让我们知道下一步该怎么做

效果图

来看一看效果图,要做的游戏页面是像下面这样的

布局

有目标,加油干!

这里做页面是用基础模板template来做的,有vue基础的就很容易完成,

在页面文件pages/game/game.wxml中的添加布局,大致如下

<view class="container"><view class="row"><view class="expand"><view class="row"><image src="{{firstImg}}" class="icon" mode="scaleToFill" /><view><text>×{{scope}}</text></view></view></view><view class="" style="width: 28vw;"><view class="row"><image src="/static/five_oclock_3d.png" class="icon" mode="scaleToFill"/><view><text>{{timerNum}}s</text></view></view></view></view><view class="expand"><view class="canvas-box"><canvas class="canvas" id="canv" type="2d" bindtouchstart="onTouchStart" bindtouchend="onTouchEnd"></canvas></view></view><progress percent="{{progressPercent}}" activeColor="#38f" backgroundColor="#ccc" />
</view>

还有样式文件,这里不展开讲,自己知道怎么改样式,做成效果图一样的就好了
效果图中的弹出对话框,这是小程序系统自带的,做的时候忽略掉

以上页面的布局中,有两个部分

  • 一个画布canvas,显示游戏画面
  • 一个显示游戏状态信息,还有进度条

游戏逻辑

接下来,就写写游戏逻辑了,整理一下游戏思路,比如做一份流程图,把思路现出来

流程图

一个流程图出来了,如下所示,这下思路变清晰了吧

Created with Raphaël 2.3.0游戏开始初始化倒计时开始等待用户选两个图片是否消除?进行消除动画,累计数量倒计时结束?计算得分退出游戏?游戏结束yesnoyesnoyesno

大纲

看一下大纲,就能知道大概的游戏逻辑吧,

游戏的相关配置如下,是可以调节的

const MatchCount = 3;//达到3个可消除
const AnimationTime = 10;//动画延迟10ms
const isShowClearLine = false;//圈出欲消除的图片
const maxTimeNum = 300;//倒计时最大值
const ColNum = 7;//列数,数字越大装得图片越多

逻辑代码,都写在一个页面文件pages/game/game.js中,方便阅读,代码如下

Page({/*** 页面的初始数据*/data: {firstImg:"",//第一个图片progressPercent:100,//进度条进度timerNum:maxTimeNum,//计数scope:0,//消除数量imgList:[],//存放游戏图片},/*** 生命周期函数--监听页面加载*/ onLoad(options){//...这里处理初始化},/*** 生命周期函数--监听页面卸载*/onUnload() {//关闭定时器if(this.data.timer){clearInterval(this.data.timer);}//销毁游戏背景音if(this.data.audioPlay && this.data.audioPlay.destroy){this.data.audioPlay.destroy(); }},onTouchStart(e){//...用户触摸时,会触发这个事件方法//就在这里处理用户选图片的逻辑},onTouchEnd(){//...用户不触摸时,会触发这个事件},showGameoverModal(){//...游戏结束,计算得分,用对话框提示},initCanvasData(canvasData){//...初始化画布数据,用于绘制游戏图},clearGrids(callback){//...处理消除逻辑,包括消除动画,处理完成后回调callback},//...剩下的方法省略
})

在方法onLoad(options)这里,写初始化逻辑,代码如下

//根据id获取到画布的节点node和大小size
wx.createSelectorQuery().select('#canv').fields({ node:true, size:true },res=>{const { width, height, node:canvas } = res;const canvasData = {canvas,//画布元素(节点)context: canvas.getContext('2d'),//获取画布的操作对象(上下文)columns: ColNum,//每行的个数,设置在6~12之间最佳};//这是一些图片名字const ImageList = [//...更多图片资源"deer","chicken","chipmunk","cow_face","crab",];//加入微任务列表let taskList = ImageList.map(function(filename){//加载图片是异步操作的return new Promise(function(resolve,reject){let image = canvas.createImage();//...省略更多//加载项目下static文件夹里面的图片资源image.src=`/static/${filename}_3d.png`;});});//执行微任务Promise.all(taskList).then(imgs=>{//执行到这里,微任务执行结束,就显示第一个图片if(!this.data.firstImg) {this.setData({firstImg:'../../'+imgs[0].src})}this.data.imgList = imgs;this.initCanvasData(canvasData);this.initAudioPlay();//初始化游戏音效//...省略更多,//开始执行消除处理动画的方法this.clearGrids(()=>{//...省略更多//消除完了,开始倒计时this.data.timer = setInterval(()=>{let num = this.data.timerNum-1;if (num<0){//倒计时结束了clearInterval(this.data.timer);this.data.timer = null;//游戏结束,弹出对话框提示this.showGameoverModal();return;}// 更新倒计时和进度条this.setData({timerNum:num,progressPercent:Math.trunc(num*100/maxTimeNum)})},1000);});}).catch(function(err){//如果执行有问题,会将错误输出到控制台console.error(err)});//...
}).exec();

代码中有调用的其它方法,它们不是重要的,这里不展开讲,
如果你对这个Promise感到陌生,它是处理异步(微)任务的,建议你熟悉 Promise,相信这对你很有帮助

初始化数据

在这个方法initCanvasData(canvasData)里去实现初始化,参数canvasData表示画布数据,很简单,代码如下

let padding = 1;//初始内边距
const bgColor = '#000';//背景色
const lineColor = '#ff3';//选中边框色
//获取初始化数据对象
const { canvas } = this.data.canvasData;
const { width, height } = canvas;
padding += Math.trunc((width-padding*2)%columns/2);
//根据画布宽高,算出单元格大小
const gridSize = Math.trunc((width-padding*2)/columns);
//算出有多少行
const rows = Math.trunc((height-padding*2)/gridSize);
//网格列表
const gridList = [];
for(let row=0; row<rows; row++){for(let col=0; col<columns; col++){let grid = {//...};//获取随机的图片(索引)grid.font = this.getRandomFont();//加入列表gridList.push(grid);}
}
//所有初始化数据放到canvasData中
Object.assign(canvasData, { bgColor, lineColor, gridList, gridSize, padding, columns, rows });
this.data.canvasData = canvasData;

其中方法getRandomFont() 是获取随机的图片,返回图片列表中的索引即可,代码如下,只写一行足矣

return Math.trunc(Math.random()*this.data.imgList.length);

重复写代码是低效率的,要这样做,将重复的代码块放进一个方法中,后面有很多地方会调用到

处理用户选图片

这应该不难吧,在这个方法onTouchStart(e)中去处理,参数e是用户触摸画布时由系统处理传入的,代码如下

//触摸时,获取第一个坐标点对象
const t = e.touches[0];
const { selectedGrid, isAnimating } = this.data;
//如果在进行动画,就直接返回,不继续执行
if(isAnimating) return;
const { context, canvas, gridList, gridSize, bgImg } = this.data.canvasData;
//获取触摸到的图片索引
let gIndex = this.getTouchGridIndex(t);
//若触摸的不是图片,就直接返回
if(gIndex<0) return;
//开始清理画布
this.clearCanvas(true);
context.drawImage(bgImg,0,0,canvas.width,canvas.height);
//选中图片,画出选中小效果
let grid = gridList[gIndex];
//获取图片的坐标
let coord = this.getGridCoordinate(grid);
context.rect(coord.left,coord.top,gridSize,gridSize);
context.stroke();
//将选中的图片存到起
let newSelectedGrid = {//...
};
if (selectedGrid) {this.data.selectedGrid = null;//选到了两个图片,这里开始处理切换动画this.startToggleAnimation(selectedGrid,newSelectedGrid);
}else{this.data.selectedGrid = newSelectedGrid;
}

其中,两个方法getTouchGridIndex(touch)getGridCoordinate(grid)是很容易实现的,这里不展开讲,

另一个方法 startToggleAnimation(selectedGrid,nextSelectedGrid) ,是处理切换动画效果的,实现起来有难度,这里大致讲一下,代码如下

//...
//判断选的两个图片是否可以切换
const isToggle = this.calcIsClearUp(selectedGrid,nextSelectedGrid);
let hasEq;
//...省略判断逻辑
//如果可以消除
if(hasEq){//...//设置动画进行中状态this.data.isAnimating=true;const { canvas, context, gridSize, gridList, bgImg } = this.data.canvasData;const { grids, direction } = hasEq;//...//动画结束方法const stopAnimation = () => {if(isToggle){//...}//...this.redrawBg(()=>{//处理消除逻辑的方法this.clearGrids(res=>{const { count } = res;if(count>0){//更新消除结果this.setData({scope:this.data.scope+count,timerNum:maxTimeNum,progressPercent:100})}});});}function startAnimation(){this.clearCanvas(true);context.drawImage(bgImg,0,0,canvas.width,canvas.height);//...省略了开始动画逻辑setTimeout(startAnimation,AnimationTime*2);}//重新绘制背景的方法this.redrawBg((img)=>{//开始动画startAnimation();});
}

代码中还用到了好几个方法,这里就不展开讲,
代码中用到了好几个箭头符号,据说这个是叫语法糖,

考虑到有的萌新小同学看不懂,这里讲一下语法糖,
箭头符号表示什么意思,看如下代码,应该能明白吧

//箭头函数
const fun1 = (args) => {console.log('hello')
};//编译器把箭头函数还原
function fun1(args) {console.log('hello')
}

也许有同学疑问:没看出什么用途。
在一个对象实例 中,写方法(函数 )里有写了this的,就用箭头函数吧。

为方便阅读思路清晰,避免搞混,代码中用到的this,都是统一指向当前页面的实例对象

处理消除逻辑

在一个方法clearGrids(callback,count=0)里实现,这是最难的实现部分,代码如下

const { gridList, gridSize, columns, rows, padding } = this.data.canvasData;
//先扫描可以清除的网格,记录下来
let clearGridsAtCols = [];
for(let x=0; x<columns; x++){//...for(let y=rows-1; y>=0; y--){//...}//...
}
//如果有清除的网络
if(clearGridsAtCols.length>0) {//这里添加需要移动的数据clearGridsAtCols.forEach(item=>{//...for(let y=rows-1; y>=0; y--){//...}});const offset = 4;//开始动画const startAnimating=()=>{let isDownMove=false;//处理移动的数据,更新移动位置clearGridsAtCols.forEach(x=>{//...});//如果没有可以移动的,就结束动画if(!isDownMove){//...  this.redrawBg(()=>{//...  处理完,回调返回消除数量if(typeof callback == 'function') callback({ count });});return;}this.redrawBg();//还能向下移动的,再调用startAnimating方法setTimeout(startAnimating,AnimationTime);}//重新绘制一遍this.redrawBg();if(!this.data.isAnimating){this.data.isAnimating=true;this.data.audioPlay.play();//播放游戏音效// console.log('start autio')}startAnimating();return true;
}
//没有可消除的,直接返回状态
this.data.isAnimating=false;
return false;

关于项目

相关代码就讲到这了,有了上面的思路,应该能自己做出来吧,接下来是运行测试

运行效果

做好后,运行效果图如下,换个水果之类的图片,看着感觉还可以


为什么不用表情图片了?

有人体验后反馈,说玩久了眼睛看着会不舒服,

原因吧,是显示的图片又多又小,还有图片之间颜色相似的也多,找起来容易引起视觉疲劳,

使用表情图片大多数都是黄脸,所以颜色相似的很多,这是作为图形设计师的基本常识把,
知道了这点不足,就换了图片,看上面的感觉还好,

运行小程序项目,游戏交互效果,动图如下

既然能换图片,那就改名项目为图片消消乐好了

项目源码

  • 用到的图片素材,是参考 fluent-emoji 这里,有很多可以选几个当作游戏素材,
  • 用到的游戏背景音,也是从网上找来的,很容易找到,
  • 想看项目源代码,请前往 下载点这里 找消消乐项目源码

【图片消消乐】单机游戏-微信小程序项目开发入门相关推荐

  1. 【俄罗斯方块】单机游戏-微信小程序项目开发入门

    这是一个仿俄罗斯方块小游戏的微信小程序,只需要写一小段代码就实现出来了,有兴趣的同学完全可以自己动手开发,来看看实现过程是怎样的呢,边写边做,一起来回忆小时候玩过的经典俄罗斯方块游戏吧. 文章目录 创 ...

  2. 【斗兽棋】-单机游戏-微信小程序项目开发入门

    还记得小时候玩过的斗兽棋游戏不,90后的经典怀旧游戏哦,笔者TA远方在读小学的时候,曾玩过的游戏名单就有它,也许有人忘记了,现在才想起,理清一下斗兽棋游戏的规则:

  3. 摩斯电码-打码机练习-微信小程序项目开发入门

    这是一个对摩斯电码小程序项目的开发步骤详解,用于摩尔斯电码打码练习,适合新手练习,对摩尔斯电码很感兴趣,相信掌握以后就可以使用手电筒发射光信号了,只要远处有人能看到,以约定的摩斯电码翻译就会明白,有意 ...

  4. 【微信小程序】开发入门篇(二)

    前言 ❤️ 所谓信仰,可能就是在人们一无所剩的时候仅有的那种东西 ❤️ [微信小程序]开发入门篇(二) 一.小程序的宿主环境 (1)宿主环境简介 1.1 什么是宿主环境 1.2 小程序的宿主环境 (2 ...

  5. DAY10微信小程序项目开发技术总结

    一.每日实习任务 1.网页基础知识(html,css,js) 今天老师为我们介绍讲解了网页基础知识(html,css,js).首先,我们安装了谷歌浏览器及sublime text.老师为我们介绍了ht ...

  6. 《微信小程序:开发入门及案例详解》—— 3.4 小结

    本节书摘来自华章出版社<微信小程序:开发入门及案例详解>一 书中的第3章,第3.4节,作者李骏 边思,更多章节内容可以访问云栖社区"华章计算机"公众号查看. 3.4 小 ...

  7. 【微信小程序】开发入门篇(一)

    前言 ❤️ 你可能认为一个人无法改变世界,但我想让你知道,这个世界也无法改变像我这样的人 ❤️ [微信小程序]开发入门篇(一) 一.小程序简介 (1)小程序与普通网页开发的区别 二.第一个小程序 (1 ...

  8. 微信小程序云开发入门(图文详解)

    以下内容是我第一次微信小程序云开发的一次记录,从真正的0基础入门,到基本掌握一些内容. 其中遇到的一些问题,和根据网上 的一些资料,整理出来真正可用的,实测可行的源代码. 以下放出我的github源码 ...

  9. 微信小程序云开发入门(二)-数据库详解

    微信小程序云开发入门(二)-数据库详解 接上一篇:微信小程序云开发入门(一) 摘要: 因为微信小程序云数据库有点类似传统的关系型数据库,但又有所不同.所以刚入手的时候会有点困扰,经过一段时间的学习和摸 ...

最新文章

  1. 【HDU/算法】最短路问题 杭电OJ 2544 (Dijkstra,Dijkstra+priority_queue,Floyd,Bellman_ford,SPFA)
  2. 机器人会模仿人类微笑了,但我总觉得这笑容……
  3. Kali Linux 2016.2初体验使用总结
  4. php将换行变为 p 标签,editplus 将文本换行替换为p标签
  5. 【django】配置redis数据库【4】
  6. 最小公倍数 最大公约数
  7. 荧光共定位定量分析,单通道散点图剖析
  8. Promise处理前端异步事件
  9. 第三章选择结构(一)
  10. java获取微信用户信息(UnionID)
  11. 计算机实训报告英语,英文计算机实习证明格式
  12. 路由变化时使用axios取消所有请求
  13. R语言使用timeROC包计算存在竞争风险情况下的生存资料多时间AUC值、使用cox模型、并添加协变量、R语言使用timeROC包的plotAUCcurve函数可视化多时间生存资料的AUC曲线
  14. 美丽的夜,一个程序员的思考
  15. openssl的x509命令简单入门
  16. React路由管理 —— React Router 总结
  17. 如何开好项目启动大会
  18. 解密秒杀系统架构:不是所有的秒杀都是秒杀
  19. C# 打印照片和文档
  20. 使用linux命令直接截取ip地址

热门文章

  1. 开心农场的20条人生启示
  2. 计算机的I/O设备及I/O设备控制方式(DMA、IO通道)
  3. Java在eclipse中调用opencv时报错:java.lang.UnsatisfiedLinkError的解决方法
  4. 纯CSS实现左右拖拽改变布局大小 使用CSS3中resize属性 水平,垂直拖动元素,改变大小
  5. 微软合作伙伴表示Windows 8的发布导致用户改用苹果
  6. python eve mysql_python Eve RESTFul 尝试笔记
  7. 基于微信小程序和安卓的二手车查询平台APP
  8. python一个文本循环输出_Python实现动态循环输出文字功能
  9. 2000-2019年各省产业结构合理化指数(干春晖泰尔指数)
  10. 企业级管理软件快速开发平台技术白皮书