贪吃蛇-单机游戏-微信小程序项目开发流程详解

还记得小时候玩过的经典贪食蛇小游戏吗,游戏规则简单,对新手来说很容易入门编程,在这里,TA远方来讲一下实现过程,详解过程按照实现思路来,请仔细往下看

💡 阅读此文章需要满足的以下条件

  • 会使用微信开发工具,或者HBuilderX开发工具
  • 属性Javascript编程语言
  • 熟悉使用Canvas组件
  • 这里TA远方使用的是微信开发工具,新建项目的时候,选择小程序,使用Javascript语言,选择空模板,最后点击完成,
    若有项目源码,直接选择小程序再打开项目就即可
  1. 接下来,在项目中的路径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>
  1. 接着在对应的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){
		//...
	},
})
  1. 接下来,用下层canvA组件的来实现绘制,应该绘制什么呢,是游戏地图,效果如下图所示,绘制网格
    在这里插入图片描述

  2. 那么如何绘制游戏地图呢,用火眼金睛观察一下,游戏中的小蛇每移动一步的位置和距离都在变化,用网格来表示最合适不过了,绘制过程很简单,看下方法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();
	},
})
  1. 接下来,绘制蛇自身,在网格中的位置,方法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和b
	createCoordinate(a,b){
		//...
	}
	//...加长蛇身
	addSnakeLength(){
		//...
	}
})

  1. 接着,绘制食物,用一个位置来表示,方法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(){
		//...
	}
})
  1. 最后,实现重绘的逻辑方法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, 否则返回false
		return true;
	},
	//...对游戏结束的处理,关闭定时器
	endGame(isEnd){
		//...
	},
	onUnload(){
		this.endGame();
	}
})
  1. 对了,还有,游戏的交互逻辑,就用触摸操作手势,实现代码如下
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;
		//获取手势操作方法getG
		const 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触摸手势操作的详解文章请点击此处查看

  1. 就写到这里,其它的细节不重要,在这里就不讲,重要是实现思路清晰,最后,项目中小程序编译运行没问题的话,结果如下图,一个动态图,这样看着比较直观
    在这里插入图片描述

如需要看微信小程序项目源码的,还有类似uniapp项目源码也有,
请点击【项目源码】前往找到再下载(如果是在手机浏览器上看的话,可能会找不到下载项,请用PC端浏览器上查看即可 ),
若觉得此文章有帮助,请点赞再走不迟~,谢谢!
在这里插入图片描述

  • 3
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TA远方

谢谢!收到你的爱╮(╯▽╰)╭

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值