ホームページ >ウェブフロントエンド >jsチュートリアル >JavaScript でスネーク ウィジェットを実装するサンプル コード

JavaScript でスネーク ウィジェットを実装するサンプル コード

黄舟
黄舟オリジナル
2017-08-20 09:48:232032ブラウズ

1 前に書いたもの

「JavsScript Advanced Programming」がスキルを練習するために小さなデモを作成したかったようで、私は Snake ゲームを選択しました。以前はすべて C# で書いていたため、Snake をクラスに記述し、それを 1 つずつ小さなメソッドに分割し、提供する必要があるメソッドだけを提供しました。このように、Snake はモジュールとして使用でき、どこでも再利用できます。ただし、js で記述する場合、js 言語の機能をモジュール型プログラミングにうまく活用できないため、最初のバージョンの実装では完全にプロセス指向のアプローチが採用され、関数内で必要なすべての変数がグローバル変数として宣言されます。これも関数を実現できますが、再利用できず、多くのトップレベル変数が定義され、グローバル変数が汚染されます。書いた後は、提供する必要がある変数または関数インターフェイスのみを外部に提供できるように、書いたものを再カプセル化したいと常に考えています。いろいろ情報を調べた結果、jsのカプセル化はクロージャを使って実装できることが分かりました。関数内でローカル変数とクロージャ関数をその型のプライベート変数と関数として宣言することにより、オブジェクトにはこれを通じて開発する必要があるインターフェイスが提供されます。 Snakeコンポーネントの使用2.1基本的な例サンプルコード1は次のとおりです:

rreeefirstsnakegame.jsコンポーネントを導入し、senakegameオブジェクトをインスタンス化して送信しますto SnakeGame コンストラクターは 2 つのパラメーターを渡します。最初のパラメータはキャンバスの ID で、2 番目のパラメータはゲーム設定オブジェクトです。これが空の場合は、デフォルトの設定が使用されます。最後に、オブジェクトの startGame() メソッドを呼び出して、スネークのロジックを実装します。デフォルトの方向制御キーは上下左右のキーで、一時停止はスペースです。その効果は次のとおりです:

インスタンス化中に渡される設定オブジェクトを変更することで、ゲームをより詳細に制御できます。

サンプルコード 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",{
 });
 snakeGame.startGame();
</script>
</html>

パラメータの名前でわかり、ヘビの色、食べ物、ゲームの背景、ゲームを制御する方向キーを設定できます。設定方向の順序は [左、下、右、上] です。効果は次のとおりです:

もちろん、さらに多くの構成があります。スコア変更時のコールバック関数、ゲーム終了時のコールバック関数などを定義することもできます。以下に、SnakeGame オブジェクトに共通の構成パラメーターとメソッドを紹介します。

2.2 パブリックメソッド


•startGame(): ゲームを開始します。このメソッド内で、さまざまな設定が初期化されます。たとえば、スコア、スネークボディ、スピードなどをリセットします。

•changeGameStatus(): ゲームのステータスを変更します。つまり、ゲームのステータス変数として SnakeGame オブジェクトにプライベート変数があります。

2.3 ゲームパラメータを設定するためのオブジェクト gameConfigObj 属性、

gameConfigObj オブジェクトには合計 10 個の属性、3 つのコールバック関数が必要ですattributes

•size: ヘビブロックと食べ物のサイズ、デフォルトは 20

•rowCount : row、デフォルト 30 行

•colCount: 列、デフォルト 30 列

•snakeColor: ヘビの体の色、デフォルト グリーン

•foodColor: 食べ物の色、デフォルト イエロー•scenseColor: ゲーム シーンの背景色、デフォルト 黒•directionKey: 方向キー、デフォルト [39、40、37、38] 上下左右

•pasueKey: 一時停止キー、デフォルト 32、スペースキー

•levelCount: 速度レベル制御、デフォルト 10。

•curSpeed: 初期速度、デフォルト200 ミリ秒

コールバック関数


•onCountChange: イベント、すべての食べ物、スコアが変化し、パラメーター (カウント) を使用してこのメ​​ソッドが呼び出されます

•onGamePause: イベント、ゲームの状態が変化すると、このメソッドが呼び出されます。パラメータ 1 は一時停止を表し、0 はゲームが進行中であることを意味します。

•onGameOver: イベント、このメソッドはゲーム終了時に呼び出されます。


2.4高度な使用

上記の属性を使用すると、より対話型のプログラムを設計できます。コードは以下のように表示されます。

