キャンバスずスプラむトを䜿甚しおゲヌムを䜜成する

Webは珟圚どこにでもあり、アプリケヌションを䜜成および配垃するための非垞に匷力な環境を提䟛したす。 ルヌプの代わりにコヌドの䜜成→コンパむル→開始、アプリケヌションを曎新するか、ブラりザでコヌドを「ラむブ」で䜜成するだけです。 さらに、膚倧な数のプラットフォヌムでアプリケヌションを配垃するのは比范的簡単です。 興味深いこずに、過去数幎間で、HTML5を䜿甚したゲヌム開発が珟実のものになりたした。

canvas芁玠はHTML5で導入され、それを操䜜するためのAPIを提䟛したす。 APIはシンプルですが、グラフィックスを䞀床も䜿甚したこずがない堎合は、慣れるたで時間がかかりたす。 Canvasは倚数のブラりザでサポヌトされおいるため、Webはゲヌムを䜜成するための優れたプラットフォヌムになりたす。

キャンバスの䜿甚は簡単です。タグを䜜成し、JavaScriptで衚瀺コンテキストを䜜成し、このコンテキストでfillRectやdrawImageなどのメ゜ッドを䜿甚しおフォヌムや画像を衚瀺したす。 APIには、さたざたなパスの䜜成、画像倉換などのための倚くのメ゜ッドが含たれおいたす。

この蚘事では、キャンバスを䜿甚しおゲヌムを䜜成したす。 スプラむト、衝突の远跡、そしおもちろん爆発を䌎う実際のゲヌム。 爆発のないなんおゲヌム

そしお、これが私たちが䜜成しようずしおいるゲヌムです。



準備する



ゲヌムは耇雑に芋えるかもしれたせんが、実際にはいく぀かのコンポヌネントを䜿甚するこずになりたす。 キャンバス、耇数のスプラむト、コリゞョントラッキング、ゲヌムルヌプをどれだけ䜿甚できるか、い぀も驚きたした。

ゲヌムのコンポヌネントに完党に焊点を圓おるために、コヌドずAPIのすべおの行を噛みたせん。 この蚘事は䞊玚レベルで曞かれおいたすが、すべおのレベルの人々が理解できるこずを願っおいたす。 この蚘事は、読者がすでにJavaScriptずHTMLの基本に粟通しおいるこずを前提ずしおいたす。 たた、キャンバスAPIず、ゲヌムルヌプなどの基本的なゲヌムの原則に぀いおも觊れたす。



キャンバスを䜜成



コヌドの孊習を始めたしょう。 ゲヌムのほずんどはapp.jsにありたす。

最初に行うこずは、タグを䜜成し、その幅ず高さを蚭定するこずです。 すべおをJSに保持するためにこれを動的に行いたすが、HTMLドキュメントでキャンバスを䜜成し、 getElementByIdを䜿甚しお取埗できたす。 2぀の間に違いはありたせん、それは単に奜みの問題です。

// Create the canvas var canvas = document.createElement("canvas"); var ctx = canvas.getContext("2d"); canvas.width = 512; canvas.height = 480; document.body.appendChild(canvas);
      
      





Canvasには、衚瀺コンテキストを取埗するために䜿甚されるgetContextメ゜ッドがありたす。 コンテキストは、キャンバスAPIず察話するメ゜ッドを呌び出すこずによりオブゞェクトです。 3DシヌンにWebGLを䜿甚する堎合は、パラメヌタヌ 'webgl'を枡すこずもできたす。

次に、 ctx倉数を䜿甚しおすべおの芁玠を衚瀺したす。



ゲヌムサむクル



ゲヌムを垞に曎新しお衚瀺するゲヌムサむクルが必芁です。 これは次のようなものです。

 // The main game loop var lastTime; function main() { var now = Date.now(); var dt = (now - lastTime) / 1000.0; update(dt); render(); lastTime = now; requestAnimFrame(main); };
      
      





シヌンを曎新しお衚瀺し、 requestAnimationFrameを䜿甚しお次のルヌプをキュヌに入れたす。 setTimeoutメむン、1000/60を䜿甚しお、60フレヌム/秒を衚瀺しようずするず、本圓に簡単になりたす。 app.jsの最䞊郚でrequestAnimationFrameのラッパヌを䜜成したした。すべおのブラりザヌがこのメ゜ッドをサポヌトしおいるわけではないためです。

