Help us understand the problem. What is going on with this article?

【phina.js】ゲーム作成チュートリアル(ブロック崩し)第1回=ブロックの配置=

More than 1 year has passed since last update.

※これまでのチュートリアルはphina.js チュートリアル集にまとめています。

第2回=パドルの作成=

目的

まず最初に以下のようにブロックを配置したいと思います。

breakout-tut-02.png

定数とBlockクラスを定義する

ゲーム内で使用する定数は通常のvarで、Blockクラスはphina.defineを使って定義します。コードは以下のとおりです。

phina.globalize();
/*
 * 定数
 */
var BLOCK_WIDTH = 40 * 2;
var BLOCK_HEIGHT = 60 / 2;
/*
 * メインシーン
 */
phina.define('MainScene', {
  superClass: 'DisplayScene',
  // コンストラクタ
  init: function() {
    // 親クラス初期化
    this.superInit();
    // 背景色
    this.backgroundColor = 'black';
  },
});
/*
 * ブロッククラス
 */
phina.define('Block', {
  // 親クラス指定
  superClass: 'RectangleShape',
  // コンストラクタ
  init: function() {
    // 親クラス初期化
    this.superInit({
      width: BLOCK_WIDTH,
      height: BLOCK_HEIGHT,
    });
  },
});
/*
 * メイン処理
 */
phina.main(function() {
  // アプリケーションを生成
  var app = GameApp({
    title: 'Break Out',
  });
  // 実行
  app.run();
});

コード説明

定数

var BLOCK_WIDTH = 40 * 2;
var BLOCK_HEIGHT = 60 / 2;
  • BLOCK_WIDTHはブロックの横幅、BLOCK_HEIGHTはブロックの縦幅です。
  • 40 や 60という数値を基準にしているのは、Sceneにデフォルトで設定されているGridの存在を意識しているためです。Gridについては後述します。

ブロッククラス

phina.define('Block', {
  // 親クラス指定
  superClass: 'RectangleShape',
  // コンストラクタ
  init: function() {
    // 親クラス初期化
    this.superInit({
      width: BLOCK_WIDTH,
      height: BLOCK_HEIGHT,
    });
  },
});
  • phina.defineBlockクラスを定義しています。
  • ブロックのグラフィックは、画像ファイルを使わずにphina.jsで用意されている基本図形のShapeを活用します。
  • 今回はその中の1つであるRectangleShape(矩形)クラスを継承しています。
  • this.superInitで、親クラス(RectangleShape)のwidthheightプロパティに、定数で定義したブロックのサイズをそれぞれ渡しています。

ブロックを配置する

オーソドックスなブロック崩しの場合、ブロックはタイル状に並んで配置されています。そこで、phina.jsで用意されているGridクラスを利用してタイル状に並べたいと思います。
まずはコードをご覧下さい。

// グローバルに展開
phina.globalize();
/*
 * 定数
 */
var BLOCK_WIDTH = 40 * 2;
var BLOCK_HEIGHT = 60 / 2;
/*
 * メインシーン
 */
phina.define("MainScene", {
  // 継承
  superClass: 'DisplayScene',
  // コンストラクタ
  init: function() {
    // 親クラス初期化
    this.superInit();
    // 背景色
    this.backgroundColor = 'black';
    // ブロックグループ
    this.blockGroup = DisplayElement().addChildTo(this);

    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));
      });
    });
  },
});
/*
 * ブロッククラス
 */
phina.define('Block', {
  // 親クラス指定
  superClass: 'RectangleShape',
  // コンストラクタ
  init: function() {
    // 親クラス初期化
    this.superInit({
      width: BLOCK_WIDTH,
      height: BLOCK_HEIGHT,
    });
  },
});
/*
 * メイン処理
 */
phina.main(function() {
  // アプリケーションを生成
  var app = GameApp({
    title: 'Break Out',
  });
  // 実行
  app.run();
});

[runstantで確認]

コード説明

ブロックグループの作成

this.blockGroup = DisplayElement().addChildTo(this);
  • 作成するブロックはGameSceneに直接追加しても良いのですが、利便性を考えて、ブロック管理用のグループを作成します。
  • グループはDisplayElementクラスで作成し、addChildTo(this)GameSceneに追加しています。
  • 個々のブロックは、後のループ処理でグループに追加するようにします。

Gridを使った配置:

ループ使って、ブロックをタイル状に配置する処理です。

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

まずループ処理について見てみましょう。

Array.range(2, 16, 2).each(function(spanX) {

phina.jsでは、元々のjavascriptArrayに様々な拡張が施されています。
rangeはその中の1つで、範囲指定した数値の配列を新たに作ってくれます。Pythonrangeと同じ仕様です。

Array.range(開始、終了、ステップ数)

今回の場合は、[2,4,6,8,10,12,14]という配列を作成したことになります。
次のeachは、基本的にjavascriptforEachと同じで、引数で与えられたコールバック関数内の処理を配列の各要素に適用します。
まとめると、[2,4,6,8,10,12,14]という配列の各々の要素に対して、function内の処理を適用するということになります。各要素は、引数のspanXで参照することができます。
同様に、以下で縦方向の繰り返しを行っています。

Array.range(1, 4, 0.5).each(function(spanY) {

ループ処理について、今回はphina.jsらしい書き方にしてみましたが、もちろん通常のfor文を使っても全く問題ありません。

最後に、個々のブロックを作成して追加する処理について説明します。

        Block().addChildTo(self.blockGroup)
               .setPosition(self.gridX.span(spanX), self.gridY.span(spanY));
  • Blockクラスのオブジェクトを作成して、前もって作っておいたblockGroupに追加しています。
  • this.blockGroupと書きたいところですが、function内のthisGameScene内でのthisは参照先が異なるため、そのままだとエラーになってしまいます。
  • そこで、ループ処理に入る前に
var self = this;

と書いて、selfが正しい参照(GameScene)となるようにしています。

  • 次のsetPositionで、ブロックの位置を決めています。
  • オブジェクトの作成から配置まで、jqueryのチェインメソッドのように繋げています。phina.jsでは、大半がチェインメソッドに対応していますので、スッキリ書けて便利です。
  • gridX.span(spanX)は、横方向のGridspanXグリッド目の位置を表します。gridY.span(spanY)は縦方向です。
  • Gridを活用して縦と横方向にループ処理することで、このようにブロックをタイル状に配置することができます。

Gridについて

phina.jsでは、SceneにデフォルトでGridというものが設定されています。
Gridは、Scene(スクリーン)を格子状に分けた情報を持っており、横方向、縦方向にそれぞれ16分割されています。
横方向のグリッドはthis.gridX、縦方向はthis.gridYでアクセスできます。
オブジェクトの配置の際にGridはとても便利な機能ですので、別の機会に掘り下げて説明したいと思います。

今回はここまで

説明が長くなってしまいましたが、ここまでで、ブロックを配置することができました。しかし、ゲームとしての動きが何もないため面白くないですね。
次回は、プレイヤーが操作するパドルを作成して、徐々にゲームらしくしていきたいと思います。

第2回=パドルの作成=

alkn203
javascript製のゲームライブラリphina.jsを愛用している趣味プログラマ。ファミコン世代。 Hobbyist programmer javascript/html5/tmlib.js/phina.js/retro games
http://qiita.com/alkn203/items/bca3222f6b409382fe20
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした