致敬科比,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,回覆 “貪吃蛇源碼” 即可獲取全部源碼

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