前回は phina.js の土台となるシーン構成を概観しました。
今回は シーンの要素となる基本的なオブジェクトをざっと見ていきます。
参考資料は、「phina.js Tips集 上巻」です。
1. ゲーム内で表示するオブジェクト
前回の各サンプルで使われていたオブジェクトは
・Label テキストを配置
・Sprite 画像を配置
・Button テキストと枠を配置
でした。
シンプルな項目なら、そのまま生成して配置します。
var label = Label('Hello, phina.js!').addChildTo(this);
動作も含めて定義するなら、継承してクラスを作ります1。
phina.define('Piece', {
superClass: 'Button',
/* ... */
});
クラス定義しておけば、それを配置して使えます。
var numbers = Array.range(1, MAX_NUM+1).shuffle();
numbers.each(function(index, i) {
/* ... */
var p = Piece(index).addChildTo(self.group);
/* ... */
p.onpointstart = function() {
self.check(this);
};
p.appear();
});
2. 基本の図形描画(Shape)
画面の要素の基本は Shapeです。
基本の四角形を表示できます。
Shapeの図形要素があります2。
2-1. Shapeクラスのスーパークラス・定義場所
2-2. 表示・非表示・位置・サイズ
Shape() で生成して、addChildToでシーンを指定すれば、Shapeは表示されます。
変数で保持しておくと後で操作できます。
var shape = Shape().addChildTo(this);
背景色を指定します。
shape.backgroundColor = '#ffff00';
shape.backgroundColor = `rgb(0, 255, 255)`;
位置は、x, y 座標で指定できます。
moveByやadd(Vector2) で相対位置に移動させることもできます。
var shape = Shape({
x: 320,
y: 480
}).addChildTo(this)
shape.x = 320;
shape.y = 480;
var shape = Shape().addChildTo(this).setPosition(320, 480);
shape.setPosition(320, 480).moveBy(100, 200);
var v = Vector2(100, 200);
shape.position.add(v);
サイズは width, heightで指定します。
var shape = Shape({
x: 320,
y: 480,
width: 128,
height: 256,
}).addChildTo(this);
shape.width = 128;
shape.height = 128;
shape.setSize(128, 256);
ただし、Shapeにはパディング(余白)があるので注意。paddingを0にしておきましょう。
defaults: {
width: 64,
height: 64,
padding: 8,
透過率を指定したり、非表示にもできます。
shape.alpha = 0.25;
shape.hide();
削除するときはremove()を使います11。
/**
* @method remove
* 自身を親要素の子要素から削除します。
*/
remove: function() {
if (!this.parent) return ;
this.parent.removeChild(this);
this.parent = null;
return this;
},
Shapeの色やサイズを途中で変更したら、再描画が必要みたいです。
this.render(this.canvas);
2-3. 回転・拡大縮小
回転率は、360°で表記されています。
var shape = Shape({
x: 320,
y: 720,
rotation: 60
}).addChildTo(this);
shape.rotation = 45;
var shape = Shape().addChildTo(this).setPosition(320, 600).setRotation(15);
Scaleで倍率。
shape.scaleX = 1.5; // 横方向に拡大
var shape = Shape().addChildTo(this).setPosition(320, 480).setScale(0.5, 0.5);
サイズ変更と拡大・縮小の違いは、実際のサイズが変更されるかどうかです。
拡大・縮小は見た目は変わっても実際のサイズは変更されません。
当たり判定の時などに注意が必要です。
setOriginでは、表示位置の基準を変更できます。
デフォルトでは(0, 0)で画像中央。
-1〜1まで変更できます。
shape.setOrigin(0, 0);
2-4. より複雑な図形要素
四角形の枠以外に、図形要素があります。
図形では、塗りつぶしと縁取りを設定できます。
2-5. マスによる位置管理(Grid)
ゲームでは位置をマス単位で管理することが多いです。
そのためのオブジェクトが Grid です12。
Grid を活用すると、マスと位置座標を相互に参照できます。
Gridは文字通り格子を意味しています。
phina.jsでは、 Sceneに対してデフォルトでGridが設定されていて、
縦・横がそれぞれ16分割の格子に分けられています(格子の縦横比は正方形ではなく、画面比と同じになります)。

Scene::gridXとScene::gridYを利用します。
var rect = RectangleShape().addChildTo(this);
rect.setPosition(this.gridX.span(4), this.gridY.span(6));
中央からの相対位置を指定することもできます。
var rect2 = RectangleShape().addChildTo(this);
rect2.setPosition(this.gridX.center(-3), this.gridY.center(2));
rect2.alpha = 0.5;
spanやcenterの引数には小数値も指定できますので、位置の微調整が可能です。
任意の幅とグリッド数で独自にGridを作成することができます。
Grid({
width: グリッド全体の幅,
columns: グリッドの数,
loop: spanにマイナス値を指定できるかどうか(任意),
offset: オフセット値(任意)
});
x, y それぞれ用意します。
var starGridX = Grid({
width: 320,
columns: 10,
offset: 100
});
var starGridY = Grid({
width: 160,
columns: 5,
offset: 320
});
(10).times(function(spanX) {
(5).times(function(spanY) {
var star = StarShape({
radius: 12,
}).addChildTo(starGroup);
star.setPosition(starGridX.span(spanX), starGridY.span(spanY));
});
});
2-6. テキスト描画(Label)
文字を配置するには、Labelを使います。
// ラベル表示
var label = Label('Time is money').addChildTo(this);
label.setPosition(this.gridX.center(), this.gridY.center());
フォントサイズ・フォント、色も変更できます。
var label2 = Label('Time is money').addChildTo(this);
label2.setPosition(320, 600);
label2.fontFamily = "'sans-serif'";
label2.fontSize = 48;
label2.fill = 'red';
後から文字を変更できます。
// ラベル文字変更
this.onpointstart = function() {
label.text = 'Money is time';
};
文字列に応じたラベルの大きさを計測することができます。
var height = label.calcCanvasHeight();
var width = label.calcCanvasWidth();
3. 画像データの描画(Sprite)
画像描画の基本は Sprite です13。
Sprite は、Shape と同じ基底クラスを継承していますので、共通のプロパティやメソッドを使用することができます。
3-1. 画像データを読み込んで表示する
画像データを表示するには、(1)事前にアセットの定義し、(2)ゲームエンジンで読み込んでから、(3)シーン内で表示します。
アセット定義では、image の連想配列にファイル名を登録します。
// アセット
var ASSETS = {
// 画像
image: {
'tomapiko': 'https://cdn.jsdelivr.net/gh/phinajs/phina.js@develop/assets/images/tomapiko.png',
},
};
GameAppの生成時に、assets要素に定義したオブジェクトを設定します。
phina.main(function() {
var app = GameApp({
startLabel: 'main',
// アセット読み込み
assets: ASSETS,
});
app.run();
});
シーン内で表示するときには、Spriteのパラメータに設定したラベルを入れるだけで大丈夫です。
Sprite('tomapiko').addChildTo(this).setPosition(320, 480);
3-2. 回転・拡大率・サイズ・透明・反転など
sp2.rotation = 45;
Sprite('tomapiko').addChildTo(this).setPosition(320, 700).setRotation(15);
sp1.width = 128;
Sprite('tomapiko').addChildTo(this).setPosition(320, 480).setSize(128, 128);
向きを反転するには、ScaleX, ScaleY に負の数を入れます。
// 画面タッチ時処理
this.onpointstart = function() {
// 横向き反転
sprite.scaleX *= -1;
};
3-3. アニメーションとスプライトシート
Spriteは、スプライトシートを元にアニメーションを設定できます。
スプライトシート画像とは、アニメーション用のコマをシートの様に並べた画像です。
アセットでスプライトシートの情報を登録しておきます。
// アセット
var ASSETS = {
// 画像
image: {
'tomapiko': 'https://cdn.jsdelivr.net/gh/phinajs/phina.js@develop/assets/images/tomapiko_ss.png',
},
// スプライトシート
spritesheet: {
"tomapiko_ss":
{
// フレーム情報
"frame": {
"width": 64, // 1フレームの画像サイズ(横)
"height": 64, // 1フレームの画像サイズ(縦)
"cols": 6, // フレーム数(横)
"rows": 3, // フレーム数(縦)
},
// アニメーション情報
"animations" : {
"walk": { // アニメーション名
"frames": [12,13,14], // フレーム番号範囲
"next": "walk", // 次のアニメーション
"frequency": 6, // アニメーション間隔
},
}
},
}
};
フレームアニメーション設定は外部ファイルとして定義して、アセットとして読み込むことができます。
// アセット
var ASSETS = {
// 画像
image: {
'tomapiko': 'https://cdn.jsdelivr.net/gh/phinajs/phina.js@develop/assets/images/tomapiko_ss.png',
},
// フレームアニメーション情報
spritesheet: {
'tomapiko_ss': 'https://cdn.jsdelivr.net/gh/phinajs/phina.js@develop/assets/tmss/tomapiko.tmss',
},
};
スプライトシートからSpriteを生成するには、width, heightを指定します。
// スプライト画像作成
var sprite = Sprite('tomapiko', 64, 64).addChildTo(this);
登録したフレームアニメーションをアタッチすると、アニメーションを開始できます。
// スプライトにフレームアニメーションをアタッチ
var anim = FrameAnimation('tomapiko_ss').attachTo(sprite);
// アニメーションを指定する
anim.gotoAndPlay('walk');
フレームアニメーションは update で自動で管理されています。
爆発など、アニメーション終了後に消える処理では、anim.finished で判定します14。
// 毎フレーム処理
update: function() {
// アニメーションが終わったら自身を消去
if (this.anim.finished) { // ←ここで判定
this.remove();
console.log('removed');
}
},
4. 入力を受け付ける(イベント)
ユーザーの入力を受け付ける主な方法として、
update(app) 15と
// 更新
update: function(app) {
var p = app.pointer;
if (p.getPointing()) {
var diff = this.player.x - p.x;
if (Math.abs(diff) > SPEED) {
if (diff < 0) {
this.player.x += SPEED;
this.player.scaleX = -1;
} else {
this.player.x -= SPEED;
this.player.scaleX = 1;
}
/* ... */
onpointstartなどオブジェクトへのイベント16があります。
this.onpointstart = function(e) {
var p = e.pointer;
var wave = Wave().addChildTo(this);
wave.x = p.x;
wave.y = p.y;
};
4-1. タッチイベント
マウス操作は「タッチイベント」として処理されます17。
オブジェクトへのタッチ処理を定義する。
// タッチ可能にする
shape.setInteractive(true);
// タッチイベント登録
// シーンにタッチイベント登録
this.onpointstart = function(e) {
// タッチされた座標を調べる
var tx = e.pointer.x;
var ty = e.pointer.y;
// タッチされた座標にShapeを移動
shape.setPosition(tx, ty);
};
パラメータ e にタッチイベントの詳細が入っています。
e.pointer からタッチされた座標を参照できます。
4-2. app.keyboard
update中に、app.keyboardを取得すると、入力中のキーが取得できます。
// 毎フレーム処理
this.update = function(app) {
var key = app.keyboard;
// 移動
if (key.getKey('left')) {
shape.x -= SPEED;
}
// キーダウン
if (key.getKeyDown('right')) {
label1.text = 'getKeyDown: right';
}
// キーアップ
}
if (key.getKeyUp('up')) {
label2.text = 'getKeyUp: up';
}
};
矢印キーの入力は、角度やベクトルでも取得できます。
// キーの方向をangleで取得
var angle = key.getKeyAngle();
label4.text = 'angle:' + angle; // left >> 180
// キーの方向を正規化した値eで取得
var direction = key.getKeyDirection();
label5.text = 'direction:' + direction; // left >> {x:-1, y:0}
4-3. キーイベントの処理を追加
オブジェクトにキーイベント時の操作を追加することもできます。
// キーアップイベント
this.onkeyup = function(e) {
label3.text = 'onkeyup: ' + e.keyCode + ':' + String.fromCharCode(e.keyCode);
};
(補足)
- Spriteクラスを継承して独自のクラスを作る|phina.js Tips集 下巻
- <シェイプ編>|phina.js Tips集 上巻
- Shapeをシーンに表示する|phina.js Tips集 上巻
- Shapeを透明・非表示にする|phina.js Tips集 上巻
- Shapeの位置を指定する|phina.js Tips集 上巻
- Shapeのサイズを指定する|phina.js Tips集 上巻
- Shapeを回転させる|phina.js Tips集 上巻
- Shapeを拡大・縮小させる|phina.js Tips集 上巻
- Shapeの背景色を指定する|phina.js Tips集 上巻
- Shapeの原点を変更する|phina.js Tips集 上巻
- Element::remove –phina.js/element.js at develop · phinajs/phina.js · GitHub
- 【phina.js】Gridクラスを使いこなそう
- <スプライト編>|phina.js Tips集 上巻
- https://runstant.com/alkn203/projects/6c71743a
- Walk Tomapiko – phina.js
- Touch Game – phina.js
- <イベント編>|phina.js Tips集 上巻