ホームページ >ウェブフロントエンド >jsチュートリアル >JavaScript でスネーク ウィジェットを実装するサンプル コード
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 パブリックメソッド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'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 サイトの他の関連記事を参照してください。