溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

JavaScript貪吃蛇小組件實例代碼

發(fā)布時間:2020-10-11 22:14:15 來源:腳本之家 閱讀:153 作者:msay 欄目:web開發(fā)

1 寫在前面

看來《JavsScript高級編程》,想做一個小demo練練自己的手,選擇了貪吃蛇游戲。由于以前都是用c#寫的,將貪吃蛇寫到一個類里面,然后一個一個小方法的拆分,只向外提供需要提供的方法。這樣就可以將貪吃蛇作為一個模塊,任何地方都可以復(fù)用的。然而,用js進(jìn)行編寫的時候,由于不能很好的利用js語言的特性進(jìn)行模塊化編程,所以第一版的實現(xiàn)完全采用面向過程的方式,將函數(shù)中所需要的變量全部聲明為全局變量。雖然這樣也能夠?qū)崿F(xiàn)功能,但是做不到復(fù)用,而且定義非常多的最頂層變量,污染了全局變量。寫完之后,總想將自己寫的重新封裝一次,達(dá)到只向外提供必須要提供的變量、或者功能函數(shù)接口。查了一些許多資料,對于js的封裝可以采用閉包的方式來進(jìn)行實現(xiàn)。通過在函數(shù)內(nèi)部聲明局部變量和閉包函數(shù)來當(dāng)做類型的私有變量和函數(shù),然后通過this給對象向外提供需要開發(fā)的接口。

2 貪吃蛇組件的使用

2.1 初級示例

示例代碼1如下:

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>貪吃蛇組件</title>
</head>
<body>
 <canvas width="600" height="600" id="gameScense"></canvas>
</body>
<script src="SnakeGame.js"></script>
<script>
 var snakeGame = new SnakeGame("gameScense",{
 });
 snakeGame.startGame();
</script>
</html>

首先引入SnakeGame.js組件,然后通過實例化 SnakeGame對象,并向SnakeGame構(gòu)造函數(shù)傳入兩個參數(shù)。第一參數(shù)是canvas的id,第二個參數(shù)游戲配置的對象,如果為空的話,那么采用默認(rèn)的配置。最后,調(diào)用對象的startGame()方法,即可實現(xiàn)貪吃蛇的邏輯。默認(rèn)的方向控制鍵為上下左右按鍵、暫停為空格,效果如下:

JavaScript貪吃蛇小組件實例代碼

我們可以通過更改實例化時傳入的配置對象來實現(xiàn)對游戲的更多控制。

 示例代碼2:

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>貪吃蛇組件</title>
</head>
<body>
 <canvas width="600" height="600" id="gameScense"></canvas>
</body>
<script src="SnakeGame.js"></script>
<script>
 var snakeGame = new SnakeGame("gameScense",{
  snakeColor:"red",
  foodColor:"green",
  scenseColor:"blue",
  directionKey:[68,83,65,87],
 });
 snakeGame.startGame();
</script>
</html>

通過參數(shù)的名字可以知道,配置蛇、食物、游戲背景的顏色,以及控制游戲的方向鍵。配置方向順序為【左,下,右,上】。效果如下:

JavaScript貪吃蛇小組件實例代碼

當(dāng)然還有更加多的配置。還能夠定義分?jǐn)?shù)改變的回調(diào)的函數(shù),以及游戲結(jié)束時的回調(diào)函數(shù)等。下面介紹一下配置參數(shù),以及SnakeGame對象共有的方法。

2.2 公有方法

•startGame() : 開始游戲。在該方法內(nèi),會初始化各種設(shè)置。如,重置分?jǐn)?shù),蛇身,速度等。

•changeGameStatus():改變游戲狀態(tài),即暫停和開始,SnakeGame對象里面有一個私有變量,作為游戲的狀態(tài)變量。

2.3 配置游戲參數(shù)的對象gameConfigObj屬性、

gameConfigObj 對象一共該有10個屬性,3個回調(diào)函數(shù)

屬性

•size : 蛇塊和食物的大小,默認(rèn)20
•rowCount : 行,默認(rèn)30行
•colCount : 列,默認(rèn)30列
•snakeColor : 蛇身顏色,默認(rèn)green
•foodColor : 食物顏色,默認(rèn)yellow
•scenseColor : 游戲場景背景色, 默black
•directionKey : 方向鍵, 默認(rèn)[39, 40, 37, 38] 上下左右
•pasueKey : 暫停鍵, 默認(rèn)32,空格鍵
•levelCount : 速度等級控制,默認(rèn)10.
•curSpeed : 初始速度,默認(rèn)200毫秒

回調(diào)函數(shù)

