亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

JS學(xué)習(xí)筆記之貪吃蛇小游戲demo實(shí)例詳解

 更新時(shí)間:2019年05月29日 10:21:39   作者:倪曉磊  
這篇文章主要介紹了JS學(xué)習(xí)筆記之貪吃蛇小游戲demo,結(jié)合實(shí)例形式詳細(xì)分析了javascript實(shí)現(xiàn)貪吃蛇小游戲的原理、步驟與相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下

本文實(shí)例講述了JS學(xué)習(xí)筆記之貪吃蛇小游戲demo實(shí)例。分享給大家供大家參考,具體如下:

最近跟著視頻教程打了一個(gè)貪吃蛇,

來記錄一下實(shí)現(xiàn)思路,

先上代碼
靜態(tài)頁

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <title>貪吃蛇</title>
</head>
<style>
*{
  margin: 0;
  padding: 0;
}
  .map{
    width:800px;
    height: 600px;
    background-color: #ccc;
    position:relative;
  }

</style>
<body>
<!-- 畫出地圖,設(shè)置樣式 -->
 <div class="map">

 </div>
</body>
<script src="food.js"></script>
<script src="snake.js"></script>
<script src="game.js"></script>
</html>

food.js

//食物就是一個(gè)對(duì)象,寬高橫縱坐標(biāo),先定義構(gòu)造函數(shù),然后創(chuàng)建對(duì)象
(function (){
  var elements=[];//用來保存每個(gè)小方塊食物
  function Food(x,y,width,height,color){
    //橫縱坐標(biāo)
    this.x=x||0;
    this.y=y||0;
    this.width=width||20;
    this.height=height||20;
    //背景顏色
    this.color=color||"green";
  }
  //為原型添加初始化的方法(作用:在頁面上取顯示這個(gè)食物)
  //因?yàn)槭澄镆诘貓D上顯示,所以,需要地圖這個(gè)參數(shù)
  Food.prototype.init=function(map){
    //先刪除這個(gè)食物
    //外部無法訪問的函數(shù)
    remove()
    var div=document.createElement("div");
    //把這個(gè)div加到map中
    map.appendChild(div);
    //設(shè)置div的樣式
    div.style.width=this.width+"px";
    div.style.height=this.height+"px";
    div.style.backgroundColor=this.color;
    // div.style.left=this.x+"px";

    //先脫離文檔流
    div.style.position="absolute";
    //隨機(jī)橫縱坐標(biāo)
    this.x=parseInt(Math.random()*(map.offsetWidth/this.width))*this.width;
    this.y=parseInt(Math.random()*(map.offsetHeight/this.height))*this.height;
    div.style.left=this.x+"px";
    div.style.top=this.y+"px";
    // Food.prototype.init=function(map){

    // }
    //把div加入到數(shù)組elements中
    elements.push(div);
  }

  function remove(){
    //elements數(shù)組中有這個(gè)食物
    for(var i=0;i<elements.length;i++){
      var ele=elements[i]
      //找到這個(gè)子元素的父級(jí)元素,然后刪除這個(gè)子元素
      ele.parentNode.removeChild(ele);
      //再次把elements中的這個(gè)子元素也要?jiǎng)h除
      elements.splice(i,1)
    }
  }
  //把Food暴露給Window,外部可以使用
  window.Food=Food;
}());

snake.js

