シンプルなゲームの作成 - パート3
完成されたプロジェクトはこちらです。先にその1とその2 を読んでください。
ゲームスクリプトと入力
これらの2つのスクリプトgame.js
と input.js
は、「Game」と呼ばれるシーンのルートエンティティに添付されています。スクリプトは基本的にヒエラルキーで遭遇される順に実行されるので、エンティティ特有でない任意のスクリプトを最初のエンティティに添付するのが最も簡単です。また、エンティティに添付せずに最初にスクリプトを読み込むために、エディタの設定パネルでのスクリプトの読み込み順を管理することができます。
game.js
var Game = pc.createScript('game');
Game.attributes.add('uiMenu', {type: 'entity'});
Game.attributes.add('uiInGame', {type: 'entity'});
Game.attributes.add('uiGameOver', {type: 'entity'});
Game.attributes.add('audio', {type: 'entity'});
Game.STATE_MENU = 'menu';
Game.STATE_INGAME = 'ingame';
Game.STATE_GAMEOVER = 'gameover';
// initialize code called once per entity
Game.prototype.initialize = function() {
this._state = Game.STATE_MENU;
this._score = 0;
this.setResolution();
window.addEventListener("resize", this.setResolution.bind(this));
// UI からのイベントをリッスンする
this.app.on("ui:start", this.start, this);
this.app.on("ui:reset", this.reset, this);
};
Game.prototype.setResolution = function () {
// 画面の幅が 640 未満の場合は画面いっぱいに表示する
// それ以外はデフォルト設定を使用する
var w = window.screen.width;
var h = window.screen.height;
if (w < 640) {
this.app.setCanvasResolution(pc.RESOLUTION_AUTO, w, h);
this.app.setCanvasFillMode(pc.FILLMODE_FILL_WINDOW);
}
};
// MENU から INGAME に移動するために呼び出す
Game.prototype.start = function () {
this._state = Game.STATE_INGAME;
this.app.fire("game:start");
this.uiMenu.enabled = false;
this.uiInGame.enabled = true;
this.audio.sound.play("music");
};
// INGAME から GAMEOVER に移動するために呼び出す
Game.prototype.gameOver = function () {
this._state = Game.STATE_GAMEOVER;
this.app.fire("game:gameover");
this.uiInGame.enabled = false;
this.uiGameOver.enabled = true;
this.audio.sound.stop();
this.audio.sound.play("gameover");
};
// GAMEOVER から MENU に移動するために呼び出す
Game.prototype.reset = function () {
this.app.fire("game:reset");
this.resetScore();
this._state = Game.STATE_MENU;
this.uiGameOver.enabled = false;
this.uiMenu.enabled = true;
this.audio.sound.stop();
};
// 現在のスコアを返す
Game.prototype.getScore = function () {
return this._score;
};
// スコアを加算する
Game.prototype.addScore = function (v) {
this._score += v;
this.app.fire("game:score", this._score);
};
// スコアをリセットする
Game.prototype.resetScore = function () {
this._score = 0;
this.app.fire("game:score", this._score);
};
ゲームの状態
ゲームスクリプトは、ゲームの全体的な状態を管理し、ゲームの状態を変更するためのメソッドを公開し、ゲームの状態が変更されたことを他のコードに通知するためのイベントを発行します。
ゲームには3つの主要の状態、メニュー、ゲーム内、ゲームオーバーがあります。start()
, gameOver()
, reset()
のゲームスクリプトを使用して各状態間を移行します。それぞれが現在の状態を記憶するために_state
変数を設定し、状態の変化を他のスクリプトに通知するためにアプリケーションイベントを発生させ、ユーザインタフェース要素のオン・オフを切り替え、音楽やゲームオーバーの効果音の状態を管理します。
これらの状態の変更メソッドは、適切なトリガーイベントが発生した場合に他のスクリプトから呼び出されます。例えば、gameOver()
メソッドはボールが画面下から出た時に ball.js
によって呼び出されます。
アプリケーションイベント
ゲームスクリプトがどのようにアプリケーションでイベントを発生させるかを確認してみましょう。
this.app.fire("game:start")
イベントは、一つのスクリプトを他のスクリプトと通信させるための非常に便利な方法です。オブジェクトがイベントの発生を選択します(この場合はthis.app
)。オブジェクトへのアクセス権を持つ他のコードは、このオブジェクトのイベントにリッスンすることができ、イベントが発生したとき、コードに通知が送られます。
ここで問題となのは、コードがイベントにリッスンし始めるために、オブジェクトにアクセスする必要があるということです。アプリケーションイベントが非常に便利なのはこのためです。PlayCanvas内のすべてのスクリプトにthis.app
へのアクセス権があります。それは他のスクリプトとの間の中央通信ハブとして機能させることで有用になります。
イベントを明確にし、衝突を避けるために、名前空間パターンを使用しています。上記のgame:start
イベントをリッスンする場合、次のコードを使用します:
this.app.on("game:start", function () {
console.log("game:start event was fired");
}, this)
スコア
ゲームスクリプトは現在のスコアも管理します。これは、スコアを変更するためのメソッドを公開し、スコアが変更したことを他のコードに知らせるためにイベントを発生させます。
Resolution
最後に、モバイルとデスクトップの両方でメインキャンバスが正しいサイズになることを確認するために、ゲームのスクリプトは解像度の初期の選択を処理します。モバイルでは(640ピクセル未満の画面)、ゲームが画面全体を埋めます。デスクトップでは、プロジェクト設定で設定した定義済みの解像度を使用します。