ヴァンサバ風ゲームの作り方#11|タイトルとリザルトを付けて、Windowsで遊べるようにしよう

リザルトから「タイトルへ」でタイトルに戻り、「はじめる」でもう一度始まるまでの流れ

いよいよ最終回です。前回まででゲームの中身は完成しました。あとは、遊ぶ人が最初から最後まで迷わずに遊べる形にするだけです。タイトル画面から始まって、終わったら結果が出て、もう一度遊べる。そしてそれを友達に渡せるところまで持っていきます。今回はヴァンサバ風サバイバーゲームのシリーズの11回目(最終回)として、タイトル・リザルト・音を付けて、Windowsで遊べる形にビルドしてみたいと思います!

目次

今回作るもの

  • タイトル画面(「はじめる」「おわる」)
  • リザルト(生き延びた時間・とどいたレベル・「もう一度」「タイトルへ」)
  • 効果音とBGM
  • Windowsのゲーム(.exe)にして、zipで配れる形にする

作るスクリプトは3つ、消すスクリプトが2つです。

スクリプト役割付ける場所
TitleView(新しく作る)「はじめる」でゲームへ、「おわる」で終了TitleのCanvas
ResultView(新しく作る)やられたとき・クリアしたときの結果画面GameのCanvas
SoundPlayer(新しく作る)効果音とBGMを鳴らすSoundPlayer
GameOverView・GameClearView(消す)ResultViewにまとめます—

前回の続きから

  • Unityのバージョンは6.3 LTS
    • 記事作成時点では6000.3.22f1で確認しています
  • #10の状態:時間で敵が強くなり、10分でクリアになる

シーンの切り替えは8-3 シーンの切り替え、音は12-1 音を鳴らそうで紹介しています。

リザルトを作ろう

先に結果画面から作ります。#9のゲームオーバーと#10のクリアは、出すパネルが違うだけのほとんど同じスクリプトでした。前回「違いがはっきりしたらまとめる」と書いたので、ここで1つにします。

  • SurvivorsLike/Scriptsに「ResultView」を作る
using System;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

// やられたとき・クリアしたときの結果画面
public class ResultView : MonoBehaviour
{
    [SerializeField] private PlayerHealth playerHealth;
    [SerializeField] private WaveManager waveManager;
    [SerializeField] private PlayerLevel playerLevel;
    [SerializeField] private GameObject panel;
    [SerializeField] private TextMeshProUGUI titleText;
    [SerializeField] private TextMeshProUGUI detailText;
    [SerializeField] private Button retryButton;
    [SerializeField] private Button titleButton;

    private void OnEnable()
    {
        playerHealth.Died += ShowGameOver;
        waveManager.Cleared += ShowClear;
        retryButton.onClick.AddListener(Retry);
        titleButton.onClick.AddListener(ToTitle);
    }

    private void OnDisable()
    {
        playerHealth.Died -= ShowGameOver;
        waveManager.Cleared -= ShowClear;
        retryButton.onClick.RemoveListener(Retry);
        titleButton.onClick.RemoveListener(ToTitle);
    }

    private void ShowGameOver()
    {
        Show("GAME OVER", new Color(0.9f, 0.25f, 0.2f));
    }

    private void ShowClear()
    {
        Show("CLEAR!", new Color(1f, 0.85f, 0.2f));
    }

    private void Show(string message, Color color)
    {
        titleText.text = message;
        titleText.color = color;
        detailText.text = $"生き延びた時間 {TimeSpan.FromSeconds(waveManager.Elapsed):mm\\:ss}\n"
                        + $"とどいたレベル {playerLevel.Level}";

        panel.SetActive(true);
        Time.timeScale = 0f;
    }

    private void Retry()
    {
        Time.timeScale = 1f;
        SceneManager.LoadScene("Game");
    }

    private void ToTitle()
    {
        Time.timeScale = 1f;
        SceneManager.LoadScene("Title");
    }
}
  • 2つの入口、1つの出口
    • DiedとClearedは別のイベントですが、どちらも「文字と色を変えてパネルを出す」だけです
    • 違うのは見出しの文字と色だけなので、Showに渡す形にまとめました
  • まとめる時期
    • #9の時点でまとめようとしたら、「クリアのときは何を出す?」が決まっていないので、たぶん余計な作りになっていました
    • 違いがはっきりしてからまとめると、こうして短く書けます
  • 読み込むシーンは名前で指定
    • "Game"と"Title"は、SurvivorsLike/Scenesのシーンのファイル名です。名前を変えたときはここも直します

