周末的时候突发奇想就想写个小游戏,想到大学那会cv了网上的贪吃蛇案例,这次不如自己动手写个试试。由于本菜鸟对 canvas 比较好奇,索性就用canvas写个贪吃蛇小游戏练练手。好了,废话不多讲直接开搞!
设计思路
1.先画个500*500的绿色背景,游戏开始时在预先设定好的位置生成我的小蛇并随机生成食物。
2.给蛇一个初始速度(10px/0.5s)和运动方向(left),按上下左右键时改变当前蛇的运动方向
3.蛇每移动一步都应该重新绘制蛇,(我呢偷懒了直接重绘了整个地图里的内容,蛇身的绘制我会单独讲下)
4.蛇吃到食物时,增加一节蛇身,并清除食物同时随机生成一个食物
5.当然哈,既然是开发游戏,当然要对蛇和食物编写单独的构造函数和原型咯
代码分析
食物
这里主要展示的就是食物的构造函数、生成以及随机生成和被吃了。
// 构造个食物
function Food(color) {
this.x = 0; // 食物x坐标
this.y = 0; // 食物y坐标
this.fw = 10; // 食物的宽 10px
this.fh = 10; // 食物的高 10px
this.color = color;
}
// 食物正常生成
Food.prototype.init = function() {
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.fw, this.fh);
}
// 食物随机生成
Food.prototype.initRandom = function() {
this.x = Math.floor(Math.random()*(50+1))*10;
this.y = Math.floor(Math.random()*(50+1))*10;
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.fw, this.fh);
}
// 食物被干了
Food.prototype.clear = function() {
ctx.clearRect(this.x, this.y, this.fw, this.fh);
}
蛇
蛇的构造函数,主要是对蛇做了一些初始化的设置,包括蛇的x,y,宽高(蛇的粗细),颜色和方向,
function Snake(color){
this.x = 200;
this.y = 200;
this.sw = 10;
this.sh = 10;
this.direction = 'left';
// this.speed = speed;
this.color = color;
this.body = [
{x:this.x,y:this.y},
{x:this.x+10,y:this.y},
{x:this.x+20,y:this.y},
]
}
蛇的移动之后该怎么绘制是这个游戏的难点,我是将蛇整体(蛇体)分成了两部分:蛇头、蛇身。每次移动,我们可以根据移动方向和当前状态的蛇头位置计算出下次蛇头的x y值,再没有吃到食物的情况下,只需要截取除最后一节蛇身外的蛇体并用往数组头部插入移动后的蛇头
this.body.unshift(head);
this.body = newbody(this.body);
// 新的身体
function newbody(body) {
let newbody = [];
for (let i = 0; i < body.length-1; i++){
newbody.push(body[i])
}
return newbody;
}
蛇的每一次移动,我们都得重新绘制蛇身,我这里是用遍历的方式对每节蛇身进行绘制。
// 蛇变身
Snake.prototype.init = function() {
ctx.fillStyle = this.color;
this.body.forEach(item=>{
ctx.fillRect(item.x, item.y, this.sw, this.sh);
})
}
蛇的移动有两种情况,一种是匀速 另一种便是键盘的输入控制蛇的移动。以下是键盘控制蛇的移动
// 移动
Snake.prototype.move = function(e) {
if(e.keyCode==38){ // 上
let head = {
x: this.body[0].x,
y: this.body[0].y - 10
}
this.body.unshift(head);
this.body = newbody(this.body);
this.init();
}
if(e.keyCode==40){ //下
let head = {
x: this.body[0].x,
y: this.body[0].y + 10
}
this.body.unshift(head);
this.body = newbody(this.body);
this.init();
}
if(e.keyCode==37){ //左
let head = {
x: this.body[0].x-10,
y: this.body[0].y
}
this.body.unshift(head);
this.body = newbody(this.body);
this.init();
}
if(e.keyCode==39){ //右
let head = {
x: this.body[0].x+10,
y: this.body[0].y
}
this.body.unshift(head);
this.body = newbody(this.body);
this.init();
}
}
这里展示的是蛇的匀速运动
// 蛇匀速移动
Snake.prototype.moveUniform = function() {
if(this.direction === 'up'){ // 上
let head = {
x: this.body[0].x,
y: this.body[0].y - 10
}
this.body.unshift(head);
this.body = newbody(this.body);
this.init();
}
if(this.direction === 'down'){ //下
let head = {
x: this.body[0].x,
y: this.body[0].y + 10
}
this.body.unshift(head);
this.body = newbody(this.body);
this.init();
}
if(this.direction === 'left'){ //左
let head = {
x: this.body[0].x-10,
y: this.body[0].y
}
this.body.unshift(head);
this.body = newbody(this.body);
this.init();
}
if(this.direction === 'right'){ //右
let head = {
x: this.body[0].x+10,
y: this.body[0].y
}
this.body.unshift(head);
this.body = newbody(this.body);
this.init();
}
}
地图
既然是贪吃蛇,当然少不了地图了。
canvas教程代码扒拉下,这就搞个绿色的地图出来,:
const canvas = document.getElementById('map');
const ctx = canvas.getContext('2d');
ctx.fillStyle = 'green';
ctx.fillRect(0, 0, 500, 500);
前面已经对食物和蛇进行了构造函数和原型的编写。下面我们就开始对它两进行蹂躏,简单实例化外加计时器:
// 开始游戏
var theFood = {};
var mySnake= {};
function start() {
theFood = new Food('red');
theFood.initRandom();
mySnake = new Snake('blue');
mySnake.init();
// 通过计时器实现蛇的匀速移动
window.setInterval(uniformMove, 500);
}
当我们不去对键盘进行输出时,就是计时器在持续输出(按照蛇的移动方向匀速移动)
// 匀速移动
function uniformMove() {
let ate = false; // 当前的食物是否被吃
let direction = mySnake.direction;
if(direction === 'up'){ // 上
if(mySnake.body[0].y-10 === theFood.y && mySnake.body[0].x === theFood.x) {
ate = eat(mySnake,theFood,ate)
}
initMap();
}
if(direction === 'down'){ //下
if(mySnake.body[0].y+10 === theFood.y && mySnake.body[0].x === theFood.x) {
ate = eat(mySnake,theFood,ate)
}
initMap();
}
if(direction === 'left'){ //左
if(mySnake.body[0].x-10 === theFood.x && mySnake.body[0].y === theFood.y) {
ate = eat(mySnake,theFood,ate)
}
initMap();
}
if(direction === 'right'){ //右
if(mySnake.body[0].x+10 === theFood.x && mySnake.body[0].y === theFood.y) {
ate = eat(mySnake,theFood,ate)
}
initMap();
}
mySnake.moveUniform();
// 判断食物是否被吃
if(ate) {
theFood.initRandom()
ate = false;
} else {
theFood.init()
}
}
// 蛇吃食物的同时需要增加一节蛇身并清空当前的食物
function eat(mySnake,theFood,ate) {
let tail = mySnake.body[mySnake.body.length-1];
mySnake.body.push(tail);// 增加尾部
theFood.clear(); // 清除食物
ate = true;
return ate
}
当时当我们控制方向键时(其实跟前面的匀速运动很相似):
// 键盘点击事件
document.onkeydown = function(event) {
let e = event || window.event || arguments.callee.caller.arguments[0];
if(![37, 38, 39, 40].includes(e.keyCode)){
return;
}
let ate = false; // 当前的食物是否被吃
if(e.keyCode==38){ // 上
mySnake.direction = 'up'; // 设置蛇的移动方向
if(mySnake.body[0].y-10 === theFood.y && mySnake.body[0].x === theFood.x) {
ate = eat(mySnake,theFood, ate)
}
initMap();
}
if(e.keyCode==40){ //下
mySnake.direction = 'down';
if(mySnake.body[0].y+10 === theFood.y && mySnake.body[0].x === theFood.x) {
ate = eat(mySnake,theFood,ate)
}
initMap();
}
if(e.keyCode==37){ //左
mySnake.direction = 'left';
if(mySnake.body[0].x-10 === theFood.x && mySnake.body[0].y === theFood.y) {
ate = eat(mySnake,theFood,ate)
}
initMap();
}
if(e.keyCode==39){ //右
mySnake.direction = 'right';
if(mySnake.body[0].x+10 === theFood.x && mySnake.body[0].y === theFood.y) {
ate = eat(mySnake,theFood,ate)
}
initMap();
}
// 判断食物是否被吃
mySnake.move(e);
if(ate) {
theFood.initRandom()
ate = false;
} else {
theFood.init()
}
}
结束游戏的话,只需要将蛇头所在的xy和边界的xy值进行比较即可。到此就先结束吧,明天还得搬砖呢~
我将源码分享到了gitlab,有需要的童鞋自取哈~
https://gitlab.com/1842347744/snake.git