ManagerSceneでゲームの流れを管理しよう

phina.js Advent Calendar 2015の2日目です。

phiさん 前 ←→ 次 emadurandalさん

シューティングゲーム作るよ!

新進気鋭の国産JavaScriptゲームエンジン「tmlib.js」の後継である「phina.js」がリリースされました

さっそくですが、phina.jsの紹介+サンプル提供を兼ねてシューティングゲームを開発中です。

PhinaShooter

まだまだ開発途上ですが、phina.jsの新機能をふんだんに盛り込んでいきたいと考えています。

ManagerSceneを使ってシーン遷移をスッキリ管理

本ゲームはたくさんのシーンを次々に遷移しながら実行されます。

開発にあたる際、たとえばステージ3の道中中盤をデザインしている時に、テストのためにわざわざタイトル画面から実際にプレイするのは大変ですよね。

シーンごとに独立して製作し、あとで組み合わせる方法をとるのがスマートです。

そんな時に役立つのがphina.game.ManagerSceneです。

ManagerSceneの基本的な使い方

シーンAからシーンBへ遷移し、その後再びシーンAに戻ってくるような流れを作りたい場合、以下のようなコードになります。

  • SceneSequence.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// SceneSequenceクラス
phina.define("SceneSequence", {
// phina.game.ManagerSceneを継承します
superClass: "phina.game.ManagerScene",
// 初期化
init: function() {
this.superInit({
scenes: [
// A
{
label: "シーンA", // ラベル。参照用
className: "SceneA", // シーンAのクラス名
},
// B
{
label: "シーンB",
className: "SceneB",
nextLabel: "シーンA" // シーン終了時に次に遷移するシーンのラベル
}
]
});
}
});
  • SceneA.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// シーンクラス
phina.define("SceneA", {
// phina.display.CanvasSceneを継承します
superClass: "phina.display.CanvasScene",
// 初期化
init: function() {
this.superInit();
console.log("これはシーンAです");
},
// 毎フレーム行う処理
update: function(app) {
// クリックされたら
if (app.pointing.getPointingEnd()) {
// exitメソッドでシーンを終了させます
this.exit();
}
}
});

(SceneB.jsは省略)

Sceneクラス内で exit() メソッドを呼び出すことにより、そのシーンを終了させることが出来ます。

終了後は、

  • nextLabelで指定されたシーン

nextLabel指定がなければ

  • ManagerScene内で次に書かれているシーン

に遷移します。

条件分岐

特定の条件を満たす場合のみ遷移先を変えたい時もありますね。

たとえば、ゲーム中に3回ミスをしてしまったら次のステージではなくゲームオーバーシーンに遷移させたい場合などです。

そういった時は以下のように記述します。

  • SettingScene.js