パネルを1つにする

  • HierarchyでGameOverPanelを選び、名前を「ResultPanel」にする
    • 中のGameOverTextを「ResultTitle」にリネーム
    • 中のRetryButtonはそのまま
    • ResultPanelの上で右クリック>UI>Text – TextMeshPro、名前を「ResultDetail」にする
      • Pos Y:0、Width:800、Height:160、Font Size:40、Alignment:中央・中央
    • RetryButtonをCtrl+Dで複製して「TitleButton」にする
      • Pos Y:-250、中のTextを「タイトルへ」にする
  • ClearPanelは削除する
  • CanvasからGameOverViewとGameClearViewを削除し、Add Component>ResultView
    • 8つの欄を入れる
      • Player Health:Player、Wave Manager:WaveManager、Player Level:Player
      • Panel:ResultPanel、Title Text:ResultTitle、Detail Text:ResultDetail
      • Retry Button:RetryButton、Title Button:TitleButton
  • SurvivorsLike/ScriptsのGameOverViewとGameClearViewを削除する
リザルト画面。GAME OVERの下に生き延びた時間ととどいたレベルが並び、「もう一度」「タイトルへ」のボタンがある

タイトル画面を作ろう

シーンを作る

  • SurvivorsLike/Scenesで右クリック>Create>Scene、名前を「Title」にする
  • ダブルクリックで開いて、次を置く
    • Hierarchyで右クリック>UI>Canvas(#6と同じ設定:Screen Space – Overlay、Scale With Screen Size、1920×1080)
    • Canvasの上にUI>Text – TextMeshProを置き、名前を「TitleText」、Textを「スライムちゃんの\nおかしなサバイバー」、Font Size:90、Pos Y:200
      • Width:1400、Height:320、Alignment:中央・中央、Color:こい茶色
    • UI>Button – TextMeshProを2つ置き、「StartButton」(Text:はじめる、Pos Y:-80)と「QuitButton」(Text:おわる、Pos Y:-220)にする
    • どちらもWidth:480、Height:120、中のTextのFont Size:44
    • Main CameraのBackgroundを明るいクリーム色にしておくと、Gameシーンの工房の床と雰囲気がそろいます
  • 一緒に作られたEventSystemは、#6と同じくInput System UI Input Moduleが付いていることを確かめる

スクリプト

  • SurvivorsLike/Scriptsに「TitleView」を作る
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

// タイトル画面。ゲームを始める・終わる
public class TitleView : MonoBehaviour
{
    [SerializeField] private Button startButton;
    [SerializeField] private Button quitButton;

    private void OnEnable()
    {
        startButton.onClick.AddListener(StartGame);
        quitButton.onClick.AddListener(Quit);
    }

    private void OnDisable()
    {
        startButton.onClick.RemoveListener(StartGame);
        quitButton.onClick.RemoveListener(Quit);
    }

    private void StartGame()
    {
        SceneManager.LoadScene("Game");
    }

    private void Quit()
    {
        // エディタでは再生が止まり、ビルドしたゲームでは本当に終了する
#if UNITY_EDITOR
        UnityEditor.EditorApplication.isPlaying = false;
#else
        Application.Quit();
#endif
    }
}
  • CanvasにTitleViewを付けて、2つのボタンを入れる
    • Start Button:StartButton、Quit Button:QuitButton

シーンはScene Listに入れておかないと、SceneManager.LoadSceneで読み込めません

エディタで「はじめる」や「タイトルへ」を押して、couldn't be loaded because it has not been added to the build settingsのようなエラーが出たらこれです。File>Build ProfilesのScene Listに、ProjectウインドからTitleとGameをドラッグして入れておきます。並び順は、このあとの「Windowsのゲームにしよう」で整えます。

Application.Quit()は、エディタでは何も起きません

再生中に押しても止まらないので「効いていない」と思いがちですが、ビルドしたゲームでは正しく終了します。

#if UNITY_EDITORで囲んだ部分は、エディタでだけコンパイルされます。ここでは、エディタなら再生を止める処理に切り替えています。この書き方をしないと、UnityEditorはビルドに含まれないのでビルドのときにエラーになります。

タイトル画面。「スライムちゃんの おかしなサバイバー」と「はじめる」「おわる」のボタン

効果音とBGMを付けよう

音は#1でダウンロードした配布素材のzipに入っていて、取り込んでいればSurvivorsLike/Audioにあります(se_levelup / se_damage / se_gameover / se_clear / bgm_battle)。見当たらないときは、zipの中のSurvivorsLike/Audioを同じ場所に入れてください。

  • Gameシーンを開き直す
  • Hierarchyで右クリック>Create Empty、名前を「SoundPlayer」にする
  • Add Component>Audio Sourceを2つ付ける
    • 1つ目(効果音用):Play On Awake のチェックを外す
    • 2つ目(BGM用):Audio Resource:bgm_battle、Loop にチェック、Volume:0.4
  • SurvivorsLike/Scriptsに「SoundPlayer」を作る
using UnityEngine;

// ほかのスクリプトが出す「合図」を聞いて、音を鳴らす
public class SoundPlayer : MonoBehaviour
{
    [SerializeField] private PlayerLevel playerLevel;
    [SerializeField] private PlayerHealth playerHealth;
    [SerializeField] private WaveManager waveManager;
    [SerializeField] private AudioSource seSource;
    [SerializeField] private AudioClip levelUpClip;
    [SerializeField] private AudioClip damageClip;
    [SerializeField] private AudioClip gameOverClip;
    [SerializeField] private AudioClip clearClip;

    private void OnEnable()
    {
        playerLevel.LevelUp += PlayLevelUp;
        playerHealth.Damaged += PlayDamage;
        playerHealth.Died += PlayGameOver;
        waveManager.Cleared += PlayClear;
    }

    private void OnDisable()
    {
        playerLevel.LevelUp -= PlayLevelUp;
        playerHealth.Damaged -= PlayDamage;
        playerHealth.Died -= PlayGameOver;
        waveManager.Cleared -= PlayClear;
    }

    private void PlayLevelUp()
    {
        seSource.PlayOneShot(levelUpClip);
    }

    private void PlayDamage()
    {
        seSource.PlayOneShot(damageClip);
    }

    private void PlayGameOver()
    {
        seSource.PlayOneShot(gameOverClip);
    }

    private void PlayClear()
    {
        seSource.PlayOneShot(clearClip);
    }
}
  • HierarchyでSoundPlayerを選び、欄を入れる
    • Player Level・Player Health:Player、Wave Manager:WaveManager
    • Se Source:1つ目(効果音用)のAudio Source
      • 同じオブジェクトに2つあるので、Inspectorから上のAudio Sourceの見出しを欄にドラッグします
    • Level Up Clip:se_levelup、Damage Clip:se_damage、Game Over Clip:se_gameover、Clear Clip:se_clear
ここまでeventで作ってきた見返り

このスクリプトを足すのに、ほかのスクリプトは1行も直していません。PlayerLevelもPlayerHealthもWaveManagerも、「レベルが上がった」「ダメージを受けた」と言っているだけで、誰が聞いているかを知らないからです。

もしPlayerHealthの中に直接audioSource.PlayOneShot(...)と書いていたら、HPの計算と音が混ざって、片方を直すたびにもう片方を壊す心配をすることになります。あとから機能を足しやすいかどうかは、こういう所で決まります。

スライム

音が付いただけで、急にゲームっぽくなりました!

フェアリー

でしょ?音は最後に足すものだと思われがちだけど、足しやすい形にしておくのが本当の準備なんだよ

Windowsのゲームにしよう

ビルドの基本は10-1 ビルドの基本と10-2 はじめてのビルドにまとめてあるので、ここではこのゲームで必要なところだけ書きます。

  • File>Build Profilesを開く
  • Scene Listに2つのシーンを、Titleが上、Gameが下の順で並べる
    • 一番上のシーンが、起動したときに最初に開くシーンです
  • Player Settingsを開いて設定する
項目値
Product Nameスライムちゃんのおかしなサバイバー
Company Name自分の名前でOK
Default Iconicon_whipperなど好きな絵
Fullscreen ModeWindowed
Default Screen Width / Height1280 / 720
Resizable Windowチェックを入れる
  • 全画面ではなく、1280×720のウインドウで起動するようにしています。遊ぶ人の画面の大きさが分からないので、まずは小さめのウインドウが無難です
    • Fullscreen Modeなどは、Player SettingsのResolution and Presentationの中にあります
  • Buildを押して、空のフォルダを選ぶ(例:Build/SlimeSurvivors)
    • Assetsの中やプロジェクトのフォルダを選ぶと、ビルドの成果物がプロジェクトに混ざって大変なことになります。必ず新しいフォルダを作ってください

友達に配れる形にしよう

ビルドが終わると、選んだフォルダの中はこうなっています。

SlimeSurvivors/
├── SlimeSurvivors.exe        ← これを起動する(保存するときに付けた名前)
├── UnityPlayer.dll
├── UnityCrashHandler64.exe
├── D3D12/
├── MonoBleedingEdge/
├── SlimeSurvivors_Data/      ← ゲームの中身が全部入っている
└── スライムちゃんのおかしなサバイバー_BurstDebugInformation_DoNotShip/   ← 配らない
配る前のチェック
  • .exeだけをコピーしても動きません。_DataフォルダとUnityPlayer.dllが必要です
  • _BurstDebugInformation_DoNotShipは消してから配ります。名前のとおり「配らないで」というフォルダで、デバッグ用の情報が入っています
    • このフォルダだけ、.exeではなくProduct Nameの名前が付きます。日本語の名前にしていると日本語のフォルダになります
  • 残りをフォルダごとZIPに圧縮して渡します。相手は展開して.exeをダブルクリックするだけです
スライム

「DoNotShip」って書いてあると、うっかり配ったら怒られそうでドキッとしますね・・・

「WindowsによってPCが保護されました」と出たら

個人が作った.exeは署名が無いので、初めて起動するときにSmartScreenの警告が出ます。「詳細情報」>「実行」で起動できます。

渡すときに、この一言を添えておくと親切です。怪しいから出ているのではなく、「誰が作ったか証明されていない」から出ているだけです。

ビルドした.exeを起動したところ。1280×720のウインドウでタイトル画面が出ている

全部つながったか確認しよう

最後に、シリーズを通しての答え合わせです。ビルドしたゲームで確認してください。

全11回の答え合わせ

  • #1 スライムちゃんが8方向に向きを変えて歩く
  • #2 工房の外に出られない(見えない壁がある)
  • #3 敵が画面の外から出てきて追いかけてくる。大量に出ても重くならない
  • #4 一番近い敵に計量スプーンが自動で飛ぶ
  • #5 ホイッパーが3秒回って3秒休む
  • #6 倒した敵が金平糖を落とし、拾うとレベルが上がる
  • #7 レベルアップでゲームが止まり、3択から選ぶと強くなる
  • #8 攻撃が当たるとダメージの数字が出て、敵がのけぞる
  • #9 敵に触れられるとHPが減り、0でゲームオーバー
  • #10 時間で敵の種類と数が変わり、10分でクリア
  • #11 タイトルから始まり、結果が出て、タイトルに戻れる

1つでも「あれ?」と思ったら、その回に戻ってみてください。どこで止まったかが分かれば、直す場所はだいたい1か所です。

シリーズのまとめ

回作ったもの覚えた機能
#1動かすInput System・8方向のアニメ・Cinemachine
#2床と壁Tilemap・Collider
#3敵ObjectPool・レイヤー
#4自動で攻撃当たり判定・最近接の探索
#5武器のデータScriptableObject・継承
#6経験値プール・Canvas・ゲージ
#73択の強化親クラスの活かし方・timeScale
#8手応えの演出コルーチン
#9HPとゲームオーバー無敵時間・イベント
#10ウェーブデータで難易度を作る
#11タイトルとビルドシーン遷移・音・配布

次にやってみること

  • 武器と強化を増やす。#5・#7の作りなら、クラスとアセットを1つ足すだけです
  • ボスを出す。#10の「ボスを出したい人へ」に方針を書きました
  • スマホ向けにする。画面を縦にして、移動をバーチャルパッドに変えます
  • 絵を差し替える。配布素材は仮の絵なので、自分の絵にすると一気に自分のゲームになります

作り直すなら、ここから

Enemy.Spawnの引数が5つになったのが、このゲームでいちばん重くなった所でした。「敵に渡すもの」を1つのクラスにまとめると、これ以上増えてもSpawnの形を変えずに済みます。

実際に作り切ってから「ここが重かった」と分かるのが普通なので、最初から完璧な形を目指さなくて大丈夫です。

あとがき

全11回、おつかれさまでした!1本の動く2Dゲームを、最初から配れる形まで作り切れたと思います。このシリーズで使った機能は、どれも別のゲームでもそのまま使えるものばかりです。ぜひ次は、自分の好きなお菓子や自分の絵で、自分だけのサバイバーゲームを作ってみてください。感想や「ここで詰まった」があれば、ぜひ教えてもらえると嬉しいです!

フェアリー

完成したゲームは、ぜひ友達にも遊んでもらってね!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次