致敬科比,JS手写贪吃蛇

今天早上坐地铁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,回复 “贪吃蛇源码” 即可获取全部源码

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章