manmanrai’s diary

新米フロントエンジニアの勉強記録ブログ

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;
}

f:id:manmanrai:20170427143555p:plain

ゲーム完成です!!

補足:スプライトについて

スプライトの座標、visible、回転以外いろんなオプションあるみたいで、詳しく公式のドキュメントで調べられます。
リンクはこちらです。

Class: Sprite

基本、スプライトは継承のルール従ってます。

  • DisplayObject > Container > Sprite

これで終了になります!
お疲れ様でした。

シリーズ:2D WebGL renderer Pixi.js v4

manmanrai.hatenablog.com

参考サイト

pixi.jsの公式サイト(英語)

www.pixijs.com

参考しているチュートリアル(英語)

github.com