今天早上坐地铁13号线,到西二旗站下车后,看见了一个身穿印有科比曼巴标志的紫金外套的小姐姐,瞬间吸引了我,还戴着棒球帽,在这疫情肆虐的时期,真是人群中的一股清流,一股温暖。
真想上去搭讪,奈何人群中我只看了她一眼,而她,早已烟消云散。
于是乎我打算用JS写个和黑曼巴有关的东西,于是就想到了写个贪吃蛇吧。纪念一下老大。
话不多说,进入正题
先欣赏一下效果图吧
紫金模式
暗黑模式
技术
- Jquery
- Js
- Htmll
- Css
- Bootstrap
- SKPlayer
实现功能
- 开始/暂停/继续游戏
- 游戏得分
- 排行榜
- 暗黑/紫金模式切换
- 集成SKPlayer组件
- 附赠3首SQ无损品质音乐
- 夜空中最亮的星(纯音乐钢琴版)
- See You Again(Charlie Puth/Wiz Khalifa)
- Because Of You(Kelly Clarkson)
游戏原理
- 首先有个游戏区域,例如在900*600内的区域游戏
- 以XY点座标来实现蛇和事物的定位
- 蛇行走的路线通过定时器来实现,例如蛇头往左移动,1秒后,蛇头的座标X加上1个单元,而座标Y不变,蛇身体里的座标也都这样计算,X加1个单元,Y轴不变,这样就实现了蛇的整体移动
- 吃食物是通过蛇头和食物的XY座标相等来判断的,然后让食物消失,在蛇的尾巴加上一个单元
- 随机生成食物就是通过随机XY座标来实现的
- 碰壁,或者撞到自己的身体就游戏结束,这也是通过XY座标来实现的。
- 游戏难易程度就是定时器的时间间隔,越难的间隔越短,蛇移动的越快
核心代码
使用构造方法创建黑曼巴蛇
function BlackMamba() {
//初始化蛇的属性,是由蛇头,身体,蛇尾,一个3个单位组成,默认地址在左上角
this.width = snakeIconWidth;
this.height = snakeIconHeight;
this.direction = direction;
this.body = [
{x: 2, y: 0},
{x: 1, y: 0},
{x: 0, y: 0}
];
}
让蛇在页面中显示出来
this.display = function () {
for (var i = 0; i < this.body.length; i++) {
if (this.body[i].x != null) { // 当吃到食物时,x==null,不能新建,不然会在0,0处新建一个
var s = document.createElement('div');
//将节点保存到状态中,以便于后面删除
this.body[i].flag = s;
//设置宽高
s.style.width = this.width + 'px';
s.style.height = this.height + 'px';
if (i == 0) {
s.style.backgroundImage = "url(image/snake.png)";
} else {
s.style.backgroundImage = "url(image/body.png)";
}
//设置位置,这里计算位置的时候一定是30的倍数,这样吃蛇的时候才能根据座标吻合来判断
s.style.position = 'absolute';
s.style.left = this.body[i].x * this.width + 'px';
s.style.top = this.body[i].y * this.height + 'px';
//添加到游戏中
gameArea.appendChild(s);
}
}
};
让蛇跑起来
//让蛇跑起来,后一个元素到前一个元素的位置
//蛇头根据方向处理,所以i不能等于0
this.run = function () {
//后一个元素到前一个元素的位置
for (var i = this.body.length - 1; i > 0; i--) {
this.body[i].x = this.body[i - 1].x;
this.body[i].y = this.body[i - 1].y;
}
//根据方向处理蛇头
switch (this.direction) {
case "left":
this.body[0].x -= 1;
break;
case "right":
this.body[0].x += 1;
break;
case "up":
this.body[0].y -= 1;
break;
case "down":
this.body[0].y += 1;
break;
}
flag = false;
//判断是否出界,因为底图的宽是900,所以宽度不能大于(900/30)-1=29,高是600,所以高度不能大于(600/30)-1=19
if (this.body[0].x < 0 || this.body[0].x > 29 || this.body[0].y < 0 || this.body[0].y > 19) {
gameOver(this);
return false;
}
//判断蛇头吃到食物,判断的依据是xy座标重合,就是蛇头和食物的座标一样
if (this.body[0].x == food.x && this.body[0].y == food.y) {
//蛇加一节,因为根据最后节点定,下面display时,会自动赋值的
this.body.push({x: null, y: null, flag: null});
scoreNum++;
$("#score").html("玩家分数:" + scoreNum);
// 清除食物,重新生成食物
gameArea.removeChild(food.flag);
food.display();
}
//碰到自己就死亡,从第五个开始与头判断,因为前四个永远撞不到
for (var i = 4; i < this.body.length; i++) {
if (this.body[0].x == this.body[i].x && this.body[0].y == this.body[i].y) {
gameOver(this);
return false;
}
}
//先删掉初始的蛇,在显示新蛇
for (var i = 0; i < this.body.length; i++) {
if (this.body[i].flag != null) { // 当吃到食物时,flag是等于null,且不能删除
gameArea.removeChild(this.body[i].flag);
}
}
//重新显示蛇
this.display();
}
随机创建食物
//随机创建食物
function Food() {
this.width = snakeIconWidth;
this.height = snakeIconHeight;
this.display = function () {
var food = document.createElement('div');
this.flag = food;
food.style.width = this.width + 'px';
food.style.height = this.height + 'px';
food.style.backgroundImage = "url(image/food.png)";
food.style.position = 'absolute';
//取1-9的随机数
this.x = randomNum(1,29);
this.y = randomNum(1,19);
//递归检查随机生成的XY座标是否和 蛇的身体座标重合
if(judgeFoodAndSnakeXYCoincidence(this.x,this.y)){
this.display();
}else{
//这里计算位置的时候一定是30的倍数,这样吃蛇的时候才能根据座标吻合来判断
food.style.left = this.x * 30 + 'px';
food.style.top = this.y * 30 + 'px';
gameArea.appendChild(food);
}
}
}
判断随机生成的事物会不会和蛇本身的XY座标重合,不允许重合
function judgeFoodAndSnakeXYCoincidence(x,y){
var flag = false;
for (var i = 0; i < blackMamba.body.length; i++) {
if (blackMamba.body[i].x == x && blackMamba.body[i].y == y) {
flag = true;
break;
console.log(i + '--------' + blackMamba.body[i].x + '---------------' + blackMamba.body[i].y);
}
}
return flag;
}
游戏结束
//游戏结束
function gameOver(obg){
// 清除定时器
clearInterval(timer);
//弹出提示框,用的是bootstrap modal
$('#myModal').modal();
//显示玩家分数
$("#content").html('GAME OVER! 您的成绩是 ' + scoreNum + ' 分');
//删除所有蛇
for (var i = 0; i < obg.body.length; i++) {
if (obg.body[i].flag != null) { // 如果刚吃完就死掉,会加一个值为null的
gameArea.removeChild(obg.body[i].flag);
}
}
// 回到初始状态
obg.body = [
{x: 2, y: 0},
{x: 1, y: 0},
{x: 0, y: 0}
];
//初始化蛇
obg.direction = direction;
obg.display();
//把成绩添加到排行榜中
var playerName = $("#player").val();
var typeName = '';
var type = $('input:radio[name="inlineRadioOptions"]:checked').val();
switch (type) {
case "500":
typeName = '简单'
break;
case "300":
typeName = '一般'
break;
case "100":
typeName = '困难'
break;
}
var html = "<tr><td>" + playerName + "</td><td>" + typeName + "</td><td>" + scoreNum + "</td><td>1分钟前</td></tr>";
$("#tableBody").append(html);
var trLength = $("#tableBody > tr").length;
if(trLength > 5){
$("#tableBody tr").eq(1).remove();
}
//分数清零
scoreNum = 0;
$("#score").html("玩家分数:" + scoreNum);
}
键盘方向按键事件
//阻塞按键事件
var flag = false;
//给body加按键事件,上下左右
document.body.onkeydown = function (e) {
if (flag) {
return;
}
// 有事件对象就用事件对象,没有就自己创建一个,兼容低版本浏览器
var ev = e || window.event;
//这里加入判断,例如蛇正在往左移动,这是就不能直接往右移动,只能往上或者下
switch (ev.keyCode) {
case 38:
if (blackMamba.direction != 'down') {
blackMamba.direction = "up";
}
break;
case 40:
if (blackMamba.direction != "up") {
blackMamba.direction = "down";
}
break;
case 37:
if (blackMamba.direction != "right") {
blackMamba.direction = "left";
}
break;
case 39:
if (blackMamba.direction != "left") {
blackMamba.direction = "right";
}
break;
}
flag = true;
};
点击开始按钮
//点击开始游戏按钮事件
begin.onclick = function () {
//判断玩家姓名是否为空
var playerName = $("#player").val();
if (playerName == '' || playerName == undefined || playerName == null) {
$('#myModal').modal();
$("#content").html('请输入玩家姓名!');
return;
}
clearInterval(timer);
//获取游戏难易程度,每次点击都获取一遍,这样在暂停的时候可以修改难易程度
var type = $('input:radio[name="inlineRadioOptions"]:checked').val();
timer = setInterval("blackMamba.run()", type);
};
点击暂停/继续按钮
//点击暂停,继续游戏事件
stop.onclick = function () {
console.log(timer)
if (timer == undefined) {
$('#myModal').modal();
$("#content").html('请先开始游戏');
return;
} else {
if (this.innerHTML == '继续游戏') {
this.innerHTML = '暂停游戏'
//获取游戏难易程度,每次点击都获取一遍,这样在暂停的时候可以修改难易程度
var type = $('input:radio[name="inlineRadioOptions"]:checked').val();
timer = setInterval("blackMamba.run()", type);
} else {
clearInterval(timer);
this.innerHTML = '继续游戏'
return;
}
}
}
切换到暗黑模式
callKobe.onclick = function () {
document.body.style.backgroundColor = white
document.body.style.color = black;
gameArea.style.backgroundImage = "url(image/kobe.jpg)";
document.body.style.color = black;
begin.setAttribute('class','btn btn-default btn-kobe');
stop.setAttribute('class','btn btn-default btn-kobe');
jianbian(document.body);
jianbian(gameArea);
skPlayerDom.style.backgroundColor = white;
skPlayerDom.style.color = mp3Black;
document.getElementsByClassName('skPlayer-percent').item(0).style.backgroundColor = mp3Black;
document.getElementsByClassName('skPlayer-percent').item(1).style.backgroundColor = mp3Black;
document.getElementsByClassName('skPlayer-curMusic').item(0).style.backgroundColor = white;
document.getElementsByClassName('skPlayer-list').item(0).style.backgroundColor = white;
}
切换到紫金模式
restKobe.onclick = function () {
document.body.style.backgroundColor = gold;
document.body.style.color = purple;
gameArea.style.backgroundImage = "url(image/bgkb.jpg)";
gameArea.style.filter="alpha(opacity=0)";
gameArea.style.opacity=0;
document.getElementById('kobeTop').style.color = purple;
document.getElementById('leftForm').style.border = '2px solid ' + purple;
begin.setAttribute('class','btn btn-default btn-purple-gold');
stop.setAttribute('class','btn btn-default btn-purple-gold');
jianbian(document.body);
jianbian(gameArea);
skPlayerDom.style.backgroundColor = purple;
skPlayerDom.style.color = gold;
document.getElementsByClassName('skPlayer-percent').item(0).style.backgroundColor = gold;
document.getElementsByClassName('skPlayer-percent').item(1).style.backgroundColor = gold;
document.getElementsByClassName('skPlayer-curMusic').item(0).style.backgroundColor = purple;
document.getElementsByClassName('skPlayer-list').item(0).style.backgroundColor = purple;
}
实现切换渐变效果
//css渐变效果
function jianbian(obj) {
var alpha = 0;
clearInterval(jianBianTimer);
jianBianTimer = setInterval(function () {
alpha++;
obj.style.opacity = alpha / 100;
obj.style.filter = "alpha(opacity=" + alpha + ")";
if (alpha == 100) {
clearInterval(jianBianTimer);
}
}, 10);
}
MP3播放器
var player = new skPlayer({
autoplay: false,
//可选项,自动播放,默认为false,true/false
listshow: true,
//可选项,列表显示,默认为true,true/false
mode: 'singleloop',
//可选项,循环模式,默认为'listloop'
//'listloop',列表循环
//'singleloop',单曲循环
//可选项配置同上
music: {
//必需项,音乐配置
type: 'file',
//必需项,自配置文件方式指定填'file'
source: [
//必需项,音乐文件数组
{
name: '夜空中最亮的星',
//必需项,歌名
author: '无损纯音乐钢琴版',
//必需项,歌手
src: 'mp3/star.mp3',
//必需项,音乐文件
cover: 'image/haha.jpg'
//必需项,封面图片
},
{
name: 'See You Again',
//必需项,歌名
author: 'Charlie Puth/Wiz Khalifa',
//必需项,歌手
src: 'mp3/see you again.mp3',
//必需项,音乐文件
cover: 'image/mp2.jpg'
//必需项,封面图片
},
{
name: 'Because Of You',
//必需项,歌名
author: 'Kelly Clarkson',
//必需项,歌手
src: 'mp3/because of you.mp3',
//必需项,音乐文件
cover: 'image/mp3.jpg'
//必需项,封面图片
}
]
}
});
获取源码
关注公众号:老王搞BUG,回复 “贪吃蛇源码” 即可获取全部源码