phina.jsではアプリの更新処理は1/30秒(fps設定により変動)ごとにループ実行されます。しかし、たまに任意のタイミングでこの更新処理を実行したい時があったのでその方法などを備忘録として残しておきます。
方法
コアのappオブジェクトの_loop
メソッドを直接実行すればとりあえず目的は達成できます。
phina.define('MainScene', {
superClass: 'phina.display.DisplayScene',
init: function(options) {
this.superInit(options);
/* ここで色々処理... */
this.on('enter', ()=> {
this.app._loop();
});
},
});
注意点としてはSceneインスタンス化(init関数内)の段階ではappにアクセスできないため、初期化の際に処理を行いたい場合はenterイベントのコールバックで行う必要があります。
より細かく処理を分けたい時
_loop
関数は各要素のupdateだけでなく描画処理も行います。しかし描画は必要無い、あるいは逆に描画だけでしたいということもあります。
その場合は_loop
の内部で実行される_update
、_draw
関数を個別に実行します。
this.on('enter', ()=> {
// this.app._loop(); // 更新+描画
this.app._update(); // 更新のみ
this.app._draw(); // 描画のみ
});
なお_draw
はシーン切り替え(pushScene)の際に自動で実行されるので、普通は初期化中に行う必要はありません。(enterイベントの後に実行)
応用例:フレームスキップ機能
これらを応用して指定したフレーム分、ゲームを進めるといったこともできます。
STGなど時間経過で進行するタイプのゲームのデバッグ作業に使えます。
skipFrame: function(frameNum) {
for (let i = 0; i < frameNum; i++) {
// 途中の描画処理は無駄なので更新のみ(※)
this.app._update();
}
// 仕上げに描画
this.app._draw();
}
(※) _drawは要素の位置に関する計算も行うので、当たり判定処理が絡む場合は注意が必要。
またapp.frame値が実行ごとに加算されることにも注意。
所感
privateっぽいメソッドをいじるのはどうなの?と思わないでもないですが、デフォルトでは他に方法はなさそうなのでこれで頑張ります。
余談1:_loopの細かい内部処理
- App、現在のscene(currentScene)、およびscene子要素それぞれのupdate/enterframeコールバックを実行(
_update
) - 現在のsceneのInteractionチェック(pointstartなどの発火)
- 描画処理(
_draw
) - stats.jsが動いているときはstatsの更新
余談2:phina.jsはどうやってループ処理をしている?
原動機となっているのがAppクラスのメンバであるtickerオブジェクト。
tickerはAppのrunメソッドを実行した段階で恒久ループに入り、一定時間ごと(fps準拠)にtickメソッドを実行、それがAppの_loopメソッドをトリガーしています。その後は上で説明した通り。