JavaScript + Canvasを使用してSnakeゲームを作成する

こんにちは、友達。 次に、ゲームSnakeを作成する方法を紹介します。 もちろん、コードの行数の点で最速の方法でも最小の方法でもありませんが、私の意見では、私のような初心者の開発者にとって最も理解しやすいものです。 この記事は、canvas要素と2Dグラフィックスを操作するための簡単な方法に少し精通したい人向けに書かれています。

画像

ヘビを「古い」形式で、特に美しいグラフィックなしで、立方体の形で書きます。 しかし、これは開発の理解を単純化するだけです。 さあ、行こう!



準備する



おそらく、ゲームの作成とコードの作成の準備から始める価値があります。 シンプルなSublime Textエディターを使用します。 ただし、これは重要ではありません。 1つのドキュメントですべてを行い、高速化します。



まず、ドキュメントにキャンバスを埋め込むためのコード自体を記述します。 canvasはHTML5でのみサポートされていることを思い出してください。



<!--      ,     . --> <canvas id="gP">HTML5  </canvas>
      
      





準備が完了しました。ゲーム自体の作成を開始できます。



はじめに



まず、ヘビがどのように機能するかを説明したいと思います。それはより明確になります。 私たちの蛇はアレイです。 要素の配列。要素は、分割される部分です。 これらは、X座標とY座標を持つ単なる正方形で、ご存知のように、Xは水平、Yは垂直です。 通常の形式では、次のような座標平面を想像します。



画像



間違いなく間違いありませんが、コンピューターモニター(特にキャンバス)では、次のように表示が異なります。



画像



初めてキャンバスに直面した場合、これを知る必要があります。 私がこれに出くわしたとき、私はすぐにそれを理解したため、最初はポイントが(0,0)である場所を理解していませんでした。 問題ないことを願っています。



ヘビの要素、その部分に戻りましょう。 各要素には独自の座標がありますが、高さと幅は同じであると想像してください。 これらは小さな正方形です。 今、私たちがヘビを描いたと想像してください。すべての正方形は、同じものを互いに追いかけます。 そして、ヘビを右に動かす必要があると判断しました。 どうしますか?



一部の人々は、最初の要素を右に移動し、次に2番目に、次に3番目に移動する必要があると答えます。 しかし、私にとってこのオプションは正しくありません。なぜなら、ヘビが巨大でコンピューターが弱い場合、ヘビが時々壊れることに気付くことができるからです。 とにかく、品質を損なうことなくはるかに少ない量で実行できる場合、この方法には多くのチームが必要です。 そして今、私のやり方:ヘビの最後の要素を取得し、それを先頭に置き、頭の後ろになるように座標を変更します。 今、この要素は頭です。 ただ何か! そして、はい、動きの効果は存在しますが、尾部をどのように隠して最初に置くかはコンピューター上で目立ちません。 これはまさに蛇の動きを作成するときに行うことです。



ここから始まります



最初に、将来使用するいくつかの変数を宣言する必要があります。



 //  . function rand (min, max) {k = Math.floor(Math.random() * (max - min) + min); return (Math.round( k / s) * s);} //    . function newA () {a = [rand(0, innerWidth),rand(0, innerHeight)];} //       . function newB () {sBody = [{x: 0,y: 0}];} var gP = document.getElementById('gP'), // canvas. // "" (    canvas). g = gP.getContext('2d'), sBody = null, // ,    . d = 1, //  1 - d, 2 -  3 - , 4 - . a = null, //, , 0  - x, 1  - y. s = 30; newB(); newA(); // .
      
      





したがって、一般的に、乱数を返す関数で変数sを乗算およ​​び除算する必要がある理由を少し説明する必要があります。この関数は、幅、並行、およびスネーク要素の高さのみを格納します。 実際、これは、移動中に変位がないようにするために必要です。30の要素幅があるため、不連続なく移動したい場合、すべての座標を余りなく30で割る必要があります。 それが、私が数字で割り、丸め、そして掛ける理由です。 したがって、数値は、余りなしで30で除算できるように返されます。



反対に、キャンバスの幅と高さを30の倍数にすることができると言うことができます。しかし、実際にはこれは最良の選択肢ではありません。 私は個人的に画面の幅全体を使用していたので。 また、幅= 320の場合、ユーザーから最大20ピクセルを取得する必要があり、不快感を引き起こす可能性があります。 そのため、ヘビではオブジェクトのすべての座標が30で除算されるため、予期しない瞬間はありません。 これは、コードでよく使用されるため、別の関数として作成するのがさらに正しいでしょう。 しかし、私はこの結論に遅れて来ました。 (しかし、おそらくそれさえ必要ではないでしょう)。



次に、画像の表示品質を明確にします。 記事の途中で、コードを追加します。いくつかの場所で最初に戻りますので、コードの全体像を把握してください。



 gP.width = innerWidth; //  ,    . gP.height = innerHeight; //  ,    .
      
      





どちらかといえば、変数innerWidthとinnerHeightはグローバル名前空間に保存されます。

したがって、その方法でアクセスできます。 確かに、これを正しく行うかどうかはわかりません。



