还记得小时候玩过的经典贪食蛇小游戏吗,游戏规则简单,对新手来说很容易入门编程,在这里,TA远方来讲一下实现过程,详解过程按照实现思路来,请仔细往下看
这里TA远方使用的是微信开发工具,新建项目的时候,选择小程序,使用Javascript语言,选择空模板,最后点击完成,💡 阅读此文章需要满足的以下条件
会使用微信开发工具,或者HBuilderX开发工具属性Javascript编程语言熟悉使用Canvas组件
若有项目源码,直接选择小程序再打开项目就即可 接下来,在项目中的路径pages/index/index.wxml文件里,写一下布局,用层叠布局方式,需要放置三个Canvas组件,分别是下层canvA,中间层canvB,上层canvC,上层的加上一些写属性,如下图
<view class="page"><view class="canvas-box"><canvas class="canvas" canvas-id="canvA" id="canvA"></canvas><canvas class="canvas" canvas-id="canvB" id="canvB"></canvas><canvas class="canvas" canvas-id="canvC" id="canvC" :disable-scroll="true" bindtouchstart="onTouchStart" bindtouchmove="onTouchMove" bindtouchend="onTouchEnd" bindtouchcancel="onTouchEnd"></canvas></view></view>
接着在对应的index.js文件里写下代码,获取布局中的那三个Canvas组件,做好初始化,代码如下
App({data: {canvas: {/*包括画布的相关数据*/},//...},onLoad(){wx.createSelectorQuery().select('#canvA').fields({size: true }, res => {const canvA = wx.createCanvasContext('canvA');const canvB = wx.createCanvasContext('canvB');const canvC = wx.createCanvasContext('canvC');//初始画布的相关数据this.data.canvas = {width: res.width,height: res.height,canvA,canvB,canvC};this.canvA_drawBg();this.canvC_drawSnake();this.canvB_drawFood();this.redraw(this.data.canvas);}).exec();},//...绘制地图...背景图...还有障碍物(现在把它叫做墙壁)canvA_drawBg(){//...},//...绘制蛇自身canvC_drawSnake(){//...},//...绘制食物...canvB_drawFood(){//...},//...重绘...实现游戏刷新的逻辑redraw(canvas){//...},})
接下来,用下层canvA组件的来实现绘制,应该绘制什么呢,是游戏地图,效果如下图所示,绘制网格
那么如何绘制游戏地图呢,用火眼金睛
观察一下,游戏中的小蛇每移动一步的位置和距离都在变化,用网格来表示最合适不过了,绘制过程很简单,看下方法canvA_drawBg的实现,代码如下
App({data: {canvas: {/*包括画布的相关数据*/},config: {gridSize: 10,//网格大小drawGrid: false,//绘制网格wellCount: 5,//墙壁数量},//...}/*** 绘制地图...背景图...还有障碍物(现在把它叫做墙壁)*/canvA_drawBg(){const {width, height, canvA } = this.data.canvas;//画布宽高const {gridSize:g, drawGrid, wellCount } = this.data.config;//关于游戏的配置//g是网格大小, wellCount障碍物数量//此处省略...初始化处理this.data.canvas.w = w;//网格宽this.data.canvas.h = h;//网格高this.data.canvas.l = l;//left 左边距this.data.canvas.t = t;//top 顶边距this.data.canvas.ln = ln;//x grid count 横向 X坐标网格数this.data.canvas.tn = tn;//y grid count 纵向 y坐标网格数//绘制背景色canvA.fillRect(0,0,width,height);//所有坐标集合,包括制障碍物的const coordinaties = [];for(let i=0; i<ln; i++){//是否绘制网格,纵向绘制if (drawGrid) {//...}//是否绘制障碍物,判断数量大于0即可if (wellCount>0) {//...}}//是否绘制网格,横向绘制if (drawGrid) {for(let i=0; i<tn; i++){//...}} else {//...如果不绘制网格,就绘制边框}if(coordinaties.length>0) {//...do{//绘制障碍物...}while(this.data.wall.length<wellCount);//绘制障碍物的数量判断//...}canvA.draw();},})
接下来,绘制蛇自身,在网格中的位置,方法canvC_drawSnake的实现代码如下
const MoveDire = {u: 'up',d: 'down',l: 'left',r: 'right',};const Colors = {bgColor: '#A4D1FC',fillColor: '#365DC0',};App({data: {canvas: {/*包括画布的相关数据*/},config: {gridSize: 10,//网格大小snakeMinLength: 1,//初始蛇身长度},snakeDire: MoveDire.u,//移动方向,默认向上移//...}/*** 绘制蛇自身* @param isReset 是否重置蛇身长*/canvC_drawSnake(isReset){const {gridSize:g, snakeMinLength } = this.data.config;//关于游戏的配置const {w, h, canvC, l, t, ln, tn } = this.data.canvas;const {snakeDire } = this.data;if (isReset || this.data.snake.length<=0) {this.data.snake = [this.createCoordinate(parseInt(ln/2), parseInt(tn/2), snakeDire)];for(let i=1; i<snakeMinLength; i++){this.addSnakeLength();}}canvC.setFillStyle(Colors.fillColor);this.snake.forEach(f => {canvC.fillRect(f.x,f.y,g,g);});canvC.draw();},//...创建坐标信息,传入坐标系a和bcreateCoordinate(a,b){//...}//...加长蛇身addSnakeLength(){//...}})
接着,绘制食物,用一个位置来表示,方法canvB_drawFood的实现代码如下
App({data: {canvas: {/*包括画布的相关数据*/},config: {gridSize: 10,//网格大小},food: null,//食物坐标//...},/*** 绘制食物* @param isNew 是否用新的*/canvB_drawFood(isNew){const {gridSize:g } = this.data.config;const {canvB } = this.data.canvas;let f = this.data.food;if (isNew || f==null) {f = this.createRandomFood();this.data.food = f;}canvB.setFillStyle(Colors.fillColor);canvB.fillRect(f.x,f.y,g,g);canvB.draw();},//...获取新的食物,食物放置位置随机createRandomFood(){//...}})
最后,实现重绘的逻辑方法redraw,刷新游戏视图,代码如下
const App = getApp();const Util = App.getUtil();App({data: {canvas: {/*包括画布的相关数据*/},config: {speed: 5,//初始移动速度 1~5},food: null,//食物坐标timer: null,//定时器//...},/*** 实现重绘的逻辑,刷新游戏* @param canvas 画布信息*/redraw(canvas){const {speed } = this.data.config;const maxSpeed = 200;//记录最大值const moveSpeed = Math.max(maxSpeed*Math.abs(speed-1) - this.data.snake.length*100, maxSpeed);//下一次刷新等待时长越短,移动速度越大//定时器this.data.timer = setTimeout(() => {const {food } = this.data;//判断是否显示食物,实现闪烁效果if (food) {let timeout = Date.now()-food.t;if (timeout>1000*30) {//如果超过30s,则食物过期,应重新出现this.canvB_drawFood(true);} else if (food.show) {// food.t = Date.now();food.show = false;this.canvB_drawFood();//绘制} else if (!food.show) {food.show = true;this.data.canvas.canvB.draw();//不绘制} else {}}//移动蛇身,返回判断是否继续游戏let confirmGame = this.moveCoordinaties();if (confirmGame) {this.canvC_drawSnake();//使用window下的方法,如果页面不是激活状态下的话,动画会自动暂停,有效节省了CPU开销requestAnimationFrame(this.redraw);return;}//游戏结束了this.endGame(true);//显示对话框Util.alert('游戏结束!确认要重新开始吗?', res => {if (!res.confirm) return;wx.reLaunch({url:'/pages/index/index'})});}, moveSpeed);},//...绘制蛇身canvC_drawSnake(){//...},//...移动蛇身,处理蛇身新坐标,新的位置moveCoordinaties(){//...蛇头移动后//...判断是否吃到食物,碰到自己//...判断是否碰到墙(障碍物)//...最后移动没问题的话,返回true, 否则返回falsereturn true;},//...对游戏结束的处理,关闭定时器endGame(isEnd){//...},onUnload(){this.endGame();}})
对了,还有,游戏的交互逻辑,就用触摸操作手势,实现代码如下
import Gesture from './../../utils/gesture.js';App({data: {gestures: {},//定义触摸操作的数据snakeDire: MoveDire.u,//当前的移动方向},onTouchStart(event){this.data.gestures.a = event.touches[0];//触摸开始点},onTouchMove(event){this.data.gestures.b = event.touches[0];//最后一个触摸点},//触摸结束onTouchEnd(){const {snakeDire: dire } = this.data;//Gesture是引入来的,封装了手势操作的处理const Ges = Gesture.G;//获取手势操作方法getGconst g = Gesture.getG(this.data.gestures);switch(g){case Ges.left: //向左划//判断,若是相反方向就无效,以下同理if (dire!=MoveDire.r) {this.snakeDire = MoveDire.l;}break;case Ges.up: //向上划if (dire!=MoveDire.d) {this.snakeDire = MoveDire.u;}break;case Ges.right: //向右划if (dire!=MoveDire.l) {this.snakeDire = MoveDire.r;}break;case Ges.down: //向下划if (dire!=MoveDire.u) {this.snakeDire = MoveDire.d;}break;case Ges.click: //只是点击default:this.pauseGame();}this.data.gestures = {};},//...暂停,或继续游戏pauseGame(){//...}})
就写到这里,其它的细节不重要,在这里就不讲,重要是实现思路清晰,最后,项目中小程序编译运行没问题的话,结果如下图,一个动态图,这样看着比较直观💡 小提示
关于Gesture触摸手势操作的详解文章请点击此处查看
如需要看微信小程序项目源码的,还有类似uniapp项目源码也有,
请点击【项目源码】前往找到再下载(如果是在手机浏览器上看的话,可能会找不到下载项,请用PC端浏览器上查看即可),
若觉得此文章有帮助,请点赞❤
再走不迟~,谢谢!