檢驗原生JS功底的時候到了--純JS實現貪食蛇

前言

之前讀書學習C語言的時候,老是想着要完成貪食蛇這款遊戲,可是那時候個人編程基礎和編程思惟還不夠紮實和成熟,因此致使怎麼折騰都寫不出來,可是如今工做了,就想着要完成讀書時候的當心願,就開始動手寫了,寫着寫着,就還真是寫出來了,哈哈,看來我也是成長了很多。。。html

貪食蛇這款遊戲,相信你們必定都不陌生了,童年之做,用編程語言編寫出來的貪食蛇,無非就是要實現幾個功能:蛇的移動、是否吃到本身、吃到食物、撞牆,只要把須要實現的功能給列出來了,接下來只要一個個去實現就行了,思路清晰仍是很重要的,由於都是用原生JS來編寫的,因此對JS功底仍是有點要求的,我以爲有時間練手的能夠嘗試一下,好了,廢話很少說,進去正題。。。node

分析思路

首先咱們須要建立一個大畫布,開始和暫停按鈕,畫布是蛇在裏面跑的,開始和暫停分別控制開始和中止,這裏須要用到HTML和CSS,但我選擇了用JS實現,畢竟練手嘛,固然是要往不一樣的方法去出發。git

而後動態建立蛇的節點(一個蛇頭兩個蛇點)和食物,好了,最基本的HTML和CSS已經用JS來實現了,接下來開始實現須要實現的功能了編程

  • 蛇的移動
  • 撞牆的判斷
  • 是否吃到食物
  • 是否吃到本身

好了,思路分析好了就開始動手了!!bash

Let't it go ..app

場景準備

如上所述,首先要建立場景,畫布和開始、暫停按鈕dom

直接上代碼:編程語言

let d = document,
                //定時器
                timer = null,
                //禁止連續按鍵
                disabled = 0,
                //main是畫布,open是開始,stop是暫停
                node = {
                    main: d.createElement('div'),
                    open: d.createElement('button'),
                    stop: d.createElement('button'),
                }
            
            //賦予樣式
            node.main.style.background = `rgb(104, 163, 210)`;
            node.main.style.width = `${15 * (30+1)}px`;
            node.main.style.height = `${15 * (20+1)}px`;
            node.main.id = 'main';
            node.main.position = `relative`;
            node.open.textContent = `開始`;
            node.stop.textContent = `暫停`;
            //加入document.body
            for( key in node) document.body.appendChild(node[key]);
複製代碼

這樣就建立好畫布和按鈕了,完成了基本的場景準備函數

建立蛇節點

這裏我原本打算用函數function Snake(),而後經過構造函數去調用的,但想了想,可是用ES6的class去定義吧!!學習

直接上代碼:

class Snake {
            constructor() {
                this.init();
            }

            //初始化
            init() {
                //添加蛇身彙總
                this.snakeAll = document.createElement('div');
                this.snakeAll.id = 'all';
                this.snakeAll.style.position = `relative`;
                this.snakeAll.style.top = `${0}px`;
                document.getElementById('main').appendChild(this.snakeAll);

                //蛇節點座標
                this.Snode = [
                    { x: 3 , y: 1, flag: null},
                    { x: 2 , y: 1, flag: null},
                    { x: 1 , y: 1, flag: null},
                ];

                //默認方向
                this.direction = 'right';
                //蛇節點寬度
                this.width = 15;
                //蛇節點長度
                this.height = 15;
                this.createSnake();

            }
            
        }
複製代碼

new Snake()的時候調用構造函數,調用this.init(),對蛇須要用到的參數進行初始化和建立蛇必要的節點

接下來看this.createSnake()代碼

//建立蛇
            createSnake() {
                //首先清除蛇的節點
                this.removeSnake();
                //根據座標添加節點
                this.Snode.forEach( item => {
                    if(item.x != null) {
                        let div = document.createElement('div');
                        div.style.background = `#734fb3`;
                        div.style.width = `${this.width}px`
                        div.style.height = `${this.width}px`
                        div.style.position = `absolute`;
                        div.style.left = `${item.x * this.width}px`
                        div.style.top = `${item.y * this.height}px`
                        div.style.borderRadius = `${50}%`
                        div.classList.add('snake');
                        item.flag = div;
                        this.snakeAll.appendChild(div);
                    }
                })

                
            }
複製代碼

this.createSnake()也是class Snake {}裏面的方法,只是我怕代碼太長大家看的不耐煩...

this.createSnake()的做用就是根據蛇的默認參數來建立蛇的節點,建立以前須要清除以前存在的蛇的節點

this.removeSnake()代碼:

//刪除蛇
            removeSnake() {
                for( let key in this.Snode) {
                    this.Snode[key].flag && this.snakeAll.removeChild(this.Snode[key].flag)
                }
            }
複製代碼

好了,建立蛇的節點的邏輯大概就是這樣,這裏來總結一下:

經過定義Snake的類來編寫方法,new Snake()的時候會調用構造函數裏面的this.init()來設置默認參數

而後調用this.createSnake()來根據this.Snode[]默認參數來建立蛇的節點

建立以前必定要記住調用this.removeSnake()來清除以前建立的節點,這部分的邏輯大概就是如此。。。

建立食物

建立食物的邏輯比較簡單吧,跟建立蛇的邏輯是差很少的

也是定義一個Food的類,而後經過new Food()去調用建立食物

好了,直接上代碼

class Food {
            constructor() {
                this.width = 15;
                this.height = 15;

                this.createFood();
            }

            //建立食物  
            createFood() {
                this.food && this.food.remove();
                this.food = document.createElement('div');
                this.food.id = 'food';
                this.x = Math.floor(Math.random() * 15);
                this.y = Math.floor(Math.random() * 10);
                this.food.style.position = `relative`;
                this.food.style.top = `${this.width * this.y}px`;
                this.food.style.left = `${this.height * this.x}px`;
                this.food.style.width = `${this.width}px`
                this.food.style.height = `${this.height}px`
                this.food.style.background = `#3F51B5`
                this.food.style.borderRadius = `${50}%`
                document.getElementById('all').appendChild(this.food);
            }
        }
複製代碼

看了上面建立蛇的邏輯,再看這段建立食物的邏輯應該問題不大吧?哈哈,我相信你看懂了

蛇的移動

接下來開始說蛇的移動了,這段是重點呢,由於蛇的移動包含了撞牆的判斷吃到食物的判斷吃到本身的判斷等功能,是有點麻煩,可是我會分段來講,這樣會清晰不少,好了,直接開始...

蛇的移動的原理就是除了蛇頭蛇尾的後一個點的座標等於前一個點的座標,蛇頭的座標由this.direction這個屬性來控制的,而後經過計時器不斷執行this.createSnake()就能夠實現蛇的移動了

好了,直接上代碼

//運動
            run(food,timer) {
                this.food = food;
                //移動蛇身體
                for(let i=this.Snode.length-1; i>0; i--) {
                    this.Snode[i].x = this.Snode[i-1].x;
                    this.Snode[i].y = this.Snode[i-1].y;
                }
                //移動蛇頭
                switch(this.direction){
                    case 'left': this.Snode[0].x -=1;break;
                    case 'right': this.Snode[0].x += 1; break;
                    case 'top': this.Snode[0].y -= 1; break;
                    case 'bottom': this.Snode[0].y += 1;break;
                }
                this.createSnake();
            }
複製代碼

這樣子就能夠實現蛇的移動了,是否是很簡單呢

下面開始細分功能,如下的代碼都是在run()方法裏面的,我把他給分出來這樣容易看清楚

判斷是否撞牆

判斷是否撞牆就是判斷蛇的X,Y座標是否超出畫布的最大或最小X、Y座標

上面的代碼說了

畫布X的座標範圍是0 - 30

畫布Y的座標範圍是0 - 20

//判斷是否撞牆,根據蛇頭的X和Y座標去判斷
        if(this.Snode[0].x > 30 || this.Snode[0].x < 0 || this.Snode[0].y > 20 || this.Snode[0].y < 0) {
            alert("哎喲,老兄,看路啊,把我給撞暈了");
            this.snakeAll.remove();
            clearInterval(timer);
            this.init();
            this.food.createFood();
        }
複製代碼

撞牆後移動蛇的所有節點,清除定時,從新調用this.init()方法建立,和調用this.food.createFood()方法建立食物

是否是條例很清晰呢?

判斷吃到食物

吃到食物的邏輯也是很簡單的

當蛇頭的座標XY都等於食物的XY就等因而吃到了食物...

//吃到食物
        if(this.Snode[0].x == this.food.x && this.Snode[0].y == this.food.y) {
            this.Snode.push({x: null,y: null,flag: null});
            this.food.createFood();
        }
複製代碼

this.Snode控制蛇的節點,吃到食物後須要多增長一個節點,直接push()進去就好

而後從新調用方法建立食物就好

判斷是否吃到本身

本身吃到本身,這種狀況只會在蛇擁有四個節點以上的狀況下才會發生

當蛇頭的座標等於本身身體的任意一個座標的時候,就能夠判斷爲吃到了本身

//判斷是否吃到本身,只有四個點以上纔有可能吃到本身
    for(let i=4;i<this.Snode.length;i++) {
        if(this.Snode[0].x == this.Snode[i].x && this.Snode[0].y == this.Snode[i].y) {
            alert("哎喲,老兄,肚子餓也不能吃本身啊");
            this.snakeAll.remove();
            clearInterval(timer);
            this.init();
            this.food.createFood();
        }
    }
複製代碼

吃到本身後,也是須要初始化和從新建立食物

好了,看到這裏,基本上已經完成了,接下來只須要添加點DOM事件便可

添加事件

添加鍵盤事件和開始、暫停定時事件

//添加鍵盤事件
        document.body.onkeydown = e => {
            let ev = e || window.event;
            if(!disabled) {
                disabled = 1;
                switch(ev.keyCode){
                    case 39:{
                        if(snake.direction != 'left'){
                            snake.direction = 'right';
                            break;
                        }
                    }
                    case 38:{
                        if(snake.direction != 'bottom'){
                            snake.direction = 'top';
                            break;
                        }
                    }
                    case 37:{
                        if(snake.direction != 'right'){
                            snake.direction = 'left';
                            break;
                        }
                    }
                    case 40:{
                        if(snake.direction != 'top'){
                            snake.direction = 'bottom';
                            break;
                        }
                    }
                };  
            } 

            setTimeout( () => {
                disabled = 0;
            },100);
        }

        //開始
        node.open.addEventListener('click', ()=> {
            clearInterval(timer);
            timer = setInterval( () => {
                snake.run(food,timer);
            },100);
        })

        //暫停
        node.stop.addEventListener('click', ()=> {
            clearInterval(timer);
        })
複製代碼

好了,代碼寫到這裏基本上就完成了,不知道大家有沒有看到最後(傷心臉)!!

完整代碼