1
2
3
4
5
6
7
8
9
...
scenes: [
...
// ゲームオーバーシーンを追加
{
label: "ゲームオーバー",
className: "GameOverScene"
}
...
  • SceneA.js
1
2
3
4
5
6
7
8
9
10
11
12
...
// ミスをした時の処理
miss: function() {
this.missCount += 1;
if (this.missCount >= 3) {
// ManagerScene側で設定したラベルを指定します
this.exit("ゲームオーバー");
}
}
...

exit() メソッドに引数としてラベル名を渡すことで、ManagerSceneで設定したラベルを指定してジャンプすることが出来ます。

シーンに引数を渡す

ManagerSceneでは次のシーンへの遷移時、シーンクラスをインスタンス化します。

その際にシーンクラスのコンストラクタへ引数を渡すことが出来ます。

  • SceneSequence.js
1
2
3
4
5
6
7
8
9
10
...
{
label: "ゲームオーバー",
className: "GameOverScene",
// GameOverSceneクラスのコンストラクタに渡すパラメータ
arguments: { message: "死んでしまった!" }
}
...
  • GameOverScene.js
1
2
3
4
5
6
7
8
9
10
11
12
13
phina.define("GameOverScene", {
// phina.display.CanvasSceneを継承します
superClass: "phina.display.CanvasScene",
// 初期化
init: function(param) { // 引数を受け取ります
this.superInit();
// 引数内のmessageプロパティを表示します
console.log(param.message);
},
});

次のシーンに値を渡す

遷移前のシーンから次のシーンに情報を渡すことが出来ます。

ゲームシーン中に獲得したスコアをリザルトシーンで表示する時などに使えますね。

  • SceneA.js
1
2
3
4
5
6
7
8
9
10
11
12
...
// ミスをした時の処理
miss: function() {
this.missCount += 1;
if (this.missCount >= 3) {
// exitの第2引数にオブジェクトを渡します
this.exit("ゲームオーバー", { score:this.score });
}
}
...
  • GameOverScene.js
1
2
3
4
...
// 引数内のmessageプロパティとscoreプロパティを表示します
console.log(param.message, param.score);
...

exit()メソッドの第2引数に渡したオブジェクトとManagerSceneで設定したargumentsオブジェクトはマージされます。

入れ子構造

ManagerScene自体もまたSceneのサブクラスですので、ManagerSceneから別のManagerSceneを呼び出すことも可能です。

ステージ1から最後までプレイするアーケードモードと、ステージを選んでプレイする練習モードを実装したい場合などに利用できます。

サンプル

  • ゲーム全体の流れ
  • アーケードモードの流れ
  • 練習モードの流れ
  • PhinaShooter.js(抜粋)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
/* メインシーケンス */
phina.define("ps.MainSequence", {
superClass: "phina.game.ManagerScene",
init: function() {
this.superInit({
scenes: [
{
label: "load",
className: "ps.LoadingScene",
arguments: { stageId:0 },
},
{
label: "title",
className: "ps.TitleScene",
},
// アーケードモード
{
label: "arcadeMode",
className: "ps.ArcadeModeSequence",
nextLabel: "title",
},
// 練習モード
{
label: "practiceMode",
className: "ps.PracticeModeSequence",
nextLabel: "title",
},
{
label: "tutorial",
className: "ps.TutorialScene",
nextLabel: "title",
},
{
label: "setting",
className: "ps.SettingScene",
nextLabel: "title",
},
{
label: "ranking",
className: "ps.RankingScene",
nextLabel: "title",
},
],
});
}
});
/* アーケードモード */
phina.define("ps.ArcadeModeSequence", {
superClass: "phina.game.ManagerScene",
init: function() {
this.superInit({
scenes: [
{
label: "stage1preload",
className: "ps.LoadingScene",
arguments: { stageId:1 },
},
{
label: "stage1",
className: "ps.GameScene",
arguments: { stageId:1 },
},
{
label: "stage1result",
className: "ps.ResultScene",
},
{
label: "stage2preload",
className: "ps.LoadingScene",
arguments: { stageId:2 },
},
{
label: "stage2",
className: "ps.GameScene",
arguments: { stageId:2 },
},
{
label: "stage2result",
className: "ps.ResultScene",
},
{
label: "stage3preload",
className: "ps.LoadingScene",
arguments: { stageId:3 },
},
{
label: "stage3",
className: "ps.GameScene",
arguments: { stageId:3 },
},
{
label: "stage3result",
className: "ps.ResultScene",
},
{
label: "ending",
className: "ps.EndingScene",
},
{
label: "gameover",
className: "ps.GameoverScene",
},
{
label: "nameEntry",
className: "ps.NameEntryScene",
},
],
});
},
onfinish: function() {
this.exit();
}
});
/* 練習モード */
phina.define("ps.PracticeModeSequence", {
superClass: "phina.game.ManagerScene",
init: function() {
var sharedData = {};
this.superInit({
scenes: [
{
label: "stageSelect",
className: "ps.StageSelectScene",
arguments: sharedData,
},
{
label: "preload",
className: "ps.LoadingScene",
arguments: sharedData,
},
{
label: "stage",
className: "ps.GameScene",
arguments: sharedData,
},
{
label: "result",
className: "ps.ResultScene",
},
],
});
},
onfinish: function() {
this.exit();
}
});

まとめ

今回紹介したように、ManagerSceneを使ってシーン管理をすることで、アプリケーション全体をシンプルでスマートかつ変更に強い構造にすることが出来ます。

ぜひ使ってみてください。