さて、今私たちはヘビのコードを書き始めています。



移動にはアニメーションが必要です。setInterval関数を使用します。2番目のパラメーターは60です。もう少し可能性があります(たとえば75)が、私は60が好きです。この関数は60ミリ秒ごとのみです。 「再び」蛇を描きます。 さらにコードを書くのはこの間隔だけです。



一般的に、これまで移動せずに、蛇の単純なレンダリングを表示します。



 setInterval(function(){ g.clearRect(0,0,gP.width,gP.height); // . g.fillStyle = "red"; //     . g.fillRect(...a, s, s); //    30x30   a[0]  a[1]. g.fillStyle = "#000"; //     . }, 60);
      
      





ヘビがそれ自体と衝突しないことを検証するには、最後の要素を除く各要素に対して検証を行う必要があります。 ヘビの最後の要素(頭)の座標が次のいずれかに等しいかどうかを確認します...つまり、より簡単に言えば、衝突が発生したかどうかです。 このコード行は1行でしたが、明確にしました。 これらはすべてインターバル機能に追加されることを思い出してください。



 sBody.forEach(function(el, i){ //  ,      ,     . if (a[0] + s >= gP.width || a[1] + s >= gP.height) newA(); //  . var last = sBody.length - 1; if ( el.x == sBody[last].x && el.y == sBody[last].y && i < last) { sBody.splice(0,last); //  . sBody = [{x:0,y:0}]; //  . d = 1; //    . } }); //+ //     . var m = sBody[0], f = {x: mx,y: my}, l = sBody[sBody.length - 1]; /*       . ,      ,     .       ( -  1, - ),     . ,   . */ //  ,    Y,   X  + s. if (d == 1) fx = lx + s, fy = Math.round(ly / s) * s; //   ,   X,   Y  + s. if (d == 2) fy = ly + s, fx = Math.round(lx / s) * s; //  ,   Y,   X  -s. if (d == 3) fx = lx - s, fy = Math.round(ly / s) * s; //  ,   X,   Y  -s. if (d == 4) fy = ly - s, fx = Math.round(lx / s) * s;
      
      





そして今、あなたはおそらく、座標を変更している間、常に何かを「保存」し、最初に分割し、次に数値sで丸めて乗算することに気づいたでしょう。 これは、ヘビとリンゴを揃えるのと同じ方法です。 この場合の動きは厳密で単純であるため、リンゴは間隔の最初に設定された特定のルールに従って厳密にヘビを食べることができます。 そして、ヘビの頭の座標が少なくとも1ピクセルずれていると、リンゴは食べられませんでした。 そして、はい、これは単純なオプションなので、すべてが非常に限られています。



さて、私たちは何をしなければなりませんか? そうです、配列から尾(最初の要素)を削除し、最後に新しい要素を追加して、蛇全体を描画します。 これを行うには、間隔の最後にこれらのコード行を追加します。



 sBody.push(f); //      . sBody.splice(0,1); // . //   . sBody.forEach(function(pob, i){ //   ,      X ,   ,     if (d == 1) if (pob.x > Math.round(gP.width / s) * s) pob.x = 0; //   ,      X ,   ,    . if (d == 2) if (pob.y > Math.round(gP.height / s) * s) pob.y = 0; //   ,    X  ,         ( ). if (d == 3) if (pob.x < 0) pob.x = Math.round(gP.width / s) * s; //   ,    Y  ,         ( ). if (d == 4) if (pob.y < 0) pob.y = Math.round(gP.height / s) * s; //     ,    . if (pob.x == a[0] && pob.y == a[1]) newA(), sBody.unshift({x: fx - s, y:ly}) //    . g.fillRect(pob.x, pob.y, s, s); });
      
      





ヘビのレンダリングに加えて、画面の終わりがその始まりであるように感じさせるコードを追加しました。 そして、ヘビが国境を越えて行くと、それから死を始めます。

すべてが非常に困難な場合、たとえばゲームをドロップすることで、座標のゼロ化を置き換えることができます。 しかし、私はそれがより好きです。 さて、今は、ボタンを押して蛇の方向を変えるだけです。 数秒で作ります。 setIntervalの直後にこのコードを記述するだけです。 このようなもの:



 //setInerval(...); onkeydown = function (e) { var k = e.keyCode; if ([38,39,40,37].indexOf(k) >= 0) e.preventDefault(); if (k == 39 && d != 3) d = 1; // if (k == 40 && d != 4) d = 2; // if (k == 37 && d != 1) d = 3; // if (k == 38 && d != 2) d = 4; // };
      
      





ここでは、ヘビが右に動いた場合、左に方向を変えられないようにしています。 実際、これは論理的です。



それだけです、友達。 初心者のために初心者が書いた私の最初の記事。 すべてが明確であり、誰かにとって有用であったことを願っています。 ヘビは、たとえば、ポイントカウンター、レコード、追加のチップを追加することで改善できますが、これらはすべて自分で作成できる追加です。 それだけです、皆さんに幸運を!



デモを見る(CodePen):



ヘビゲーム。



All Articles