SetTimeoutメむン、1000/60は䜿甚されたせん。これは、粟床が䜎く、䞍芁な堎合に衚瀺に倚くのサむクルを費やすためです。

曎新関数のdtのパラメヌタヌは、珟圚の時刻ず最埌の曎新の時刻の差です。 フレヌムに䞀定の倀を䜿甚しおシヌンをリフレッシュしないでくださいスピリットでは、x + = 5。 ゲヌムは異なるコンピュヌタヌ/プラットフォヌムで異なる動䜜をするため、フレヌムレヌトに関係なくシヌンを曎新する必芁がありたす。

これは、最埌の曎新からの時間を蚈算し、すべおの動きを1秒あたりのピクセルで衚珟するこずにより実珟されたす。 そしお、動きは次のx + = 50 * dt、぀たり50ピクセル/秒になりたす。



リ゜ヌスの読み蟌みずゲヌムの起動



コヌドの次の郚分では、ゲヌムを初期化し、必芁なすべおのリ゜ヌスをロヌドしたす。 これを行うには、個別に蚘述されたヘルパヌクラスの1぀であるresources.jsを䜿甚したす。 これは、すべおの画像をダりンロヌドし、すべおの画像が読み蟌たれるずむベントを発生させる非垞にシンプルなラむブラリです。

ゲヌムには、画像、シヌンデヌタなどのリ゜ヌスが含たれおいたす。 2Dゲヌムの堎合、䞻なリ゜ヌスは画像です。 すぐに䜿甚できるようにするには、アプリケヌションを起動する前にすべおのリ゜ヌスをダりンロヌドする必芁がありたす。

JavaScriptを䜿甚するず、画像を簡単にロヌドしお、必芁なずきに䜿甚できたす。

 var img = new Image(); img.onload = function() { startGame(); }; img.src = url;
      
      





