2D WebGL renderer Pixi.js v4【連載最終回】
前回の話
最後のプロジェクト「宝探しゲーム」の下準備しました。
(setup function の中身)
続きは、play functionとend functionゲームの核心の部分です。
※ 自分わかるように整理したので、チュートリアルの順番少し違います。
play function
登場人物とモンスターなど用意完了しましたので、いざゲーム始まる時なにを用意すれば良いのか、リストアップしてみました。
- 勇者の移動、移動の範囲制限
- モンスターの移動、移動の範囲制限、壁ぶつかった時の反応
- 勇者とモンスターぶつかったのか
- 勇者と宝箱ぶつかったのか(宝ゲット?)
- 勇者と扉ぶつかったのか(脱出成功?)
- ゲーム成功か失敗かの判断
そして、整理してみましょう。
helper functionとしてまとめてplay functionから出した方が良いのは、
- 範囲制限(限界に来たのかどうかの判断)
- ぶつかってるかどうかの判断
では、コードに変えると
// 範囲制限(限界に来たのかどうかの判断) // 入れる値は移動できる、移動してるスプライト(ここでは勇者とモンスター)と動ける範囲 function contain(sprite, container) { var collision = undefined; // 左 if (sprite.x < container.x) { sprite.x = container.x; collision = "left"; } // 上 if (sprite.y < container.y) { sprite.y = container.y; collision = "top"; } // 右 if (sprite.x + sprite.width > container.width) { sprite.x = container.width - sprite.width; collision = "right"; } // 下 if (sprite.y + sprite.height > container.height) { sprite.y = container.height - sprite.height; collision = "bottom"; } // 値を返す return collision; }
// ぶつかってるかどうかの判断方程式、前回に詳しく説明したので、省略する function hitTestRectangle(r1, r2) { var hit, combinedHalfWidths, combinedHalfHeights, vx, vy; hit = false; r1.centerX = r1.x + r1.width / 2; r1.centerY = r1.y + r1.height / 2; r2.centerX = r2.x + r2.width / 2; r2.centerY = r2.y + r2.height / 2; r1.halfWidth = r1.width / 2; r1.halfHeight = r1.height / 2; r2.halfWidth = r2.width / 2; r2.halfHeight = r2.height / 2; vx = r1.centerX - r2.centerX; vy = r1.centerY - r2.centerY; combinedHalfWidths = r1.halfWidth + r2.halfWidth; combinedHalfHeights = r1.halfHeight + r2.halfHeight; if (Math.abs(vx) < combinedHalfWidths) { if (Math.abs(vy) < combinedHalfHeights) { hit = true; } else { hit = false; } } else { hit = false; } return hit; };
// わざ独立させなくても良いけど、一応チュートリアルには分けて書いた function randomInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; }
play function内部に入ります。
function play() { // キーボードeventからもらった数値移動させる explorer.x += explorer.vx; explorer.y += explorer.vy; // 勇者の移動範囲 contain(explorer, {x: 28, y: 10, width: 488, height: 480}); //contain(explorer, stage); // 勇者なにもぶつかってないという初期設定 var explorerHit = false; // モンスター配列使って blobs.forEach(function(blob) { // 移動させる blob.y += blob.vy; // モンスターにも移動範囲制限かける、返ってくる値をキャッチする var blobHitsWall = contain(blob, {x: 28, y: 10, width: 488, height: 480}); // 返ってきた値を判別し、進行方向を逆方向に変える if (blobHitsWall === "top" || blobHitsWall === "bottom") { blob.vy *= -1; } // 勇者とモンスターぶつかったら、さっきなにもぶつかってない値を変える if(hitTestRectangle(explorer, blob)) { explorerHit = true; } }); // もし勇者モンスターにぶつかったら、 if(explorerHit) { // 一瞬半透明なる explorer.alpha = 0.5; // HPも減る healthBar.outer.width -= 1; } else { // 半透明から戻す explorer.alpha = 1; } // もし勇者とぶつかったものは宝箱だったら、 if (hitTestRectangle(explorer, treasure)) { // 宝箱を持って帰れるようにする(宝箱の位置を常に勇者の右下に) treasure.x = explorer.x + 8; treasure.y = explorer.y + 8; } // HP減りすぎるとゲーム終了、メッセージを失敗に変更 if (healthBar.outer.width < 0) { state = end; message.text = "You lost!"; } // (勇者&)宝箱とドアぶつかったら、ゲーム終了、メッセージを成功に変更 if (hitTestRectangle(treasure, door)) { state = end; message.text = "You won!"; } }
そして、end functionにでシーンの切り替え
function end() { gameScene.visible = false; gameOverScene.visible = true; }
ゲーム完成です!!
補足:スプライトについて
スプライトの座標、visible、回転以外いろんなオプションあるみたいで、詳しく公式のドキュメントで調べられます。
リンクはこちらです。
基本、スプライトは継承のルール従ってます。
- DisplayObject > Container > Sprite
これで終了になります!
お疲れ様でした。
シリーズ:2D WebGL renderer Pixi.js v4
参考サイト
pixi.jsの公式サイト(英語)
参考しているチュートリアル(英語)