電腦上重裝了,沒了git,懶得裝,直接貼完整代碼

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <script>

        class Snake {
            constructor() {
                this.init();
            }

            //初始化
            init() {
                //添加蛇身彙總
                this.snakeAll = document.createElement('div');
                this.snakeAll.id = 'all';
                this.snakeAll.style.position = `relative`;
                this.snakeAll.style.top = `${0}px`;
                document.getElementById('main').appendChild(this.snakeAll);

                //蛇節點座標
                this.Snode = [
                    { x: 3 , y: 1, flag: null},
                    { x: 2 , y: 1, flag: null},
                    { x: 1 , y: 1, flag: null},
                ];

                //默認方向
                this.direction = 'right';
                //蛇節點寬度
                this.width = 15;
                //蛇節點長度
                this.height = 15;
                this.createSnake();

            }

            //建立蛇
            createSnake() {
                //首先清除蛇的節點
                this.removeSnake();
                //根據座標添加節點
                this.Snode.forEach( item => {
                    if(item.x != null) {
                        let div = document.createElement('div');
                        div.style.background = `#734fb3`;
                        div.style.width = `${this.width}px`
                        div.style.height = `${this.width}px`
                        div.style.position = `absolute`;
                        div.style.left = `${item.x * this.width}px`
                        div.style.top = `${item.y * this.height}px`
                        div.style.borderRadius = `${50}%`
                        div.classList.add('snake');
                        item.flag = div;
                        this.snakeAll.appendChild(div);
                    }
                })

                
            }

            //刪除蛇
            removeSnake() {
                for( let key in this.Snode) {
                    this.Snode[key].flag && this.snakeAll.removeChild(this.Snode[key].flag)
                }
            }

            //運動
            run(food,timer) {
                this.food = food;
                //移動蛇身體
                for(let i=this.Snode.length-1; i>0; i--) {
                    this.Snode[i].x = this.Snode[i-1].x;
                    this.Snode[i].y = this.Snode[i-1].y;
                }
                //移動蛇頭
                switch(this.direction){
                    case 'left': this.Snode[0].x -=1;break;
                    case 'right': this.Snode[0].x += 1; break;
                    case 'top': this.Snode[0].y -= 1; break;
                    case 'bottom': this.Snode[0].y += 1;break;
                }

                //判斷是否撞牆,根據蛇頭的X和Y座標去判斷
                if(this.Snode[0].x > 30 || this.Snode[0].x < 0 || this.Snode[0].y > 20 || this.Snode[0].y < 0) {
                    alert("哎喲,老兄,看路啊,把我給撞暈了");
                    this.snakeAll.remove();
                    clearInterval(timer);
                    this.init();
                    this.food.createFood();
                }
                
                //吃到食物
                if(this.Snode[0].x == this.food.x && this.Snode[0].y == this.food.y) {
                    this.Snode.push({x: null,y: null,flag: null});
                    this.food.createFood();
                }

                //判斷是否吃到本身,只有四個點以上纔有可能吃到本身
                for(let i=4;i<this.Snode.length;i++) {
                    if(this.Snode[0].x == this.Snode[i].x && this.Snode[0].y == this.Snode[i].y) {
                        alert("哎喲,老兄,肚子餓也不能吃本身啊");
                        this.snakeAll.remove();
                        clearInterval(timer);
                    this.init();
                    this.food.createFood();
                    }
                }
                

                this.createSnake();
            }


        }

        class Food {
            constructor() {
                this.width = 15;
                this.height = 15;

                this.createFood();
            }

            //建立食物  
            createFood() {
                this.food && this.food.remove();
                this.food = document.createElement('div');
                this.food.id = 'food';
                this.x = Math.floor(Math.random() * 15);
                this.y = Math.floor(Math.random() * 10);
                this.food.style.position = `relative`;
                this.food.style.top = `${this.width * this.y}px`;
                this.food.style.left = `${this.height * this.x}px`;
                this.food.style.width = `${this.width}px`
                this.food.style.height = `${this.height}px`
                this.food.style.background = `#3F51B5`
                this.food.style.borderRadius = `${50}%`
                document.getElementById('all').appendChild(this.food);
            }
        }

        (()=>{
            let d = document,
                //定時器
                timer = null,
                //禁止連續按鍵
                disabled = 0,
                //main是畫布,open是開始,stop是暫停
                node = {
                    main: d.createElement('div'),
                    open: d.createElement('button'),
                    stop: d.createElement('button'),
                }
            
            //賦予樣式
            node.main.style.background = `rgb(104, 163, 210)`;
            node.main.style.width = `${15 * (30+1)}px`;
            node.main.style.height = `${15 * (20+1)}px`;
            node.main.id = 'main';
            node.main.position = `relative`;
            node.open.textContent = `開始`;
            node.stop.textContent = `暫停`;
            //加入document.body
            for( key in node) document.body.appendChild(node[key]);

            let snake = new Snake();
            let food = new Food();

            //添加鍵盤事件
            document.body.onkeydown = e => {
                let ev = e || window.event;
                if(!disabled) {
                    disabled = 1;
                    switch(ev.keyCode){
                        case 39:{
                            if(snake.direction != 'left'){
                                snake.direction = 'right';
                                break;
                            }
                        }
                        case 38:{
                            if(snake.direction != 'bottom'){
                                snake.direction = 'top';
                                break;
                            }
                        }
                        case 37:{
                            if(snake.direction != 'right'){
                                snake.direction = 'left';
                                break;
                            }
                        }
                        case 40:{
                            if(snake.direction != 'top'){
                                snake.direction = 'bottom';
                                break;
                            }
                        }
                    };  
                } 

                setTimeout( () => {
                    disabled = 0;
                },100);
            }

            //開始
            node.open.addEventListener('click', ()=> {
                clearInterval(timer);
                timer = setInterval( () => {
                    snake.run(food,timer);
                },100);
            })

            //暫停
            node.stop.addEventListener('click', ()=> {
                clearInterval(timer);
            })
        })();
    </script>
</body>
</html>
複製代碼

本人是菜鳥,若是以爲我那裏寫得很差,能夠及時指出來,我也很樂意接受高手的指點,哈哈!!

對了 對了 忘了貼效果圖

難看是難看了點,但重要的是邏輯思惟嘛,哈哈!!

總結

我以爲寫下了這個貪食蛇,是對本身的編程實力和思惟是一種檢測,雖然沒有什麼實際性的意義(尷尬臉)

不知不覺我也已經工做一年多了,從畢業的跨行成功到如今生活的無奈,有時候我也不知道堅持是爲了什麼,我也彷佛是忘記了本身的初心了,沒錯,我就是那種很喪很喪的那種人,哈哈!!

最後送你們一句話:人生,無非就是起起落落落落落落,因此要好好活着,哈哈,生活,加油!!

相關文章
相關標籤/搜索