もちろん、たくさんの画像がない限り、これは非垞に面倒です。 たくさんのグロヌバル倉数を䜜成し、ロヌドするかどうかをチェックする必芁がありたす。 これをすべお自動的に行うための基本的なリ゜ヌスロヌダヌを䜜成したした。

 (function() { var resourceCache = {}; var loading = []; var readyCallbacks = []; // Load an image url or an array of image urls function load(urlOrArr) { if(urlOrArr instanceof Array) { urlOrArr.forEach(function(url) { _load(url); }); } else { _load(urlOrArr); } } function _load(url) { if(resourceCache[url]) { return resourceCache[url]; } else { var img = new Image(); img.onload = function() { resourceCache[url] = img; if(isReady()) { readyCallbacks.forEach(function(func) { func(); }); } }; resourceCache[url] = false; img.src = url; } } function get(url) { return resourceCache[url]; } function isReady() { var ready = true; for(var k in resourceCache) { if(resourceCache.hasOwnProperty(k) && !resourceCache[k]) { ready = false; } } return ready; } function onReady(func) { readyCallbacks.push(func); } window.resources = { load: load, get: get, onReady: onReady, isReady: isReady }; })();
      
      





仕組みすべおのむメヌゞを読み蟌んでresources.loadを呌び出し、次にresources.onReadyを呌び出しお、すべおのデヌタのloadむベントでコヌルバックを䜜成したす。 resources.loadはゲヌムの埌半で䜿甚されず、起動時にのみ䜿甚されたす

アップロヌドされた画像はresourcesCacheにキャッシュされ、すべおの画像がアップロヌドされるず、すべおのコヌルバックが呌び出されたす。 これを行うこずができたす

 resources.load([ 'img/sprites.png', 'img/terrain.png' ]); resources.onReady(init);
      
      





Resources.get 'img / sprites.pngを䜿甚しお画像を取埗したす。 簡単

すべおの画像を手動でダりンロヌドしおゲヌムを実行するか、プロセスを簡玠化するためにresources.jsの粟神で䜕かを䜿甚できたす。

䞊蚘のコヌドでは、すべおの画像が読み蟌たれるずinitが呌び出されたす。 Initは背景画像を䜜成し、「Play again」ボタンにむベントを掛け、ゲヌムをリセットしお開始し、ゲヌムを開始したす。

 function init() { terrainPattern = ctx.createPattern(resources.get('img/terrain.png'), 'repeat'); document.getElementById('play-again').addEventListener('click', function() { reset(); }); reset(); lastTime = Date.now(); main(); }
      
      







ゲヌムの状態



さあ始めたしょう ゲヌムロゞックを始めたしょう。 すべおのゲヌムの䞭栞は「ゲヌムの状態」です。 これらは、ゲヌムの珟圚の状態を衚すデヌタです。マップ䞊のオブゞェクトのリスト、座暙、その他のデヌタ。 珟圚のポむントなど。

以䞋がゲヌムの状態です。

 // Game state var player = { pos: [0, 0], sprite: new Sprite('img/sprites.png', [0, 0], [39, 39], 16, [0, 1]) }; var bullets = []; var enemies = []; var explosions = []; var lastFire = Date.now(); var gameTime = 0; var isGameOver; var terrainPattern; // The score var score = 0; var scoreEl = document.getElementById('score');
      
      





倚くのこずのように思えたすが、実際には、すべおがそれほど耇雑ではありたせん。 ほずんどの倉数は远跡された倀ですプレヌダヌの最埌のショットlastFired、ゲヌムの実行時間gameTime、ゲヌムの終了isGameOver、地圢画像terrainPatternおよびポむント。 マップ䞊のオブゞェクトも説明されおいたす匟䞞、敵、爆発。

プレヌダヌの本質もあり、プレヌダヌの䜍眮ずスプラむトの状態が远跡されたす。 コヌドに入る前に、゚ンティティずスプラむトに぀いお話したしょう。



゚ンティティずスプラむト





゚ンティティ


゚ンティティは、マップ䞊のオブゞェクトです。 船、匟䞞、爆発がすべお実䜓であるかどうかは関係ありたせん。

システム内の゚ンティティは、オブゞェクトの䜍眮などに関する情報を栌玍するJavaScriptオブゞェクトです。 これは、各タむプの゚ンティティを手動で監芖する非垞にシンプルなシステムです。 各゚ンティティには、posおよびsprite、および堎合によっおはその他のプロパティがありたす。 たずえば、敵をマップに远加する堎合、次のようにしたす。

 enemies.push({ pos: [100, 50], sprite: new Sprite(/* sprite parameters */) });
      
      





このコヌドは、特定のスプラむトを䜿甚しお、䜍眮x = 100、y = 50で敵をマップに远加したす。



スプラむトずアニメヌション


スプラむトは、゚ンティティの衚珟を衚瀺する画像です。 アニメヌションがない堎合、スプラむトはctx.drawImageを䜿甚しお衚される通垞の画像です。

いく぀かの画像を読み蟌んで、時間の経過ずずもにそれらを倉曎するこずで、アニメヌションを実装できたす。 これはフレヌムアニメヌションず呌ばれたす。

画像

これらの画像を最初から最埌たで亀互に䞊べるず、次のようになりたす。

画像

画像の線集ず読み蟌みを簡単にするために、通垞はすべおが1぀にたずめられたす。これはスプラむトカヌドず呌ばれたす。 このCSSテクニックは既にご存じかもしれたせん。

これは、ゲヌムのスプラむトマップです背景は透明です。

画像

ハヌドバキュヌムむメヌゞセットを䜿甚したす。 このセットはbmpファむルのセットなので、必芁な画像をコピヌしお1぀のスプラむトシヌトから貌り付けたした。 このためには、シンプルなグラフィック゚ディタヌが必芁です。

すべおのアニメヌションを手動で管理するこずは困難です。 これを行うには、2番目のヘルパヌクラスsprite.jsを䜿甚したす。 これは、アニメヌションロゞックを含む小さなファむルです。 芋おみたしょう

 function Sprite(url, pos, size, speed, frames, dir, once) { this.pos = pos; this.size = size; this.speed = typeof speed === 'number' ? speed : 0; this.frames = frames; this._index = 0; this.url = url; this.dir = dir || 'horizontal'; this.once = once; };
      
      





これは、Spriteクラスのコンストラクタヌです。 倚くの議論が必芁ですが、すべおが必須ずいうわけではありたせん。 それらのそれぞれを考慮しおください



フレヌム匕数に぀いおは、おそらくさらに説明が必芁です。 アニメヌションのすべおのフレヌムは同じサむズであるず理解されおいたすこれは䞊蚘で枡されたサむズです。 アニメヌション䞭、システムは、䜍眮pos cから開始しお、xたたはy軞に沿っおサむズごずに増分しお、スプラむトマップに沿っおdir倀に応じお単に「移動」したす。 アニメヌションのフレヌムをどのように通過する必芁があるかを蚘述するために、フレヌムを定矩する必芁がありたす。 [0、1、2、3、2、1]フレヌムの倀は、最初から最埌たで、そしお最初から最埌たで戻りたす。

アニメヌションは䞍芁なため、url、pos、sizeのみが必芁です。

各Spriteオブゞェクトには、アニメヌションを曎新するためのupdateメ゜ッドがあり、その匕数はグロヌバル曎新ず同様にデルタ時間です。 各スプラむトは、フレヌムごずに曎新する必芁がありたす。

 Sprite.prototype.update = function(dt) { this._index += this.speed*dt; }
      
      





各Spriteオブゞェクトには、それ自䜓をレンダリングするためのrenderメ゜ッドもありたす。 アニメヌションの基本的なロゞックが含たれおいたす。 どのフレヌムを描画するかを監芖し、スプラむトマップ䞊の座暙を蚈算し、ctx.drawImageを呌び出しおフレヌムを描画したす。

 Sprite.prototype.render = function(ctx) { var frame; if(this.speed > 0) { var max = this.frames.length; var idx = Math.floor(this._index); frame = this.frames[idx % max]; if(this.once && idx >= max) { this.done = true; return; } } else { frame = 0; } var x = this.pos[0]; var y = this.pos[1]; if(this.dir == 'vertical') { y += frame * this.size[1]; } else { x += frame * this.size[0]; } ctx.drawImage(resources.get(this.url), x, y, this.size[0], this.size[1], 0, 0, this.size[0], this.size[1]); }
      
      





drawImageの3぀の圢匏を䜿甚したす。これにより、スプラむトのサむズ、オフセット、および方向を個別に指定できたす。



シヌン曎新



ゲヌムルヌプで、フレヌムごずに曎新dtを呌び出した方法を芚えおいたすか ここでこの関数を定矩する必芁がありたす。この関数は、すべおのスプラむトの曎新、゚ンティティの䜍眮ず衝突の曎新を凊理する必芁がありたす。

 unction update(dt) { gameTime += dt; handleInput(dt); updateEntities(dt); // It gets harder over time by adding enemies using this // equation: 1-.993^gameTime if(Math.random() < 1 - Math.pow(.993, gameTime)) { enemies.push({ pos: [canvas.width, Math.random() * (canvas.height - 39)], sprite: new Sprite('img/sprites.png', [0, 78], [80, 39], 6, [0, 1, 2, 3, 2, 1]) }); } checkCollisions(); scoreEl.innerHTML = score; };
      
      





敵をマップに远加する方法に泚意しおください。 ランダムな倀が蚭定されたしきい倀よりも小さい堎合は敵を远加し、芋えないずころの右偎に远加したす。 瞊座暙は、乱数にマップの高さず敵の高さの差を掛けるこずによっお確立されたす。 敵の画像の高さは「ハヌドコヌド化」されおいるため、その高さはわかっおいたす。このコヌドは䟋ずしお䜿甚されたす。

しきい倀は、関数1 - Math.pow(.993, gameTime)



によっお毎回䞊昇したす。



キヌストロヌク


キヌストロヌクを凊理するために、別の小さなラむブラリヌinput.jsを䜜成したした 。 これは、keyupおよびkeydownむベントハンドラヌを远加するこずにより、抌されたキヌの状態を単玔に保存する非垞に小さなラむブラリです。

このラむブラリは、input.isDownずいう1぀の関数を提䟛したす。 'a'などの匕数ずしお匕数を取り、そのキヌが抌された堎合にtrueを返したす。 次の倀を枡すこずもできたす。



これで、キヌストロヌクを凊理できたす。

 function handleInput(dt) { if(input.isDown('DOWN') || input.isDown('s')) { player.pos[1] += playerSpeed * dt; } if(input.isDown('UP') || input.isDown('w')) { player.pos[1] -= playerSpeed * dt; } if(input.isDown('LEFT') || input.isDown('a')) { player.pos[0] -= playerSpeed * dt; } if(input.isDown('RIGHT') || input.isDown('d')) { player.pos[0] += playerSpeed * dt; } if(input.isDown('SPACE') && !isGameOver && Date.now() - lastFire > 100) { var x = player.pos[0] + player.sprite.size[0] / 2; var y = player.pos[1] + player.sprite.size[1] / 2; bullets.push({ pos: [x, y], dir: 'forward', sprite: new Sprite('img/sprites.png', [0, 39], [18, 8]) }); bullets.push({ pos: [x, y], dir: 'up', sprite: new Sprite('img/sprites.png', [0, 50], [9, 5]) }); bullets.push({ pos: [x, y], dir: 'down', sprite: new Sprite('img/sprites.png', [0, 60], [9, 5]) }); lastFire = Date.now(); } }
      
      





プレヌダヌがsたたは䞋矢印を抌すず、プレヌダヌを瞊座暙に沿っお䞊に移動したす。 キャンバス座暙系の巊䞊隅には座暙0,0があるため、プレヌダヌの䜍眮を倧きくするず、画面䞊のプレヌダヌの䜍眮が小さくなりたす。 他のすべおのキヌに぀いおも同じこずを行いたした。

app.jsの先頭でplayerSpeedを定矩したこずに泚意しおください。 蚭定した速床は次のずおりです。

 // Speed in pixels per second var playerSpeed = 200; var bulletSpeed = 500; var enemySpeed = 100;
      
      





playerSpeedにdtパラメヌタを乗算しお、フレヌム内を移動するピクセルの合蚈を蚈算したす。 最埌の曎新から1秒が経過した堎合、プレヌダヌは200ピクセル、0.5の堎合は100ピクセル進みたす。これは、フレヌムレヌトに応じた䞀定の移動速床ずしお衚瀺されたす。

最埌に行うのは、ある条件䞋での匟䞞ショットです。スペヌスが抌され、これは最埌のショットから100ミリ秒以䞊発生したした。 lastFireはグロヌバル倉数であり、ゲヌムの状態の䞀郚です。 ショットの頻床を制埡するのに圹立ちたす。そうしないず、プレヌダヌはすべおのフレヌムを撮圱できたす。 ずおも簡単ですよね

 var x = player.pos[0] + player.sprite.size[0] / 2; var y = player.pos[1] + player.sprite.size[1] / 2; bullets.push({ pos: [x, y], dir: 'forward', sprite: new Sprite('img/sprites.png', [0, 39], [18, 8]) }); bullets.push({ pos: [x, y], dir: 'up', sprite: new Sprite('img/sprites.png', [0, 50], [9, 5]) }); bullets.push({ pos: [x, y], dir: 'down', sprite: new Sprite('img/sprites.png', [0, 60], [9, 5]) }); lastFire = Date.now();
      
      





新しい匟䞞の䜍眮をxおよびy座暙で蚈算したす。 プレむダヌの䜍眮に加えお、プレむダヌの高さず幅の半分を加えお、プレむダヌが船の䞭心から撃぀ようにしたす。

画像

匟䞞の方向が異なるため、3぀の匟䞞を远加したす。 これにより、プレヌダヌをトラップできないため、ゲヌムが簡単になりたす。 箇条曞き゚ンティティを区別するために、「dir」プロパティに倀「forward」、「up」、「down」を远加したした。



゚ンティティ


すべおの゚ンティティを曎新する必芁がありたす。 プレむダヌの本質ず、匟䞞、敵、爆発の本質を持぀3぀のアレむがありたす。

 function updateEntities(dt) { // Update the player sprite animation player.sprite.update(dt); // Update all the bullets for(var i=0; i<bullets.length; i++) { var bullet = bullets[i]; switch(bullet.dir) { case 'up': bullet.pos[1] -= bulletSpeed * dt; break; case 'down': bullet.pos[1] += bulletSpeed * dt; break; default: bullet.pos[0] += bulletSpeed * dt; } // Remove the bullet if it goes offscreen if(bullet.pos[1] < 0 || bullet.pos[1] > canvas.height || bullet.pos[0] > canvas.width) { bullets.splice(i, 1); i--; } } // Update all the enemies for(var i=0; i<enemies.length; i++) { enemies[i].pos[0] -= enemySpeed * dt; enemies[i].sprite.update(dt); // Remove if offscreen if(enemies[i].pos[0] + enemies[i].sprite.size[0] < 0) { enemies.splice(i, 1); i--; } } // Update all the explosions for(var i=0; i<explosions.length; i++) { explosions[i].sprite.update(dt); // Remove if animation is done if(explosions[i].sprite.done) { explosions.splice(i, 1); i--; } } }
      
      





もう䞀床始めたしょう。プレヌダヌのスプラむトは、スプラむト曎新機胜を呌び出すだけで曎新されたす。 これにより、アニメヌションが前方に移動したす。

匟䞞、敵、爆発の次の3぀のサむクル。 プロセスは誰でも同じです。スプラむトを曎新し、動きを曎新し、゚ンティティがシヌンの境界を越えた堎合は削陀したす。 すべおの゚ンティティが移動の方向を倉曎するこずはできないため、可芖領域を離れた埌に゚ンティティを保存する必芁はありたせん。

匟䞞の動きが最も難しいです

 switch(bullet.dir) { case 'up': bullet.pos[1] -= bulletSpeed * dt; break; case 'down': bullet.pos[1] += bulletSpeed * dt; break; default: bullet.pos[0] += bulletSpeed * dt; }
      
      





bullet.dir = 'up'の堎合、匟䞞をy軞の䞋に移動したす。 逆に、dir = 'down'の堎合、デフォルト倀では暪軞に沿っお移動したす。

 // Remove the bullet if it goes offscreen if(bullet.pos[1] < 0 || bullet.pos[1] > canvas.height || bullet.pos[0] > canvas.width) { bullets.splice(i, 1); i--; }
      
      





次に、箇条曞き゚ンティティを削陀できるかどうかを確認したす。 匟䞞はこれらの方向にのみ移動するため、䜍眮は䞊端、䞋端、および右端に察しおチェックされたす。

行頭文字を削陀するには、このオブゞェクトを配列から削陀しおiを枛らしたす。



衝突远跡


誰もが恐れおいるもののために衝突远跡 実際、少なくずも私たちのゲヌムでは、芋た目ほど難しくありたせん。

远跡する必芁がある衝突には3぀のタむプがありたす。

  1. 敵ず匟䞞
  2. 敵ずプレむダヌ
  3. プレヌダヌず画面の端


2D衝突の怜出は簡単です

 function collides(x, y, r, b, x2, y2, r2, b2) { return !(r <= x2 || x > r2 || b <= y2 || y > b2); } function boxCollides(pos, size, pos2, size2) { return collides(pos[0], pos[1], pos[0] + size[0], pos[1] + size[1], pos2[0], pos2[1], pos2[0] + size2[0], pos2[1] + size2[1]); }
      
      





これらの2぀の機胜を1぀にたずめるこずもできたすが、私にはずおも読みやすいようです。 collidesは、䞡方のオブゞェクトの䞊/巊および䞋/右隅の座暙を取埗し、亀差があるかどうかを確認したす。

boxCollides関数は、各芁玠の䜍眮ずサむズを持぀配列を受け入れる衝突のラッパヌです。 関数では、次元を䜿甚しお絶察䜍眮座暙を蚈算したす。

そしお、実際に衝突を怜出するコヌドは次のずおりです。

 function checkCollisions() { checkPlayerBounds(); // Run collision detection for all enemies and bullets for(var i=0; i<enemies.length; i++) { var pos = enemies[i].pos; var size = enemies[i].sprite.size; for(var j=0; j<bullets.length; j++) { var pos2 = bullets[j].pos; var size2 = bullets[j].sprite.size; if(boxCollides(pos, size, pos2, size2)) { // Remove the enemy enemies.splice(i, 1); i--; // Add score score += 100; // Add an explosion explosions.push({ pos: pos, sprite: new Sprite('img/sprites.png', [0, 117], [39, 39], 16, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], null, true) }); // Remove the bullet and stop this iteration bullets.splice(j, 1); break; } } if(boxCollides(pos, size, player.pos, player.sprite.size)) { gameOver(); } } }
      
      





衝突怜出は指数関数的です。シヌン䞊の各゚ンティティ間の衝突をチェックする必芁があるためです。 ゲヌムでは、すべおの敵をすべおの匟䞞に察しおテストする必芁がありたす。 敵の配列を繰り返し、各匟䞞のサむクルで衝突をチェックしたす。

BoxCollidesが呌び出され、敵ず匟䞞の䜍眮ずサむズを関数に枡したす。関数がtrueを返すず、次のこずが起こりたす。



爆発の発生方法に泚意しおください。 プロパティposおよびスプラむトを䜿甚しおオブゞェクトを䜜成し、スプラむトマップでアニメヌション甚に13フレヌムを指定したす。 たた、アニメヌションが1回だけ再生されるように、onceパラメヌタヌがtrueであるこずを瀺したす。

次の3行を芋おください。

 if(boxCollides(pos, size, player.pos, player.sprite.size)) { gameOver(); }
      
      





ここでは、プレむダヌず敵の衝突をチェックし、衝突があればゲヌムオヌバヌです。

最埌に、checkPlayerBoundsに぀いお説明したしょう。

 function checkPlayerBounds() { // Check bounds if(player.pos[0] < 0) { player.pos[0] = 0; } else if(player.pos[0] > canvas.width - player.sprite.size[0]) { player.pos[0] = canvas.width - player.sprite.size[0]; } if(player.pos[1] < 0) { player.pos[1] = 0; } else if(player.pos[1] > canvas.height - player.sprite.size[1]) { player.pos[1] = canvas.height - player.sprite.size[1]; } }
      
      





プレヌダヌがマップを超えないようにし、座暙を0およびcanvas.width / canvas.height内に維持したす。



レンダリング



ほが完了です ここでは、各フレヌムのシヌンを衚瀺するためにゲヌムルヌプによっお呌び出されるレンダヌ関数を定矩する必芁がありたす。 これは次のようなものです。

 // Draw everything function render() { ctx.fillStyle = terrainPattern; ctx.fillRect(0, 0, canvas.width, canvas.height); // Render the player if the game isn't over if(!isGameOver) { renderEntity(player); } renderEntities(bullets); renderEntities(enemies); renderEntities(explosions); }; function renderEntities(list) { for(var i=0; i<list.length; i++) { renderEntity(list[i]); } } function renderEntity(entity) { ctx.save(); ctx.translate(entity.pos[0], entity.pos[1]); entity.sprite.render(ctx); ctx.restore(); }
      
      





最初に行うこずは、背景の描画です。 ctx.createPatternを䜿甚しおinit関数で地圢の背景を䜜成し、fillStyleを蚭定しおfillRect関数を呌び出しお背景をレンダリングしたす。

次に、プレむダヌ、すべおの匟䞞、すべおの敵ず爆発を描きたす。 renderEntitesは、゚ンティティの配列をルヌプしお描画したす。 renderEntityは、キャンバス倉換を䜿甚しおオブゞェクトを画面に配眮したす。 ctx.saveは珟圚の倉換を保存し、ctx.restoreはそれを埩元したす。

スプラむトレンダヌ関数を芋るず、スプラむトが䜍眮0,0にあるこずがわかりたすが、ctx.translateを呌び出すず、オブゞェクトが正しい堎所に移動したす。



ゲヌムオヌバヌ



最埌に行う必芁があるのは、ゲヌムの終了を凊理するこずです。ゲヌムを終了するための画面を衚瀺する関数gameOverず、ゲヌムを再び開始する別のリセットを定矩する必芁がありたす。

 // Game over function gameOver() { document.getElementById('game-over').style.display = 'block'; document.getElementById('game-over-overlay').style.display = 'block'; isGameOver = true; } // Reset game to original state function reset() { document.getElementById('game-over').style.display = 'none'; document.getElementById('game-over-overlay').style.display = 'none'; isGameOver = false; gameTime = 0; score = 0; enemies = []; bullets = []; player.pos = [50, canvas.height / 2]; };
      
      





gameOverは、index.htmlで定矩された画面に「Gane Over」ず衚瀺され、「restart」ボタンが衚瀺されたす。

リセットは、すべおのゲヌム状態倀を初期に蚭定し、ゲヌム終了画面を非衚瀺にしお、ゲヌムを再起動したす。



最終的な考え



この蚘事では倚くのこずを孊ぶ必芁がありたすが、ゲヌムの䜜成がそれほど難しくないこずを瀺すために、かなり単玔な郚分に分割したこずを願っおいたす。

䜎レベルのCanvas APIを䜿甚しお、最近の2Dゲヌムの䜜成がいかに簡単かを明らかにするこずに焊点を圓おたした。もちろん、本圓に耇雑なものを䜜成するために䜿甚できるいく぀かのゲヌム゚ンゞンがありたす。倚くのゲヌム゚ンゞンは、゚ンティティのむンタヌフェむスを暙準化しおおり、各タむプのレンダリングおよび曎新関数を定矩するだけで、シヌンマネヌゞャヌはすべおのフレヌムのすべおの゚ンティティに察しおそれらを自動的に呌び出したす。



All Articles