例 3

<!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>

上記のコードは、OnChangeCount、onGamePause、onGameOver の 3 つのコールバック関数を設定することで、インターフェイスとコンポーネント間の対話を実現します。効果は次のとおりです。

書籍「Advanced JavaScript Programming」ではモジュール パターンについて説明していますが、このパターンはシングルトン パターンです。つまり、クロージャは最終的にリテラル オブジェクトを返します。ただし、1 つのページで 2 つの Snake ウィンドウを同時に開くことができる必要があります。2 つのゲームは、異なる方向キーとボタン操作を設定することで、2 人で同時にプレイできます。したがって、SnakeGame コンポーネントを実装する際には、Douglas が言及したモジュール モデルは使用されませんでした。 1 ページで 2 人が同時にゲームをプレイできるようにする方法を説明します。コードは次のとおりです:

例 4


まず HTML ファイルを作成します

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>Jaume&#39;s贪吃蛇</title>
 <link rel="stylesheet" href="css/gameStyle.css" rel="external nofollow" >
</head>
<body>
<p id="gamebd">
 <canvas id="gameScense" width="600" height="600">
 </canvas>
 <canvas id="gameScense1" width="600" height="600" style="background-color: black">
 </canvas>
 <p id="gameSet">
  <p id="gameControl" class="gameBoxStyle">
   <h3>游戏控制</h3>
   <p>方向键:上,下,左,右</p>
   <p>开始/暂停:空格</p>
  </p>
  <p id="gameStatus" class="gameBoxStyle">
   <h3>游戏状态</h3>
   <p>当前用户1得分:<span id="txtValue">0</span></p>
   <p>当前用户2得分:<span id="txtValue1">0</span></p>
   <input type="button" value="开始游戏" id="btnStart"/>
  </p>
  <p id="game" class="gameBoxStyle">
   <h3>游戏记录</h3>
   <a href="#" rel="external nofollow" rel="external nofollow" >查看历史记录</a>
  </p>
 </p>
</p>
<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 h3{
 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("游戏结束");
 }
});
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("游戏结束");
 }
});
btnStart.onclick=function(event){
  gameSnake.startGame();
  gameSnake1.startGame();
  btnStart.blur();
}

实例化两个SnakeGame对象,一个对象使用默认的上下左右键和空格键作为方向键和暂停键,而另一个使用了,W、A、S、D 以及 Q 作为方向键和暂停键。效果如下:

嗯哼,没错,完美实现了。使用SnakeGame这个组件,创建贪吃蛇游戏就是如此的简单。下面简单介绍一下,组件的实现方式。

3贪吃蛇组件实现方式

在上一节中就提到过,没有采用过道哥拉斯的设计模式,下面给出贪吃蛇设计结构。具体的源代码,可以在后面的链接中进行下载。代码如下:


/**
 * 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; //带有一个参数
  var onGamePause = gameConfigObj.onGamePause || null; //带有一个参数
  var onGameOver = gameConfigObj.onGameOver || null;
  //判断
  if(gameScense.width != size*rowCount || gameScense.height != size*colCount){
   throw "场景大小不等于行列大小*蛇块大小";
  }
  //特权方法
  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(){
   ···
  }
  /*产生食物*/
  function genFood(){
   ···
  }
  /*吃食物*/
  function eatFood(snakeHead){
   ···
  }
  /*判断游戏是否结束*/
  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; //最后返回一个组件构造函数
}();

上面有一个很重要的地方,就是键盘注册的代码,单独列出来分析一下。


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

该段代码的逻辑是,首先判断在 document 上是否注册了onkeydown 事件,如果注册了该事件,则保存所引用的事件处理程序,然后重置onkeydown事件程序,然后在新的事件处理程序中,调用先前的事件处理程序,这样就实现了事件触发后,调用所有监听该事件处理程序,而不是直接覆盖。

另外关于贪吃蛇的设计逻辑,可以参看我另外一篇文章,个人觉得讲的非常详细了,文章:基于控制台实现贪吃蛇游戏

3 小结

通过这次贪吃蛇组件的设计,对 js 的模块化设计稍微了解了一下,但是,我也不知道上文所实现的贪吃蛇模块有哪些缺陷,希望有大神看到这篇文章,能给一些指导。当然了,该组件还可以进行进一步的扩展,比如将游戏的方块,替换成图片。

以上がJavaScript でスネーク ウィジェットを実装するサンプル コードの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。