•onCountChange : 事件,每一個食物,分?jǐn)?shù)改變,并調(diào)用該方法,帶有一個參數(shù)(count)

•onGamePause : 事件,游戲狀態(tài)改變時,調(diào)用該方法,帶有一個參數(shù) 1,代表暫停,0 ,代表游戲在進(jìn)行。

•onGameOver : 事件,游戲結(jié)束時,調(diào)用該方法。

2.4使用進(jìn)階

通過上面的屬性我們可以設(shè)計一個交互性更加強的程序。代碼如下。

 示例3

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>貪吃蛇組件</title>
 <style type="text/css">
  *{
   margin:0px;
   padding:0px;
  }
  #gamebd{
   width:850px;
   margin:50px auto;
  }
  #gameScense{
   background-color:green;
   float:left;
  }
  #gameSet{
   margin-left:10px;
   float:left;
  }
  .gameBoxStyle{
   margin-bottom:7px;
   padding:5px 10px;
  }
  .gameBoxStyle h4{
   margin-bottom:7px;
  }
  .gameBoxStyle p{
   line-height: 1.7em;
  }
  .gameBoxStyle input{
   margin-top:7px;
   background-color: white;
   border:1px gray solid;
   padding:3px 9px;
   margin-right:9px;
  }
  .gameBoxStyle input[type=text]{
   width:90px;
  }
  .gameBoxStyle input:hover{
   background-color: #e2fff2;
  }
  .gameBoxStyle #txtValue{
   color:red;
  }
 </style>
</head>
<body>
<div id="gamebd">
 <canvas id="gameScense" width="600" height="600">
 </canvas>
 <div id="gameSet">
  <div id="gameControl" class="gameBoxStyle">
   <h4>游戲控制</h4>
   <p>方向鍵:上,下,左,右</p>
   <p>開始/暫停:空格</p>
  </div>
  <div id="gameStatus" class="gameBoxStyle">
   <h4>游戲狀態(tài)</h4>
   <p>用戶名:<input type="text" placeholder="輸入用戶名:" id="txtUserName" value="游客123"/> </p>
   <p>當(dāng)前用戶1得分:<span id="txtValue">0</span></p>
   <input type="button" value="開始游戲" id="btnStart"/>
   <input type="button" value="暫停" id="btnPause"/>
  </div>
  <div id="game" class="gameBoxStyle">
   <h4>游戲記錄</h4>
   <a href="#" rel="external nofollow" rel="external nofollow" >查看歷史記錄</a>
  </div>
 </div>
</div>
<script src="js/SnakeGame.js"></script>
</body>
<script src="SnakeGame.js"></script>
<script>
 var btnStart=document.getElementById("btnStart");
 var btnPasue=document.getElementById("btnPause");
 var gameSnake = new SnakeGame("gameScense",{
  snakeColor:"red",
  onCountChange:function(count){
   var txtScore=document.getElementById("txtValue");
   txtScore.innerText=count.toString( );
   txtScore=null;
  },
  onGamePause:function(status){
   if(status){
    btnPasue.value = "開始";
   }else {
    btnPasue.value = "暫停"
   }
  },
  onGameOver:function (status) {
   alert("游戲結(jié)束");
  }
 });
 btnStart.onclick=function(event){
  if(checkUserName()){
   gameSnake.startGame();
   btnStart.blur();
  }
 }
 btnPasue.onclick=function(event) {
  gameSnake.changeGameStatus();
  btnStart.blur();
 }
 function checkUserName(){
  var txtUserName = document.getElementById("txtUserName");
  if(txtUserName.value.length==0){
   alert("用戶名不能為空");
   return false;
  }else {
   return true;
  }
 }
</script>
</html>

上面的代碼通過設(shè)置OnChangeCount、onGamePause、onGameOver,三個回調(diào)函數(shù),實現(xiàn)界面與組件的交互。效果如下:

JavaScript貪吃蛇小組件實例代碼

在《JavaScript高級編程》這本書中說道一個模塊模式,但是這種模式是單例模式,也就是閉包最后返回一個字面量的對象。但是我需要在一個頁面中能夠同時開啟兩個貪吃蛇的窗口,兩個游戲通過設(shè)置配置不同的方向鍵和按鈕操作,實現(xiàn)兩個人同時一起玩。所以,在實現(xiàn)SnakeGame組件時,沒有采用道格拉斯所說的模塊模式。下面演示一下,如何在一個頁面中,讓兩個人同時一起玩游戲。代碼如下: 

示例4

首先建立一個html文件

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>Jaume's貪吃蛇</title>
 <link rel="stylesheet" href="css/gameStyle.css" rel="external nofollow" >
