phina.js を使って 100 行縛りでゲーム作ってみた

phi phi on phina.js, game

phina.js Advent Calendar 2015 - Qiita 14日目のエントリーです.

自分で言うのもなんですが, phina.js を使えば恐ろしく簡単にゲームを作ることができます!

一見は百聞にしかずということで, 今回は, phina.js を使って 100 行縛りで簡単なゲームを作ってみようと思います.

ゲームは Runstant で作ってあるので, このページ内でプレイできます!

About phina.js

phina.js って何?って方はこちらを御覧ください.

本日 JavaScript ゲームライブラリ『phina.js』をリリースしました! | phiary

Runstant Demo

今回作ったゲームです.

制限時間内にどれだけサークルをタッチできるかを競うというシンプルなゲームです. 良い結果が出たらリザルト画面のシェアボタンから自慢してみてください♪

Code

実装コードです. コメント含めて 90 行程度で作りました!

ゲームロジック部分だけでなく, タイトル -> ゲーム -> リザルトへの基本的な流れも phina.js が内部で共通的なことをやってくれるのですごく楽にゲームを作ることができました.

/*
 * runstant
 */

phina.globalize();

// constant
var SHARE_URL = 'http://phiary.me/phina-js-game-tie-up-100-line/';  
var SHARE_MESSAGE = 'phina.js を使えばたったこれだけの\nコードで簡単にゲームを作れます!!\nSCORE:{score}';  
var SHARE_HASH_TAGS = 'CircleTouch,phina.js';  
var TIME = 10*1000; // 10秒

// main scene
phina.define('MainScene', {  
  superClass: 'CanvasScene',

  // 初期化
  init: function() {
    this.superInit();

    // timer
    this.timer = TIME;
    // score
    this.score = 0;

    // timerLabel
    var timerLabel = Label('time:').addChildTo(this);
    timerLabel.x = this.gridX.center();
    timerLabel.y = this.gridY.span(2.5);
    timerLabel.fontSize = 64;
    timerLabel.text = 10*1000;
    this.timerLabel = timerLabel;

    // shape を生成
    var shape = CircleShape().addChildTo(this);
    shape.setInteractive(true, 'circle');
    shape.onpointstart = function() {
      this.score += 1;
      this.setRandomPosition();
    }.bind(this);
    this.shape = shape;
  },

  // 開始時
  onenter: function() {
    this.setRandomPosition();
  },

  // 更新
  update: function(app) {
    this.timer -= app.deltaTime;
    this.timerLabel.text = (this.timer/1000).toFixed(2);
    if (this.timer <= 0) {
      this.gameover();
    }
  },

  // ランダムな位置にセット
  setRandomPosition: function() {
    var shape = this.shape;
    var x = Math.randint(shape.width, this.gridX.width-shape.width);
    var y = Math.randint(shape.height, this.gridY.width-shape.height);
    var colorAngle = Math.randint(0, 360);

    shape.fill = 'hsl({0}, 80%, 60%)'.format(colorAngle);

    shape.setPosition(x, y);
  },

  // ゲームオーバー
  gameover: function() {
    // score を渡して終了
    this.exit({
      score: this.score,
      message: SHARE_MESSAGE,
      url: SHARE_URL,
      hashtags: SHARE_HASH_TAGS
    });
  },
});

phina.main(function() {  
  // app 作成
  var app = GameApp({
    title: 'Circle Touch',
    startLabel: 'title',
  });

  app.run();
});

Tips

簡単にいくつか tips 形式で解説したいと思います.

タッチに反応させよう

要素に対して

elm.setInteractive(true);  

とすることで, タッチ判定を有効にすることができます. 有効になるとタッチ系のイベントが走るようになるので, 下記のようにすることでタッチ時に実行したい処理を登録することができます.

elm.setInteractive(true);  
// タッチ時 or クリック時に呼ばれる
elm.onpointstart = function() {  
  // TODO: ...
}

今回のサンプルでは下記のように使っています.

// shape を生成
var shape = CircleShape().addChildTo(this);  
shape.setInteractive(true, 'circle');  
shape.onpointstart = function() {  
  this.score += 1;
  this.setRandomPosition();
}.bind(this);

経過時間を取得しよう

要素の update に渡される app の変数 deltaTime で毎フレーム間の 経過ミリ秒を取得することができます.

これを scene のもつ timer から毎フレーム引くことでタイマー機能を実現しています.

update: function(app) {  
  this.timer -= app.deltaTime;

ランダムな数値を生成

本来の JavaScript では, Math.random() という 0~1 の浮動小数点を返すものしかありませんが, phina.js ではそれを拡張して最小値, 最大値を指定して乱数を生成する Math.randint(min, max) という関数を定義しています.

今回のサンプルでは setRandomPosition() 部分で使用しています.

var x = Math.randint(shape.width, this.gridX.width-shape.width);  
var y = Math.randint(shape.height, this.gridY.width-shape.height);  

リザルト画面に様々なオプションを渡そう

MainSceneexit 時にオブジェクトを指定することで ResultScene にオプションを渡すことができます.

今回のデモでは,

// score を渡して終了
this.exit({  
  score: this.score,
  message: SHARE_MESSAGE,
  url: SHARE_URL,
  hashtags: SHARE_HASH_TAGS
});

とすることで, スコアの他にもメッセージや, twitter share 用の url と hastags も渡しています.

phina.js 勉強会

ちょっと勉強会というか phina.js ハッカソンを近々やろうと思っています.

きっと phina.js コントリビュータの方々も参加してくれると思うので phina.js の使い方だけではなくゲームプログラミングに関する様々な技術について学べるかと思います.

今回のエントリーのような 『100 行縛りでゲームプログラミングハッカソン』 とかやると面白いかもしれないですね.

良いアイディアなどありましたら気軽にご連絡下さい.

具体的な内容や日時など決まりましたらまたこのブログにて告知します. たまに覗いていただけると嬉しいです.