//蛇
(function(){
  var elements=[];//存放小蛇的每個(gè)身體部分
  //蛇的構(gòu)造函數(shù)
  function Snake(width,height,direction){
    //小蛇的每個(gè)部分的寬
    this.width=width||20;
    this.height=height||20;
    //身體
    this.body=[
      {x:3,y:2,color:"red"},
      {x:2,y:2,color:"orange"},
      {x:1,y:2,color:"orange"}
    ];

    this.direction=direction||"right";
  }
//蛇的初始化
  Snake.prototype.init=function(map){
    remove()
    //循環(huán)遍歷創(chuàng)建div
    for(var i=0;i<this.body.length;i++){
      var obj=this.body[i];
      //創(chuàng)建div
      var div=document.createElement("div");
      //把div加入到map地圖中
      map.appendChild(div);
      //設(shè)置div的樣式;
      div.style.position="absolute";
      div.style.width=this.width+"px";
      div.style.height=this.height+"px";
      div.style.left=obj.x*this.width+"px";
      div.style.top=obj.y*this.height+"px";
      div.style.backgroundColor=obj.color;

      //把div加入到elements數(shù)組中--目的是刪除
      elements.push(div)
    }
  }
  //蛇的移動(dòng)
  Snake.prototype.move=function(food,map){
    //改變蛇身體位置
    var i=this.body.length-1; //2
    for(;i>0;i--){
      this.body[i].x=this.body[i-1].x;
      this.body[i].y=this.body[i-1].y;
    }
    //判斷方向---改變小蛇的頭的坐標(biāo)位置
    switch (this.direction){
      case "right":
        this.body[0].x+=1;
        break;
      case "left":
        this.body[0].x-=1;
        break;
      case "top":
        this.body[0].y-=1;
        break;
      case "bottom":
        this.body[0].y+=1;
        break;
    }

    //判斷有沒有吃到食物
    //小蛇的頭的坐標(biāo)和食物位置
    var headX=this.body[0].x*this.width;
    var headY=this.body[0].y*this.height;
    //食物的橫縱坐標(biāo)
    var foodX=food.x;
    var foodY=food.y;
    if(headX==foodX&&headY==foodY){
      //獲取蛇的最后尾巴
      var last=this.body[this.body.length-1];
      //把最后的蛇尾復(fù)制一份
      this.body.push({
        x:last.x,
        y:last.y,
        color:last.color
      })
      //重新初始化食物
      food.init(map);
    }



  }

  //刪除小蛇的私有函數(shù)
  function remove(){
    //獲取數(shù)組
    var i=elements.length-1;
    for(;i>=0;i--){
      //先從當(dāng)前的子元素中找到該子元素的父級(jí)元素,然后再弄死這個(gè)子元素
      var ele=elements[i];
      //從map地圖上刪除這個(gè)子元素div
      ele.parentNode.removeChild(ele);
      elements.splice(i,1);
    }
  }
  window.Snake=Snake;
}());

//蛇
(function(){
  var elements=[];//存放小蛇的每個(gè)身體部分
  //蛇的構(gòu)造函數(shù)
  function Snake(width,height,direction){
    //小蛇的每個(gè)部分的寬
    this.width=width||20;
    this.height=height||20;
    //身體
    this.body=[
      {x:3,y:2,color:"red"},
      {x:2,y:2,color:"orange"},
      {x:1,y:2,color:"orange"}
    ];

    this.direction=direction||"right";
  }
//蛇的初始化
  Snake.prototype.init=function(map){
    remove()
    //循環(huán)遍歷創(chuàng)建div
    for(var i=0;i<this.body.length;i++){
      var obj=this.body[i];
      //創(chuàng)建div
      var div=document.createElement("div");
      //把div加入到map地圖中
      map.appendChild(div);
      //設(shè)置div的樣式;
      div.style.position="absolute";
      div.style.width=this.width+"px";
      div.style.height=this.height+"px";
      div.style.left=obj.x*this.width+"px";
      div.style.top=obj.y*this.height+"px";
      div.style.backgroundColor=obj.color;

      //把div加入到elements數(shù)組中--目的是刪除
      elements.push(div)
    }
  }
  //蛇的移動(dòng)
  Snake.prototype.move=function(food,map){
    //改變蛇身體位置
    var i=this.body.length-1; //2
    for(;i>0;i--){
      this.body[i].x=this.body[i-1].x;
      this.body[i].y=this.body[i-1].y;
    }
    //判斷方向---改變小蛇的頭的坐標(biāo)位置
    switch (this.direction){
      case "right":
        this.body[0].x+=1;
        break;
      case "left":
        this.body[0].x-=1;
        break;
      case "top":
        this.body[0].y-=1;
        break;
      case "bottom":
        this.body[0].y+=1;
        break;
    }

    //判斷有沒有吃到食物
    //小蛇的頭的坐標(biāo)和食物位置
    var headX=this.body[0].x*this.width;
    var headY=this.body[0].y*this.height;
    //食物的橫縱坐標(biāo)
    var foodX=food.x;
    var foodY=food.y;
    if(headX==foodX&&headY==foodY){
      //獲取蛇的最后尾巴
      var last=this.body[this.body.length-1];
      //把最后的蛇尾復(fù)制一份
      this.body.push({
        x:last.x,
        y:last.y,
        color:last.color
      })
      //重新初始化食物
      food.init(map);
    }



  }

  //刪除小蛇的私有函數(shù)
  function remove(){
    //獲取數(shù)組
    var i=elements.length-1;
    for(;i>=0;i--){
      //先從當(dāng)前的子元素中找到該子元素的父級(jí)元素,然后再弄死這個(gè)子元素
      var ele=elements[i];
      //從map地圖上刪除這個(gè)子元素div
      ele.parentNode.removeChild(ele);
      elements.splice(i,1);
    }
  }
  window.Snake=Snake;
}());

game.js

//游戲
(function(){
   var that=null;
  //游戲的構(gòu)造函數(shù)
  function Game(map){
    this.food=new Food();
    this.snake=new Snake();
    this.map=map;//地圖
    that=this;
  }
  Game.prototype.init=function(){
    //初始化游戲
    //食物初始化
    this.food.init(this.map);
    this.snake.init(this.map);

    this.runSnake(this.food,this.map)
    this.bindKey();
  }

  Game.prototype.runSnake=function(food,map){
    //自動(dòng)的去移動(dòng)
    var timeId=setInterval(function(){
      //此時(shí)的this是window
      //蛇的移動(dòng)
      this.snake.move(food,map);
      //初始化蛇
      this.snake.init(map);
      //橫坐標(biāo)最大值
      var maxX=map.offsetWidth/this.snake.width;
      //獲取縱坐標(biāo)的最大值
      var maxY=map.offsetHeight/this.snake.height;
      //蛇頭的坐標(biāo)
      var headX=this.snake.body[0].x;
      var headY=this.snake.body[0].y;
      //判斷橫坐標(biāo)
      if(headX<0||headX>=maxX){
        clearInterval(timeId)
        alert("游戲結(jié)束")
      }
      //判斷縱坐標(biāo)
      if(headY<0||headY>maxY){
        clearInterval(timeId)
        alert("游戲結(jié)束")
      }
      console.log(headX)
    }.bind(that),150)
  }

  Game.prototype.bindKey=function(){
    //獲取用戶的按鍵,改變小蛇的方向
    document.addEventListener("keydown",function(e){
      //獲取案件的值
      switch(e.keyCode){
        case 37:
          this.snake.direction="left";
          break;
        case 38:
          this.snake.direction="top";
          break;
        case 39:
          this.snake.direction="right";
          break;
        case 40:
          this.snake.direction="bottom";
          break;
      }
    }.bind(that),false)
  }


  window.Game=Game;

}());

//初始化游戲?qū)ο?
var gm=new Game(document.querySelector(".map"));
gm.init()

這里加一個(gè)小插曲,關(guān)于匿名函數(shù)自調(diào)用的三種寫法

第一種


第二種


第三種


注意!注意! 注意! 匿名函數(shù)的最后不要忘記加封號(hào);  因?yàn)槿绻思?系統(tǒng)很容易與后面的代碼混淆 造成各種很奇葩的報(bào)錯(cuò);

這里我推薦第三種寫法,比較清晰明了

好,代碼貼完了,我們來分析一下實(shí)現(xiàn)思路

首先 第一步

建立一個(gè)畫布

設(shè)置畫布的寬度為800px   高度為600px  因?yàn)樾∩?需要在畫布內(nèi)任意移動(dòng),需要脫離標(biāo)準(zhǔn)文檔流,所以需要設(shè)置絕對(duì)定位, 因此我給畫布添加了position:relative;  ,再給背景添加一個(gè)顏色 ,灰色#ccc

好,畫布創(chuàng)建好了,我們可以開始編寫邏輯代碼了

Food.js 代碼分析

首先我們需要?jiǎng)?chuàng)建一個(gè)貪吃蛇 吃的“食物”,因此我們需要?jiǎng)?chuàng)建一個(gè)食物的對(duì)象,這里我在food.js中創(chuàng)建了一個(gè)自定義構(gòu)造函數(shù)

定義了 “食物Food”的 x值、y值、寬度、高度、顏色

這里我利用 || 運(yùn)算設(shè)置了默認(rèn)值,如果 || 左邊為false 則會(huì)自動(dòng)取右邊的值,所以當(dāng)實(shí)例化對(duì)象時(shí)若未傳參時(shí) 自動(dòng)取 “||” 運(yùn)算符右邊的值

然后在"Food"的原型上定義了一個(gè) init 初始化方法

首先創(chuàng)建一個(gè)div ,并將此對(duì)象保存在 div變量中

然后 在地圖中 添加上這個(gè) div  再逐步給這個(gè) div元素 加上他的寬度、高度、背景顏色、并且設(shè)置絕對(duì)定位

那我們?cè)趺炊ㄎ荒兀?/p>

這里我們可以把整個(gè)地圖看成是一個(gè)坐標(biāo)系,把地圖的寬度除以 “食物”的寬度 來切分這個(gè)地圖 ,x=1則相當(dāng)于1個(gè)“食物寬度”的單位長,x=3 則相當(dāng)于3個(gè)“食物寬度的單位長”

高度同理

這里我取了隨機(jī)數(shù)  乘以  地圖被切分的總份數(shù)   這樣就會(huì)的到 隨機(jī)的 X和Y   然后乘以 寬度和高度 就的到了不會(huì)超出地圖的隨機(jī)坐標(biāo) ,舉例 :  因?yàn)镸ath.random(0,5)  是不包括5的


因?yàn)椤笆澄铩笔菚?huì)被貪吃蛇 “吃”掉的

所以我們必須創(chuàng)建一個(gè)方法來“消滅”這個(gè)“食物”,因此我定義了一個(gè) remove 函數(shù)

并且 上方創(chuàng)建了一個(gè) 數(shù)組elements用于存放 創(chuàng)建出來的 "食物" 這個(gè)div元素 的對(duì)象,方便用來刪除,每次初始化“食物” 時(shí),將對(duì)象追加入elements 數(shù)組 


我們遍歷 elements 數(shù)組, 通過數(shù)組中每個(gè)div對(duì)象 先找到其父級(jí),然后通過removeChild 方法將其自身刪除

因?yàn)橛斜4嬖趀lements 數(shù)組中,那我們想要?jiǎng)h除“食物就很方便了”,每次初始化之前我們秩序要調(diào)用一次 remove函數(shù)就是實(shí)現(xiàn)了“消滅”食物,然后再生成新的食物

因?yàn)榇颂幍乃泻瘮?shù)都寫在了一個(gè) 自調(diào)用的匿名函數(shù)中,所以內(nèi)部的Food 對(duì)象,在外部是訪問不到的,

那怎么辦呢?

這里我調(diào)用了window 對(duì)象,將配置好的Food 對(duì)象暴露給window ,這樣,我們?cè)倨渌牡胤接行枰獣r(shí)也可以實(shí)例化 Food了 

Snake.js 代碼分析

其實(shí) “貪吃蛇”身體的實(shí)現(xiàn)和 “食物”的實(shí)現(xiàn)原理大體相同 ,首先我也同樣建立了一個(gè)elements數(shù)組,用于存放之后小蛇移動(dòng)時(shí)所產(chǎn)生的舊的“身體”,用于刪除,因?yàn)槎际蔷植孔兞?,所以雖然兩個(gè)數(shù)組名字相同,但不會(huì)沖突