</head>
<body>
<div id="gamebd">
 <canvas id="gameScense" width="600" height="600">
 </canvas>
 <canvas id="gameScense1" width="600" height="600" >
 </canvas>
 <div id="gameSet">
  <div id="gameControl" class="gameBoxStyle">
   <h4>游戲控制</h4>
   <p>方向鍵:上,下,左,右</p>
   <p>開始/暫停:空格</p>
  </div>
  <div id="gameStatus" class="gameBoxStyle">
   <h4>游戲狀態(tài)</h4>
   <p>當(dāng)前用戶1得分:<span id="txtValue">0</span></p>
   <p>當(dāng)前用戶2得分:<span id="txtValue1">0</span></p>
   <input type="button" value="開始游戲" id="btnStart"/>
  </div>
  <div id="game" class="gameBoxStyle">
   <h4>游戲記錄</h4>
   <a href="#" rel="external nofollow" rel="external nofollow" >查看歷史記錄</a>
  </div>
 </div>
</div>
<script src="js/SnakeGame.js"></script>
<script src="js/UIScript.js"></script>
</body>
</html>

樣式文件如下:

*{
 margin:0px;
 padding:0px;
}
#gamebd{
 /*width:850px;*/
 /*margin:50px auto;*/
 width:100%;
}
#gameScense{
 background-color:green;
 float:left;
}
#gameSet{
 margin-left:10px;
 float:left;
}
.gameBoxStyle{
 margin-bottom:7px;
 padding:5px 10px;
}
.gameBoxStyle h4{
 margin-bottom:7px;
}
.gameBoxStyle p{
 line-height: 1.7em;
}
.gameBoxStyle input{
 margin-top:7px;
 background-color: white;
 border:1px gray solid;
 padding:3px 9px;
 margin-right:9px;
}
.gameBoxStyle input[type=text]{
 width:90px;
}
.gameBoxStyle input:hover{
 background-color: #e2fff2;
}
.gameBoxStyle #txtValue{
 color:red;
}

在html中拖入了兩個文件,一個是貪吃蛇組件,另一個是UIScript.js,其中的代碼如下:

/**
 * Created by tjm on 8/16/2017.
 */
var btnStart=document.getElementById("btnStart");
var gameSnake = new SnakeGame("gameScense",{
 snakeColor:"red",
 directionKey:[68,83,65,87],
 pauseKey:81,
 onCountChange:function(count){
  var txtScore=document.getElementById("txtValue");
  txtScore.innerText=count.toString( );
  txtScore=null;
 },
 onGameOver:function (status) {
  alert("游戲結(jié)束");
 }
});
var gameSnake1 = new SnakeGame("gameScense1",{
 snakeColor:"green",
 size:20,
 onCountChange:function(count){
  var txtScore=document.getElementById("txtValue1");
  txtScore.innerText=count.toString();
  txtScore=null;
 },
 onGameOver:function (status) {
  alert("游戲結(jié)束");
 }
});
btnStart.onclick=function(event){
  gameSnake.startGame();
  gameSnake1.startGame();
  btnStart.blur();
}

實例化兩個SnakeGame對象,一個對象使用默認(rèn)的上下左右鍵和空格鍵作為方向鍵和暫停鍵,而另一個使用了,W、A、S、D 以及 Q 作為方向鍵和暫停鍵。效果如下:

JavaScript貪吃蛇小組件實例代碼

嗯哼,沒錯,完美實現(xiàn)了。使用SnakeGame這個組件,創(chuàng)建貪吃蛇游戲就是如此的簡單。下面簡單介紹一下,組件的實現(xiàn)方式。

3貪吃蛇組件實現(xiàn)方式

在上一節(jié)中就提到過,沒有采用過道哥拉斯的設(shè)計模式,下面給出貪吃蛇設(shè)計結(jié)構(gòu)。具體的源代碼,可以在后面的鏈接中進(jìn)行下載。代碼如下:

/**
 * Created by tjm on 8/18/2017.
 */
