9章 : タイトル・ゲームオーバー画面を作ってみよう
ここまでの実装でシューティングとしてほぼほぼ完成形になりました。
あと一息,タイトル画面や死亡時のゲームオーバー画面を作ればもうゲームとして成立してしまいます。
タイトル画面の追加
タイトルノードの実装
まず以下のコードを追加してタイトル画面を作りましょう。
+ using Altseed2;
+
+ namespace Tutorial
+ {
+ // タイトル画面
+ public class TitleNode : Node
+ {
+ // 画面が遷移中かどうか
+ private bool fading = false;
+
+ // エンジンに追加された時に実行
+ protected override void OnAdded()
+ {
+ // タイトル
+ var titleText = new TextNode();
+ // タイトルのフォントを読み込む
+ titleText.Font = Font.LoadDynamicFontStrict("Resources/GenYoMinJP-Bold.ttf");
+ // タイトルの文字のサイズを設定
+ titleText.FontSize = 100;
+ // タイトルの文字を設定
+ titleText.Text = "Tutorial STG";
+ // タイトルの座標を設定
+ titleText.Position = new Vector2F(Engine.WindowSize.X / 2, 100f);
+ // タイトルの中心座標を設定
+ titleText.CenterPosition = titleText.ContentSize / 2;
+
+ // タイトルを追加
+ AddChildNode(titleText);
+
+ // 画面下に表示される案内
+ var announce = new TextNode();
+ // 案内のフォントを読み込む
+ announce.Font = Font.LoadDynamicFontStrict("Resources/GenYoMinJP-Bold.ttf");
+ // 案内の文字のサイズを設定
+ announce.FontSize = 50;
+ // 案内の文字を設定
+ announce.Text = "Press Z to start";
+ // 案内の座標を設定
+ announce.Position = new Vector2F(Engine.WindowSize.X / 2, 600f);
+ // 案内の中心座標を設定
+ announce.CenterPosition = announce.ContentSize / 2;
+
+ // 案内を追加
+ AddChildNode(announce);
+
+ // 背景のテクスチャ
+ var backTexture = new SpriteNode();
+ // 背景のテクスチャを読み込む
+ backTexture.Texture = Texture2D.LoadStrict("Resources/image.png");
+ // 背景の表示位置を奥に設定
+ backTexture.ZOrder = -1;
+
+ // 背景を追加
+ AddChildNode(backTexture);
+ }
+
+ // フレーム毎に実行
+ protected override void OnUpdate()
+ {
+ // 画面が遷移中でなく,Zキーが押された時に実行
+ if (!fading && Engine.Keyboard.GetKeyState(Key.Z) == ButtonState.Push)
+ {
+ // エンジンから自身を削除
+ Engine.RemoveNode(this);
+
+ // エンジンにメイン画面を追加
+ Engine.AddNode(new MainNode());
+
+ // 画面遷移中のフラグを立てる
+ fading = true;
+ }
+ }
+ }
+ }
基本的に実装は今迄行ってきたような,SpriteNode
やTextNode
を用いた画像/文字の描画です。
ここで注意する点が2つあります。
1つ目は
// エンジンから自身を削除
Engine.RemoveNode(this);
// エンジンにメイン画面を追加
Engine.AddNode(new MainNode());
の部分です。
第4章にて,Program.cs
でEngine.AddNode(Node)
は書きましたね。
ここでは,タイトル画面でZキーを押したときメインのシューティング画面に変更する処理を行っています。
タイトル画面にいるときは,ノードの親子関係はこのようになっています。
此処におけるRoot
は,Engine
内で定義されている全てのノードの祖先となるノードです。
TitleNode
の子ノードとして,タイトルのテキストや案内の文字,背景のテクスチャがあります。
この状態でEngine.RemoveNode(TitleNode)
を行うとノードの親子関係はこのようになります。
Root
とTitleNode
の親子関係が解消され,TitleNode
に登録されているテキストやテクスチャがウィンドウに描画されなくなります。
Root.RemoveNode(TitleNode)
とやっていることに違いはありません。
次にEngine.AddNode(MainNode)
を行う事でノードの親子関係は次のようになります。
Root
の子にMainNode
が追加され,MainNode
での実装が実行されます。
前の章で実装してきたシューティングゲーム本体の内容ですね。
此処で大事なのは,Engine.Rootの子孫になったノードに描画や更新処理が適用されるという事です。
描画や更新をしたくないノードはRemove
して,描画や更新を実行したいノードをAdd
するようにしましょう。
2つ目は
// 画面が遷移中かどうか
private bool fading = false;
の部分です。
この変数は,タイトル画面でZキーを押したときにtrue
に変更されます。
// 画面が遷移中でなく,Zキーが押された時に実行
if (!fading && Engine.Keyboard.GetKeyState(Key.Z) == ButtonState.Push)
{
そして,true
になるとこのif文の条件の様に,もう一度Zキーを押してもif文の内容が実行されなくなります。
これは,Zキー連打によってMainNode
がRoot
に複数追加されるのを防ぎたいという事です。
タイトルノードへの遷移を追加
タイトルを実装したので,実際にゲームを始めた時に最初に表示されるようにしてみましょう。
Program.cs
のコードを次のように書き換えてみましょう。
namespace Tutorial
{
class Program
{
[STAThread]
static void Main(string[] args)
{
// エンジンを初期化
Engine.Initialize("Tutorial", 960, 720);
+ // タイトル画面をエンジンに追加
+ Engine.AddNode(new TitleNode());
- // メイン画面をエンジンに追加
- Engine.AddNode(new MainNode());
// メインループ
while (Engine.DoEvents())
{
…以下略…
これで最初に表示されるノードがMainNode
からTitleNode
に変わりました。
ゲームオーバー画面の追加
次は死亡時にゲームオーバー画面を表示してみましょう。
TitleNode
と同様に実装します。
+ using Altseed2;
+
+ namespace Tutorial
+ {
+ // ゲームオーバー画面
+ public class GameOverNode : Node
+ {
+ // 画面が遷移中かどうか
+ private bool fading = false;
+
+ // エンジンに追加された時に実行
+ protected override void OnAdded()
+ {
+ // タイトル
+ var titleText = new TextNode();
+ // タイトルのフォントを読み込む
+ titleText.Font = Font.LoadDynamicFontStrict("Resources/GenYoMinJP-Bold.ttf");
+ // タイトルの文字のサイズを設定
+ titleText.FontSize = 100;
+ // タイトルの文字を設定
+ titleText.Text = "Game Over";
+ // タイトルの座標を設定
+ titleText.Position = new Vector2F(Engine.WindowSize.X / 2, 100f);
+ // タイトルの中心座標を設定
+ titleText.CenterPosition = titleText.ContentSize / 2;
+ // タイトルを追加
+ AddChildNode(titleText);
+
+ // 画面下に表示される案内
+ var announce = new TextNode();
+ // 案内のフォントを読み込む
+ announce.Font = Font.LoadDynamicFontStrict("Resources/GenYoMinJP-Bold.ttf");
+ // 案内の文字のサイズを設定
+ announce.FontSize = 50;
+ // 案内の文字を設定
+ announce.Text = "Press Z to go title";
+ // 案内の座標を設定
+ announce.Position = new Vector2F(Engine.WindowSize.X / 2, 600f);
+ // 案内の中心座標を設定
+ announce.CenterPosition = announce.ContentSize / 2;
+ // 案内を追加
+ AddChildNode(announce);
+ }
+
+ // フレーム毎に実行
+ protected override void OnUpdate()
+ {
+ // 画面が遷移中でなく,Zキーが押された時に実行
+ if (!fading && Engine.Keyboard.GetKeyState(Key.Z) == ButtonState.Push)
+ {
+ // エンジンから自身を削除
+ Engine.RemoveNode(this);
+
+ // エンジンにタイトル画面を追加
+ Engine.AddNode(new TitleNode());
+
+ // 画面遷移中のフラグを立てる
+ fading = true;
+ }
+ }
+ }
+ }
次に,MainNode
に,ゲームオーバーへの遷移メソッドを実装します。
using Altseed2;
using System.Collections.Generic;
namespace Tutorial
{
// メインステージのクラス
public class MainNode : Node
{
...略...
// スコアを表示するノード
private TextNode scoreNode;
+ // 他画面へ遷移しているかどうか
+ private bool fading = false;
// スコア
public int score;
...略...
+ // ゲームオーバー画面に遷移
+ public void ToGameOver()
+ {
+ // BGMをフェードアウト
+ if (bgmID.HasValue)
+ {
+ Engine.Sound.FadeOut(bgmID.Value, 1.0f);
+
+ // BGMが止まったのでIDをnullに
+ bgmID = null;
+ }
+
+ // 画面遷移中でないなら遷移処理を実行
+ if (!fading)
+ {
+ // 自身をエンジンから削除
+ Engine.RemoveNode(this);
+
+ // エンジンにゲームオーバー画面を追加
+ Engine.AddNode(new GameOverNode());
+
+ // 遷移中フラグを縦立てる
+ fading = true;
+ }
+ }
// 敵召還関連
private void UpdateStage()
...略...
}
}
これで,ToGameOver()
メソッドを呼び出すことでゲームオーバー画面に遷移できるようになりました。
ゲームオーバーの呼び出しはプレイヤーが死亡したとき,つまりプレイヤーが敵/敵弾に衝突したときに呼び出せば良いですね。
それでは,以下のようにPlayer.cs
にて呼び出してみましょう。
using Altseed2;
namespace Tutorial
{
// プレイヤーのクラス
public class Player : CollidableObject
{
...略...
// 衝突時に実行
protected override void OnCollision(CollidableObject obj)
{
// 衝突対象が敵か敵の弾だったら
if (obj is Enemy || obj is EnemyBullet)
{
// 死亡音を読み込む
var deathSound = Sound.LoadStrict("Resources/Explosion.wav", true);
// 死亡音を再生
Engine.Sound.Play(deathSound);
// 自身を親から削除
Parent.RemoveChildNode(this);
+ // ゲームオーバーに遷移
+ mainNode.ToGameOver();
}
}
...略...
これでプレイヤーが敵/敵弾に衝突したときにゲームオーバー画面が呼び出されるようになりました。
// BGMをフェードアウト
if (bgmID.HasValue)
{
Engine.Sound.FadeOut(bgmID.Value, 1.0f);
// BGMが止まったのでIDをnullに
bgmID = null;
}
この部分ではBGMのフェードアウトを行っています。
SoundMixer.FadeOut(int id, float seconds)
は,指定した音を指定した秒数でフェードアウトするというものです。
BGMが再生中,つまりbgmID
がnullじゃないとき(=bgmID.HasValue
がtrue
のとき)にフェードアウトを行い,bgmID
をnull
にします。ここでbgmID
をnull
にすることでbgmID.HasValue
がfalse
となるため,フェードアウト処理は一度だけしか行われません。
クリア画面の追加
クリア画面も追加してみましょう。
TitleNode
やGameOverNode
と同じ要領でクリア画面も作ってみましょう。
+ using Altseed2;
+
+ namespace Tutorial
+ {
+ // クリア画面
+ public class LevelCompletedNode : Node
+ {
+ // 画面遷移中かどうか
+ private bool fading = false;
+
+ // エンジンに追加された時に実行
+ protected override void OnAdded()
+ {
+ // タイトル
+ var titleText = new TextNode();
+ // タイトルのフォントを読み込む
+ titleText.Font = Font.LoadDynamicFontStrict("Resources/GenYoMinJP-Bold.ttf");
+ // タイトルの文字のサイズを設定
+ titleText.FontSize = 100;
+ // タイトルの文字を設定
+ titleText.Text = "Clear!";
+ // タイトルの座標を設定
+ titleText.Position = new Vector2F(Engine.WindowSize.X / 2, 100f);
+ // タイトルの中心座標を設定
+ titleText.CenterPosition = titleText.ContentSize / 2;
+ // タイトルを追加
+ AddChildNode(titleText);
+
+ // 画面下の案内
+ var announce = new TextNode();
+ // 案内のフォントを読み込む
+ announce.Font = Font.LoadDynamicFontStrict("Resources/GenYoMinJP-Bold.ttf");
+ // タイトルの文字のサイズを設定
+ announce.FontSize = 50;
+ // 案内のテキストを設定
+ announce.Text = "Press Z to go title";
+ // 案内の座標を設定
+ announce.Position = new Vector2F(Engine.WindowSize.X / 2, 600f);
+ // 案内の中心座標を設定
+ announce.CenterPosition = announce.ContentSize / 2;
+ //案内を追加
+ AddChildNode(announce);
+ }
+
+ // フレーム毎に実行
+ protected override void OnUpdate()
+ {
+ // 画面遷移中でなく,かつZキーが押された時に実行
+ if (!fading && Engine.Keyboard.GetKeyState(Key.Z) == ButtonState.Push)
+ {
+ // エンジンから自身を削除
+ Engine.RemoveNode(this);
+
+ // エンジンにタイトル画面を追加
+ Engine.AddNode(new TitleNode());
+
+ // 画面遷移中のフラグを立てる
+ fading = true;
+ }
+ }
+ }
+ }
同様に,MainNode.cs
にもクリア画面への遷移を追加してみましょう。
using Altseed2;
using System.Collections.Generic;
namespace Tutorial
{
// メインステージのクラス
public class MainNode : Node
{
...略...
// 敵召還関連
private void UpdateStage()
{
// カウントが100の倍数だったら
if (count % 100 == 0)
{
// 敵が残っていたら画面に追加
if (enemies.Count > 0)
{
characterNode.AddChildNode(enemies.Dequeue());
}
else
{
+ // もし画面遷移中でなければ実行
+ if (!fading)
+ {
+ // BGMをフェードアウト
+ if (bgmID.HasValue)
+ {
+ Engine.Sound.FadeOut(bgmID.Value, 1.0f);
+
+ // BGMが止まったのでIDをnullに
+ bgmID = null;
+ }
+
+ // エンジンから自身を削除
+ Engine.RemoveNode(this);
+
+ // クリア画面をエンジンに追加
+ Engine.AddNode(new LevelCompletedNode());
+
+ // 画面遷移中フラグを立てる
+ fading = true;
+ }
}
}
}
}
}
これで,ウェーブをクリアしたときに自動的にBGMがフェードアウトされ,クリア画面に遷移します。