這里我也給小蛇設(shè)置了寬、高,寬高我設(shè)定為默認(rèn)和食物相同,并且還設(shè)置了方向direction 這用來控制小蛇的移動(dòng)方向,這里我默認(rèn)給了“right”向右移動(dòng),

并且,因?yàn)楫?dāng)游戲開始時(shí),小蛇必當(dāng)有一個(gè)初始的長度,我給了它一個(gè)腦袋 二節(jié)身體,腦袋設(shè)置成了紅色,方便識(shí)別

所以我之后如果需要讓小蛇增加長度,體現(xiàn)越吃越長的感覺 ,只需要在 body這個(gè)數(shù)組中追加對(duì)象就可以了



好,小蛇的基本屬性配置完了,我們下一步就是要初始化小蛇 

同樣的,上方也提到了,我創(chuàng)建過一個(gè)elements數(shù)組,用于存放“小蛇”的舊身體,所以在初始化之前,我們需要調(diào)用remove函數(shù),遍歷elements數(shù)組,和刪除“食物”一樣的方法,將舊的“蛇身”都給刪除了


執(zhí)行完刪除之后呢,我們就可以專心初始化了,

蛇身這么長,那我們?cè)撛趺粗郎呱淼拿恳还?jié)到底該在哪里呢

這時(shí)就用到了我們上方定義的  body 這個(gè)數(shù)組了,它存放了小蛇的身體的所有部分

我們只需要遍歷它,根據(jù)其中每個(gè)對(duì)象的屬性都進(jìn)行創(chuàng)建新的div對(duì)象,同意設(shè)置其寬、高、left、top,并且,將創(chuàng)建好的對(duì)象又存入elements數(shù)組中,方便下一次刪除


定義完初始化的方法后,我們就得考慮小蛇的移動(dòng)該怎么實(shí)現(xiàn)了,

既然是貪吃蛇游戲,我們肯定需要與玩家互動(dòng),讓玩家來操控小蛇的走向,

對(duì)了,我們自定義構(gòu)造函數(shù)的時(shí)候不是設(shè)置過一個(gè)direction 屬性嗎,我們就得利用起來,依據(jù)此來判斷小蛇的走向

至于更改方向,我們放在之后的代碼中實(shí)現(xiàn)

這里我們定義了一個(gè)move 函數(shù) ,并傳入兩個(gè)參數(shù) food "食物對(duì)象" 和 map“地圖對(duì)象”  

那為什么要傳呢,

雖然我們這個(gè)demo里面只有一條小蛇,但這樣的寫法保留了同時(shí)開啟多個(gè)游戲的可能性

  首先我們獲取 body 數(shù)組的長度 存入i 中,然后倒序倒序倒序遍歷 i  ,根據(jù) i 作為索引, 從蛇尾巴開始向蛇腦袋遍歷,大家想象一下,貪吃蛇的蛇身是不是都是按部就班的沿著腦袋走過的路徑走的? 你給它繞個(gè)直角、或者正方形它總是老老實(shí)實(shí)的走完,所以我們每次移動(dòng),只需要控制蛇腦袋移動(dòng),讓蛇身體讓它他們挨個(gè)獲取他們前面那一節(jié)身體的坐標(biāo)就可以了

所以,這里我們倒序遍歷,將第 i 節(jié)身體賦值前一節(jié)身體的 x 屬性和 y 屬性

蛇身的重新賦值做完了,我們判斷一下蛇頭的移動(dòng)方向,因?yàn)槭枪潭ǖ?個(gè)方向,所以這里使用switch較為方便,

根據(jù) 上、下、左、右不一樣的情況對(duì) 頭部的x和y增加或減少

既然是貪吃蛇,我們食物也創(chuàng)建好了,需要實(shí)現(xiàn)貪吃蛇吃食物這個(gè)過程

首先我們分別計(jì)算出 蛇腦袋和食物的X和Y

