20行js的貪吃蛇

本文首發(fā)于我的博客

在csdn上看到一位大神用20行代碼就寫出了一個貪吃蛇的小游戲泻蚊,鏈接請點(diǎn)這里躲舌,感覺被驚艷到了,就試著讀了一下這段代碼性雄,閱讀過程中不斷為作者寫法的巧妙而叫絕没卸,其中我發(fā)現(xiàn)自己對運(yùn)算符優(yōu)先級和一些js的技巧不是很清楚,所以看完之后決定把思路分享出來秒旋,和大家一起學(xué)習(xí)约计。可以在這里預(yù)覽效果迁筛,可能需要翻墻煤蚌,你也可以在我的github上直接查看源碼。

我對代碼稍稍做了些修改细卧,并添加了一些注釋尉桩,方便理解。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>貪吃蛇重構(gòu)</title>
    <style>
        body {
            display: flex;
            height: 100vh;
            margin: 0;
            padding: 0;
            justify-content: center;
            align-items: center;
        }
    </style>
</head>
<body>
    <canvas id="can" width="400" height="400" style="background-color: black">對不起贪庙,您的瀏覽器不支持canvas</canvas>
    <script>

        var snake = [41, 40],       //snake隊(duì)列表示蛇身蜘犁,初始節(jié)點(diǎn)存在但不顯示
            direction = 1,          //1表示向右,-1表示向左止邮,20表示向下这橙,-20表示向上
            food = 43,              //食物的位置
            n,                      //與下次移動的位置有關(guān)
            box = document.getElementById('can').getContext('2d');
                                    //從0到399表示box里[0~19]*[0~19]的所有節(jié)點(diǎn),每20px一個節(jié)點(diǎn)

        function draw(seat, color) {
            box.fillStyle = color;
            box.fillRect(seat % 20 *20 + 1, ~~(seat / 20) * 20 + 1, 18, 18);
                                    //用color填充一個矩形导披,以前兩個參數(shù)為x屈扎,y坐標(biāo),后兩個參數(shù)為寬和高撩匕。
        }

        document.onkeydown = function(evt) {    
                                    //當(dāng)鍵盤上下左右鍵摁下的時候改變direction
            direction = snake[1] - snake[0] == (n = [-1, -20, 1, 20][(evt || event).keyCode - 37] || direction) ? direction : n;
        };

        !function() {
            snake.unshift(n = snake[0] + direction);    
                                    //此時的n為下次蛇頭出現(xiàn)的位置鹰晨,n進(jìn)入隊(duì)列
            if(snake.indexOf(n, 1) > 0 || n < 0 || n > 399 || direction == 1 && n % 20 == 0 || direction == -1 && n % 20 == 19) {
                                    //if語句判斷貪吃蛇是否撞到自己或者墻壁,碰到時返回止毕,結(jié)束程序
                return alert("GAME OVER!");
            }
            draw(n, "lime");        //畫出蛇頭下次出現(xiàn)的位置
            if(n == food) {         //如果吃到食物時并村,產(chǎn)生一個蛇身以外的隨機(jī)的點(diǎn),不會去掉蛇尾
                while (snake.indexOf(food = ~~(Math.random() * 400)) >= 0);
                draw(food, "yellow");
            } else {                //沒有吃到食物時正常移動滓技,蛇尾出隊(duì)列
                draw(snake.pop(),"black");
            }
            setTimeout(arguments.callee, 150);      
                                    //每隔0.15秒執(zhí)行函數(shù)一次哩牍,可以調(diào)節(jié)蛇的速度
        }();
    </script>
</body>
</html>

首先,我們要知道做一個貪吃蛇最主要的是什么令漂,是做出蛇活動的場所和如何使蛇動起來膝昆。
??我們先看蛇活動的場所:

    <!-- html -->
    <canvas id="can" width="400" height="400" style="background-color: black">
        對不起,您的瀏覽器不支持canvas
    </canvas>
    <!-- js -->
    box = document.getElementById('can').getContext('2d');

這是一個400px*400pxcanvas叠必,思路是以20px*20px為一個方格荚孵,組成2020列的方陣,總共400格纬朝,然后綠色填充的格子表示蛇身收叶,用黃色表示食物。這400個格子和數(shù)字0~399一一對應(yīng)共苛,對應(yīng)的方式就是以20作為基數(shù)判没,n / 20再取整表示第幾行蜓萄,n % 20表示第幾列。行數(shù)和列數(shù)都用0~19表示澄峰。
??蛇用一個一維數(shù)組表示嫉沽,每個值都是這400個數(shù)中的一個,用var snake = [41, 40];初始化這條蛇俏竞,索引0為蛇頭绸硕。food表示食物的位置,direction表示蛇頭下一次運(yùn)動的轉(zhuǎn)向魂毁。蛇的運(yùn)動就用添加和刪除數(shù)組元素來實(shí)現(xiàn)玻佩,每次執(zhí)行繪制蛇頭,去掉蛇尾席楚,循環(huán)執(zhí)行使蛇運(yùn)動咬崔。
??下邊從函數(shù)運(yùn)行的起始處(39行)開始看:

!function() {}();

什么鬼?這其實(shí)是立即執(zhí)行函數(shù)IIFE的另一種寫法酣胀。關(guān)于IIFE刁赦,這篇文章講的挺不錯的。繼續(xù)往下看闻镶,給蛇頭添加一個節(jié)點(diǎn)n甚脉,其值為當(dāng)前蛇頭的值加direction的值,如此一來就能理解為什么要用20表示向下铆农,-20表示向上了牺氨。再下一行是一個if語句,其中值得提醒的是&&的優(yōu)先級高于||墩剖,這個語句就是判斷即將出現(xiàn)的蛇頭是不是屬于蛇身猴凹,或者跑到box外邊去了。如果沒有死亡岭皂,就把這個蛇頭繪制出來郊霎,下邊就看看繪制的代碼:

function draw(seat, color) {
    box.fillStyle = color;
    box.fillRect(seat % 20 *20 + 1, ~~(seat / 20) * 20 + 1, 18, 18);
}

填充時填充18*18的像素,留1px邊框爷绘。.fillRect()中第一個參數(shù)就是要繪制的矩形的x坐標(biāo)seat % 20 *20 + 1书劝,即先得到所要繪制的矩形塊在方陣中的位置:第~~(seat / 20)行,第seat % 20列土至,再* 20 + 1具體到像素點(diǎn)购对。可能這個~~有點(diǎn)難理解陶因,我感覺在這里的用處應(yīng)該和Math.floor()差不多骡苞,可以用來截除小數(shù)。這兩者具體還有一些區(qū)別,可以去看《你不知道的js(中卷)》62頁解幽。
??回到47行贴见,又是一個判斷語句,判斷下次蛇頭出現(xiàn)的位置是不是和當(dāng)前的食物的位置相同亚铁,如果相同蝇刀,生成下一個食物螟加,食物的位置為一個隨機(jī)數(shù)徘溢,但是要判斷這個點(diǎn)不是出現(xiàn)在當(dāng)前的蛇身上,繪制食物捆探。如果沒有吃到食物然爆,即蛇在正常運(yùn)動時,每向前一次黍图,將蛇尾彈出曾雕,并利用其返回值將這個點(diǎn)重新繪制為黑色。
??最后的setTimeout助被,循環(huán)執(zhí)行當(dāng)前函數(shù)剖张,設(shè)置執(zhí)行周期來調(diào)蛇的移動速度。
??到了這里揩环,我們發(fā)現(xiàn)這條蛇已經(jīng)可以動了搔弄,加上鍵盤的操作就完成了:

document.onkeydown = function(evt) {    
    direction = snake[1] - snake[0] == (n = [-1, -20, 1, 20][(evt || event).keyCode - 37] || direction) ? direction : n;
};

將這個函數(shù)綁定到鍵盤事件上,evt || event用法的原因這里有詳細(xì)的解釋丰滑,是為了兼容ie顾犹。
??三目運(yùn)算符?前邊的判斷語句又可分為兩部分:

  1. snake[1] - snake[0]的值應(yīng)該就是-direction,按理說此處寫成-direction應(yīng)該和原來是一個效果褒墨,那為什么沒有這么做呢炫刷,因?yàn)槿绻@樣寫,玩家可能在一個函數(shù)周期中多次改變direction的值郁妈,最后使得direction和當(dāng)前真正的運(yùn)動方向不一致浑玛,導(dǎo)致游戲崩潰。
  2. ==后邊噩咪,[-1, -20, 1, 20][(evt || event).keyCode - 37]中前邊的[]是一個數(shù)組顾彰,后邊的[]是取索引,左上右下四個鍵的keyCode分別為37, 38, 39, 40剧腻,計(jì)算后的索引為0, 1, 2, 3拘央,使方向鍵與direction的取值對應(yīng)起來。這里的巧妙之處在于如果按下的按鍵不是方向鍵书在,在數(shù)組中將得不到對應(yīng)的值灰伟,返回undefine。此時,由于之后的||運(yùn)算符栏账,n會取到direction原來的值帖族。

再用三目運(yùn)算符來判斷,如果按鍵方向不是反方向挡爵,就更新direction的值竖般。

以上就是本篇的全部內(nèi)容了,雖然都是一些基礎(chǔ)的東西茶鹃,但是感覺還是挺好玩的涣雕。要是哪里理解的不對還希望指證出來,共同進(jìn)步闭翩。如果你覺得有所收獲記得在github上點(diǎn)個star哦挣郭。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市疗韵,隨后出現(xiàn)的幾起案子兑障,更是在濱河造成了極大的恐慌,老刑警劉巖蕉汪,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件流译,死亡現(xiàn)場離奇詭異,居然都是意外死亡者疤,警方通過查閱死者的電腦和手機(jī)福澡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來宛渐,“玉大人竞漾,你說我怎么就攤上這事】妫” “怎么了业岁?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長寇蚊。 經(jīng)常有香客問我笔时,道長,這世上最難降的妖魔是什么仗岸? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任允耿,我火速辦了婚禮,結(jié)果婚禮上扒怖,老公的妹妹穿的比我還像新娘较锡。我一直安慰自己,他們只是感情好盗痒,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布蚂蕴。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪骡楼。 梳的紋絲不亂的頭發(fā)上熔号,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天,我揣著相機(jī)與錄音鸟整,去河邊找鬼引镊。 笑死,一個胖子當(dāng)著我的面吹牛篮条,可吹牛的內(nèi)容都是我干的弟头。 我是一名探鬼主播,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼兑燥,長吁一口氣:“原來是場噩夢啊……” “哼亮瓷!你這毒婦竟也來了琴拧?” 一聲冷哼從身側(cè)響起降瞳,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蚓胸,沒想到半個月后挣饥,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡沛膳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年扔枫,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片锹安。...
    茶點(diǎn)故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡短荐,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出叹哭,到底是詐尸還是另有隱情忍宋,我是刑警寧澤,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布风罩,位于F島的核電站糠排,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏超升。R本人自食惡果不足惜入宦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望室琢。 院中可真熱鬧乾闰,春花似錦、人聲如沸盈滴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至宽菜,卻和暖如春谣膳,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背铅乡。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工继谚, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人阵幸。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓花履,卻偏偏與公主長得像,于是被迫代替她去往敵國和親挚赊。 傳聞我的和親對象是個殘疾皇子诡壁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評論 2 359

推薦閱讀更多精彩內(nèi)容