phina.js が面白そうなのでゲームでも作ってみるか
ハマった所
spritesheetの使い方がよく解らず難しかった。
スプライトシートのような複数画像をまとめた画像ファイルは、アニメーションする必要が無くてもアニメーション情報を設定する必要があるのかな?
アニメーションしない画像を1つにまとめるな!という方針なんだろうか?
とりあえず、毎フレーム同じ画像を表示するようにしといた。
画面サイズの取得方法が不明
サンプルでは、MainScene の処理内で MainScene のサイズを取ってたりするんだけど、SpriteをsuperClassにしたクラスを作ったときに、画面サイズからはみ出たかどうかの判定をどうすれば良いのか解らなかった。
updateで渡されるappを参照すればGameAppの情報が取れることは解ったので、app.canvas.width、app.canvas.height で画面サイズを取ることにしたけどこれで正しいのか不明。使ってるシーンとか取れそうだと思うんだけど、プロパティがよく解らない。
みんな、こういうAPI周りってどうやって理解しているんだろう?
→ってなことをTwitterで呟いたら、this.parent で addChildTo の追加先が取れますよ!と教えて頂いたので修正。
SpriteをsuperClassにしたクラス内で、this.parent.width とすれば画面の横幅が取れました。
感動した所
Sprite(っていうか描画オブジェクト全般)に
this.top、this.bottom、this.left、this.right
なプロパティがあること。
top座標は this.y - (this.height / 2) なんていう式を公式のように使ってたけど、this.top だけで良いなんて!
しかも、this.top = 0 ってすれば、画面上部ぎりぎりに表示されるようにy座標を設定してくれる。
##今日の成果
自機をカーソルキーで動かす所までできた。
phina.js Tips集 https://qiita.com/alkn203/items/bca3222f6b409382fe20 を見ながら作ってます。
今日の成果をここに上げました
http://hirotyan.my.coocan.jp/phinajs/Shooting/001/index.html
// phina.js をグローバル領域に展開
phina.globalize();
// アセット
var ASSETS = {
// 画像
image: {
'ship': 'ship.gif',
},
spritesheet: {
"ship_ss":
{
// フレーム情報
"frame": {
"width": 32,
"height": 32,
"cols": 4,
"rows": 1,
},
// アニメーション情報
"animations" : {
"center": { // アニメーション名
"frames": [1], // フレーム番号範囲
"next": "center", // 次のアニメーション
"frequency": 1, // アニメーション間隔
},
"left": { // アニメーション名
"frames": [0], // フレーム番号範囲
"next": "left", // 次のアニメーション
"frequency": 1, // アニメーション間隔
},
"right": { // アニメーション名
"frames": [2], // フレーム番号範囲
"next": "right", // 次のアニメーション
"frequency": 1, // アニメーション間隔
},
}
},
},
};
// MainScene クラスを定義
phina.define('MainScene', {
superClass: 'DisplayScene',
init: function() {
this.superInit();
// 背景色を指定
this.backgroundColor = '#000000';
// スプライトを作成
var ship = Ship().addChildTo(this);
// 初期位置
ship.x = this.width / 2;
ship.y = this.height * 4 / 5;
},
});
// 自機クラス
phina.define('Ship', {
// Spriteを継承
superClass: 'Sprite',
// 初期化
init: function() {
// 親クラスの初期化
this.superInit('ship', 32, 32);
// SpriteSheetをスプライトにアタッチ
var anim = FrameAnimation('ship_ss').attachTo(this);
// スプライトシートのサイズにフィットさせない
anim.fit = false;
//アニメーションを再生する
anim.gotoAndPlay('center');
// サイズ変更
this.setSize(32*2, 32*2);
// 参照用
this.anim = anim;
},
update: function(app) {
var key = app.keyboard;
// 上下左右移動
this.anim.gotoAndPlay('center');
var speed = 16;
if (key.getKey('left')) {
this.x -= speed;
this.anim.gotoAndPlay('left');
}
if (key.getKey('right')) {
this.x += speed;
this.anim.gotoAndPlay('right');
}
if (key.getKey('up')) {
this.y -= speed;
}
if (key.getKey('down')) {
this.y += speed;
}
// 端まで行った場合は動かさない
if (this.left < 0) {
this.left = 0;
} else if (this.right > this.parent.width) {
this.right = this.parent.width;
}
if (this.top < 0) {
this.top = 0;
} else if (this.bottom > this.parent.height) {
this.bottom = this.parent.height;
}
},
});
// メイン処理
phina.main(function() {
// アプリケーション生成
var app = GameApp({
startLabel: 'main', // メインシーンから開始する
// アセット読み込み
assets: ASSETS,
});
// アプリケーション実行
app.run();
});