然后,我們判斷一下,

當(dāng)蛇腦袋的x,y 和食物的x,y都相等的時(shí)候,

我們延長一節(jié)蛇身,這里我復(fù)制了一份最后一節(jié)蛇身體 然后追加入body數(shù)組,注意!

因?yàn)閷?duì)象是引用類型,所以必須這樣拆開賦值

最后,再調(diào)用一次食物的初始化,產(chǎn)生新的食物


同樣的,這里我也將,Snake 對(duì)象暴露給window,供下方的Game.js中的代碼調(diào)用


Game.js 代碼分析

在Game.js中,開頭就定義了that 

用來保存this 的指向,供后面使用

我們分別實(shí)例化一個(gè) “食物”對(duì)象和一個(gè)“貪吃蛇”對(duì)象

傳入地圖對(duì)象,并賦值

、

屬性設(shè)置好了

那既然是游戲那我們是不是應(yīng)該設(shè)定點(diǎn)游戲規(guī)則,

當(dāng)我們的小蛇到達(dá)地圖邊界時(shí),小蛇就會(huì)一頭撞死了,游戲結(jié)束,

并且我們也沒有實(shí)現(xiàn)小蛇的移動(dòng),讓我們來接著實(shí)現(xiàn)吧

這里我定義了一個(gè)runSnake函數(shù),傳入 food 和map 對(duì)象

首先,定義一個(gè)計(jì)時(shí)器,存入timeId這個(gè)變量中

調(diào)用一個(gè)蛇的 move(移動(dòng)) 和 init (初始化函數(shù))

在小蛇成功移動(dòng)之后,我們?cè)倥袛嘁幌?,小蛇是否已?jīng)走到邊界了,

計(jì)算出,地圖寬度最多能被蛇頭的寬度分為幾份,高度同理

取出蛇頭自身的x和y

判斷 如果蛇頭x<0 說明越過左邊界,超過maxX則說明超過右邊界,

y同理

如果越過邊界,則清除定時(shí)器,執(zhí)行一個(gè)彈框

注意,我在這個(gè)定時(shí)器中的方法后加個(gè)一個(gè)bind 并傳入了開始定義的 that ,也就是提前保存的this 指向,如果不加,這里的代碼多處用到了this ,因?yàn)閟etInterVal 的指向?yàn)閣indow 所以會(huì)導(dǎo)致代碼出現(xiàn)錯(cuò)誤,無法找到這些方法和屬性


接下來我們?cè)賮韺?shí)現(xiàn)一下如何用鍵盤控制小蛇的移動(dòng)

根據(jù)keycode 來更改 snake對(duì)象的 direction ,

同樣的,此處的this 指向也不正確,指向的是 觸發(fā)該事件的對(duì)象,這是無法調(diào)用snake對(duì)象的,所以我們必須改變它,在bind中傳入(that)

然后將Game 對(duì)象暴露給 window


接著定義初始化游戲的函數(shù)

分別調(diào)用food對(duì)象的初始化函數(shù)、小蛇的初始化函數(shù),調(diào)用runSnake函數(shù)開啟定時(shí)器讓小蛇跑起來

最后綁定上keydown 事件


最后的最后

實(shí)例化一個(gè)Game對(duì)象

調(diào)用gm 的init  貪吃蛇小demo就實(shí)現(xiàn)了


效果展示


更多關(guān)于JavaScript相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《JavaScript數(shù)學(xué)運(yùn)算用法總結(jié)》、《JavaScript數(shù)據(jù)結(jié)構(gòu)與算法技巧總結(jié)》、《JavaScript數(shù)組操作技巧總結(jié)》、《JavaScript排序算法總結(jié)》、《JavaScript遍歷算法與技巧總結(jié)》、《JavaScript查找算法技巧總結(jié)》及《JavaScript錯(cuò)誤與調(diào)試技巧總結(jié)

希望本文所述對(duì)大家JavaScript程序設(shè)計(jì)有所幫助。

相關(guān)文章

最新評(píng)論