var SnakeGame = function () {
 /*蛇塊和食物組件類*/
 function SnakeBlock(row,col){
  this.row=row;
  this.col=col;
 }
 SnakeBlock.prototype.draw = function(graphic,color,size){
  graphic.fillStyle=color;
  graphic.fillRect(size*this.col,size*this.row,size-2,size-2);
 }
 SnakeBlock.prototype.clearDraw = function(graphic,color,size){
  graphic.fillStyle=color;
  graphic.fillRect(size*this.col,size*this.row,size,size);
 }
 SnakeBlock.prototype.equal = function(snakeBlock){
  if(snakeBlock.row==this.row && snakeBlock.col==this.col){
   return true;
  }else{
   return false;
  }
 }
 /*貪吃蛇組件類*/
 function SnakeGame(gameScenseId, gameConfigObj) {
  // 私有屬性
  var gameScense = document.getElementById(gameScenseId);
  var graphic = gameScense.getContext("2d");
  var count = 0;
  var snake;
  var curFood;
  var runId;
  var isMoved = false;//方向改變后,如果沒有移動則方向鍵暫時失效。
  var gameStatus = false;
  var curDirection = 1;
  var size = gameConfigObj.size || 20;
  var rowCount = gameConfigObj.rowCount || 30;
  var colCount = gameConfigObj.colCount || 30;
  var snakeColor = gameConfigObj.snakeColor || "green";
  var foodColor = gameConfigObj.foodColor || "yellow";
  var scenseColor = gameConfigObj.scenseColor || "black";
  var directionKey = gameConfigObj.directionKey || [39, 40, 37, 38];
  var pauseKey = gameConfigObj.pauseKey || 32;
  var levelCount = gameConfigObj.levelCount || 10;
  var curSpeed = gameConfigObj.curSpeed || 200;
  //公開事件
  var onCountChange = gameConfigObj.onCountChange || null; //帶有一個參數(shù)
  var onGamePause = gameConfigObj.onGamePause || null; //帶有一個參數(shù)
  var onGameOver = gameConfigObj.onGameOver || null;
  //判斷
  if(gameScense.width != size*rowCount || gameScense.height != size*colCount){
   throw "場景大小不等于行列大小*蛇塊大小";
  }
  //特權(quán)方法
  this.startGame = startGame;
  this.changeGameStatus = changeGameStatus;
  //注冊 dom 鍵盤事件
  var preFunc = document.onkeydown;
  document.onkeydown = function (e) {
   var key = (e || event).keyCode;
   handleKeyInput(key);
   if (typeof preFunc == "function") {
    preFunc(e);
   }
  }
  //私有方法
  /*初始化蛇身*/
  function initSnake(){
   ···
  }
  /*繪制場景背景色*/
  function initScense(){
   ···
  }
  /*產(chǎn)生食物*/
  function genFood(){
   ···
  }
  /*吃食物*/
  function eatFood(snakeHead){
   ···
  }
  /*判斷游戲是否結(jié)束*/
  function gameOver(){
   ···
  }
  /*蛇移動*/
  function snakeMove(){
   ···
  }
  function changeSpeed(){
   ···
  }
  function handleKeyInput(key){
   ···
  }
  function initGame(){
   ···
  }
  function triggerEvent(callback,argument){
   ···
  }
  function runGame(){
   ···
  }
  function pauseGame() {
   ···
  }
  function changeGameStatus(){
   ···
  }
  function startGame(){
   ···
  }
 }
 return SnakeGame; //最后返回一個組件構(gòu)造函數(shù)
}();

上面有一個很重要的地方,就是鍵盤注冊的代碼,單獨列出來分析一下。

var preFunc = document.onkeydown;
  document.onkeydown = function (e) {
   var key = (e || event).keyCode;
   handleKeyInput(key);
   if (typeof preFunc == "function") {
    preFunc(e);
   }
  }

該段代碼的邏輯是,首先判斷在 document 上是否注冊了onkeydown 事件,如果注冊了該事件,則保存所引用的事件處理程序,然后重置onkeydown事件程序,然后在新的事件處理程序中,調(diào)用先前的事件處理程序,這樣就實現(xiàn)了事件觸發(fā)后,調(diào)用所有監(jiān)聽該事件處理程序,而不是直接覆蓋。

另外關(guān)于貪吃蛇的設(shè)計邏輯,可以參看我另外一篇文章,個人覺得講的非常詳細(xì)了,文章:基于控制臺實現(xiàn)貪吃蛇游戲

3 小結(jié)

通過這次貪吃蛇組件的設(shè)計,對 js 的模塊化設(shè)計稍微了解了一下,但是,我也不知道上文所實現(xiàn)的貪吃蛇模塊有哪些缺陷,希望有大神看到這篇文章,能給一些指導(dǎo)。當(dāng)然了,該組件還可以進(jìn)行進(jìn)一步的擴展,比如將游戲的方塊,替換成圖片。有興趣的可以從下面的鏈接進(jìn)行下載,更改后別忘了分享哦。

源碼下載鏈接:https://github.com/StartAction/SnakeGame

以上所述是小編給大家介紹的JavaScript貪吃蛇小組件實例代碼,希望對大家有所幫助,如果大家有任何疑問歡迎給我留言,小編會及時回復(fù)大家的!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI