現役学生がphina.jsでゲーム作ってみた

こんなんで簡単なブロック崩しゲームが作れます。
一度皆さんもやってみてはいかがでしょうか

```index.html
 
<html>
  <head>
    <meta charset="utf-8" />
    
    <title>test  </title>
    
<style>
html {
  font-size: 62.5%;
}
body {
  color: #444;
  background-color: hsl(0, 0%, 96%);
}
h1 {
  font-size: 1.8rem;
}

</style>
  </head>
  <body>
    <script src="http://cdn.rawgit.com/phi-jp/phina.js/v0.2.0/build/phina.js"></script>
<script>
phina.globalize();//phina.game.GameAppのように階層的に定義されているクラスをGameAppだけで呼び出せるようにする処理などをしています。

var BLOCK_WIDTH = 40 * 2;
var BLOCK_HEIGHT = 60 / 2;
var PADDLE_WIDTH = BLOCK_WIDTH * 1.5;
var PADDLE_HEIGHT = BLOCK_HEIGHT;
var BALL_RADIUS = BLOCK_WIDTH / 8;
/*
 * メインシーン
 */
phina.define("MainScene", {//phina.defineは、クラスを定義する関数です。
  // 継承
  superClass: 'DisplayScene',//phina.defineは、クラスを定義する関数です。
  // コンストラクタ
  init: function() {//init関数は、いわゆるコンストラクタです。
    // 親クラス初期化
    this.superInit();//this.superInitで親クラスのコンストラクタを呼び出しています。この後に、実際のゲーム処理コードを書いていくことになります。
  
    this.backgroundColor = 'black';    // 背景色
    
    this.blockGroup = DisplayElement().addChildTo(this);// ブロックグループ
    
    this.dummyGroup = DisplayElement().addChildTo(this); // ダミーブロックグループ
  
    var screenRect = Rect(0, 0, 640, 960); // 位置判定用のRect

    var self = this;
    // Gridを利用してブロック設置
    Array.range(2, 16, 2).each(function(spanX) {
      Array.range(1, 4, 0.5).each(function(spanY) {
        Block().addChildTo(self.blockGroup)
               .setPosition(self.gridX.span(spanX), self.gridY.span(spanY));
      });
    });
    // パドル移動ライン
    var paddleY = this.gridY.span(14.5);
    // パドル設置
    var paddle = Paddle().addChildTo(this)
                         .setPosition(this.gridX.center(), paddleY);
    // 画面上でのタッチ移動時
    this.onpointmove = function(e) {
      // タッチ位置に移動
      paddle.setPosition(e.pointer.x | 0, paddleY);
      // 画面はみ出し防止
      if (paddle.left < screenRect.left) { paddle.left = screenRect.left; }
      if (paddle.right > screenRect.right) { paddle.right = screenRect.right; }
    };
    // 画面上でタッチが離れた時
    this.onpointend = function() {
      if (self.status === 'ready') {
        // ボール発射
        self.ball.vy = -self.ball.speed;
        self.status = 'move';
      }
    };
    // スコア
    this.score = 0;
    var scoreLabel = Label({
      text: this.score + '',
      fill: 'lime',
      fontSize: 64,
    }).addChildTo(this);
    scoreLabel.setPosition(this.gridX.center(), this.gridY.center());
    scoreLabel.alpha = 0.6;
    // 連続ヒット数
    this.hitNumber = 0;
    // ボール作成
    this.ball = Ball().addChildTo(this);
    // シーン全体から参照可能にする
    this.paddle = paddle;
    this.screenRect = screenRect;
    this.scoreLabel = scoreLabel;
    // ゲーム状態
    this.status = 'ready';
  },
    // 毎フレーム更新
  update: function() {
    var ball = this.ball;
    var paddle = this.paddle;
    var screenRect = this.screenRect;
    // ボール待機中
    if (this.status === 'ready') {
      // ボールはパドルの真上
      ball.vx = ball.vy = 0;
      ball.x = paddle.x;
      ball.bottom = paddle.top;
    }
    // ボール移動中
    if (this.status === 'move') {
      // ボール移動
      ball.moveBy(ball.vx, ball.vy);
      // 画面端反射
      // 上
      if (ball.top < screenRect.top) {
        ball.top = screenRect.top;
        ball.vy = -ball.vy;
      }
      // 左
      if (ball.left < screenRect.left) {
        ball.left = screenRect.left;
        ball.vx = -ball.vx;
      }
      // 右
      if (ball.right > screenRect.right) {
        ball.right = screenRect.right;
        ball.vx = -ball.vx;
      }
      // 落下
      var self = this;

      if (ball.top > screenRect.bottom) {
        // ゲームオーバー表示
        var label = Label({
          text: 'GAME OVER',
          fill: 'yellow',
          fontSize: 64,
        }).addChildTo(this);
        label.setPosition(this.gridX.center(), this.gridY.center());
        // 少し待ってからタイトル画面へ
        label.tweener.clear()
                     .wait(2000)
                     .call(function() {
                       self.nextLabel = 'title';
                       self.exit();
                     });
      }
      // パドルとの反射
      if (ball.hitTestElement(paddle) && ball.vy > 0) {
        ball.bottom = paddle.top;
        ball.vy = -ball.vy;
        // 当たった位置で角度を変化させる
        var dx = paddle.x - ball.x;
        ball.vx = -dx / 5;
      }
      // ブロックとの反射
      this.blockGroup.children.some(function(block) {
        // ヒットなら
        if (ball.hitTestElement(block)) {
          // 左上かど
          if (ball.top < block.top && ball.left < block.left) {
            // 位置補正
            ball.right = block.left;
            ball.bottom = block.top;
            // 移動方向設定
            ball.vx = -ball.speed;
            ball.vy = -ball.speed;
            // ブロック消去
            self.disableBlock(block);
            return true;
          }
          // 右上かど
          if (block.top < ball.top && block.right < ball.right) {
            ball.left = block.right;
            ball.bottom = block.top;
            ball.vx = ball.speed;
            ball.vy = -ball.speed;
            self.disableBlock(block);
            return true;
          }
          // 左下かど
          if (block.bottom < ball.bottom && ball.left < block.left) {
            ball.right = block.left;
            ball.top = block.bottom;
            ball.vx = -ball.speed;
            ball.vy = ball.speed;
            self.disableBlock(block);
            return true;
          }
          // 右下かど
          if (block.bottom < ball.bottom && block.right < ball.right) {
            ball.left = block.right;
            ball.top = block.bottom;
            ball.vx = ball.speed;
            ball.vy = ball.speed;
            self.disableBlock(block);
            return true;
          }
          // 左側
          if (ball.left < block.left) {
            ball.right = block.left;
            ball.vx = -ball.vx;
            self.disableBlock(block);
            return true;
          }
          // 右側
          if (block.right < ball.right) {
            ball.left = block.right;
            ball.vx = -ball.vx;
            self.disableBlock(block);
            return true;
          }
          // 上側
          if (ball.top < block.top) {
            ball.bottom = block.top;
            ball.vy = -ball.vy;
            self.disableBlock(block);
            return true;
          }
          // 下側
          if (block.bottom < ball.bottom) {
            ball.top = block.bottom;
            ball.vy = -ball.vy;
            self.disableBlock(block);
            return true;
          }
        }
      });
    }
    // クリアチェック
    if (this.blockGroup.children.length === 0) {
      // スコアをリザルトシーンに渡す
      this.exit({
        score: this.score,  
      });
    }
  },
  // ブロックの消去処理
  disableBlock: function(block) {
    // 消去アニメーション用のダミーブロック
    var dummy = Block().addChildTo(this.dummyGroup);
    // 属性コピー
    dummy.x = block.x;
    dummy.y = block.y;
    // ブロック削除
    block.remove();
    // 縮小して消えるアニメーション
    dummy.tweener.clear()
                 .to({scaleX: 0.1, scaleY: 0.1}, 200)
                 .call(function() {
                   dummy.remove();
                 });
    // スコア加算
    this.addScore();
  },
  // スコア加算処理
  addScore: function() {
    this.hitNumber++;
    this.score += this.hitNumber * 10;
    this.scoreLabel.text = this.score;
  },
});
/*
 * ブロッククラス
 */
phina.define('Block', {
  // 親クラス指定
  superClass: 'RectangleShape',
  // コンストラクタ
  init: function() {
    // 親クラス初期化
    this.superInit({
      width: BLOCK_WIDTH,
      height: BLOCK_HEIGHT,
    });
  },
});
/*
 * パドルクラス
 */
phina.define('Paddle', {
  // 親クラス指定
  superClass: 'RectangleShape',
  // コンストラクタ
  init: function() {
    // 親クラス初期化
    this.superInit({
      width: PADDLE_WIDTH,
      height: PADDLE_HEIGHT,
      fill: 'silver',
    });
  },
});
/*
 * ボールクラス
 */
phina.define('Ball', {
  // 親クラス指定
  superClass: 'CircleShape',
  // コンストラクタ
  init: function() {
    // 親クラス初期化
    this.superInit({
      radius: BALL_RADIUS,
      fill: 'silver',
    });
    // スピード
    this.speed = 6;
  },
});
/*
 * メイン処理
 */
phina.main(function() {
  // アプリケーションを生成
  var app = GameApp({
    title: 'GAMES',
  });
  // fps変更
  app.fps = 60;
  // 実行
  app.run();
});
</script>
  </body>
</html>
```