うまげーむのゲームブログ

ゲームの情報を主に投稿します。

【Unity】ソースコードコピペ可能!マリオ風2Dアクションゲームの作り方 #7 効果音 / タイトル画面 / メニュー画面

はじめに

こんばんは。あけましておめでとうございます。

今回もUnityをやります。

前回:

www.umagame.info

効果音の追加

Audio Sourceの追加

まずは、効果音を追加していきます。同じ要領でBGMの追加もできますが、今回は音源を用意できなかったのでBGMは追加しません。

まず、音源ファイル用のフォルダを作成します。

名前はSoundsにします。

用意した音源ファイルを入れます。周波数の関係で、mp3ファイルを入れると音が正しく認識されない場合があるのでwav形式がおすすめです。

mp3→wavへの変換は調べれば変換サイトがいくつもあるのでそれを使いましょう。

今回はジャンプ時、前回(第6回)追加したブロックの破壊時、コインの所得時に再生される効果音を追加します。

Playerオブジェクトのインスペクターから、「Audio Source」というコンポーネントを追加します。

「オーディオクリップ」に音源ファイルをドラッグアンドドロップします。

同じようにして、3つのAudio Sourceを作ります。

この3つは全て効果音なので、インスペクターから「ゲーム開始時に再生」のチェックを外しておきます。BGMの場合はチェックを入れたままでOKです。

スクリプトの編集

Player.csを書き換えて効果音が鳴るようにします。

//Player.cs

public class Player : MonoBehaviour
{
    public AudioSource JumpSE;
    public AudioSource BreakSE;
    public AudioSource MoneySE;

    void Update()
    {
        if (Input.GetButtonDown("Jump") && isGrounded())
        {
            rb.velocity = new Vector2(rb.velocity.x, JumpForce);
            //ジャンプ音の追加
            JumpSE.Play();
        }
    }

    private void OnCollisionEnter2D(Collision2D collision)
    {
        //Breakable
        BoxCollider2D c = GetComponent<BoxCollider2D>();
        if (obj.CompareTag("Breakable") && rb.velocity.y == 0 &&
            obj.transform.position.y - obj.GetComponent<Collider2D>().bounds.size.y / 2 >= this.transform.position.y)
        {
            GameObject.Destroy(collision.gameObject);
            //ブロック破壊音の追加
            BreakSE.Play();
        }
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        GameObject obj = collision.gameObject;

        //Collectable
        if (obj.GetComponent<Collectable>() != null)
        {
            if(obj.GetComponent<Collectable>().ID == "money")
            {
                Money++;
                GameObject.Destroy(obj);
                //コイン取得音の追加
                MoneySE.Play();
            }
        }
    }
}

3つのAudioSource型public変数を作って、ジャンプ・ブロック破壊・コイン取得を実装している部分でPlay()を呼び出して効果音を再生するようにしています。

Playerオブジェクトのインスペクターに戻って、先程作ったAudio Sourceを適用します。

3つとも設定します。

これで、効果音の実装は完了です。Audio Sourceの設定から、音量設定やピッチの変更なども出来ます。

タイトル画面

シーンの作成

次に、タイトル画面を作っていきます。

その前に、すでにあるシーン(SampleScene)の名前を変更しておきます。

MainSceneにしました。

Assets/Scenesで右クリック→作成→シーンをクリックして新しいシーンを作ります。

名前はTitleSceneにします。

作ったやつをダブルクリックして開くと、シーンが切り替わります。新規作成時は、Main Cameraのみある状態です。

背景の追加

まずは、背景を追加します。右クリック→UI→「Raw 画像」をクリック。

名前はBackgroundにします。

同時にCanvasも作成されるので、Canvasの「レンダーモード」を「ワールド空間」にして、

XおよびY座標を0に設定します。

その後、BackgroundのXY座標・幅高さをこのように変更します。幅と高さの比は用意した背景用画像と合わせてください。(今回は16:9です)

「スケール」を調整して、カメラがBackgroundで全て埋まるようにします。(Xの左のボタンを押すとXYZ全て同じ値に保ったまま変更できます)

画像を取り込んで、上画像のように設定します。

いつも通り「単位毎のピクセル数」、「フィルターモード」、「圧縮」の設定を変更して、「ラップモード」を「繰り返し」に設定します。

(単位毎のピクセル数は変更しなくても良かったですが、一応設定しておきます)

ちなみに今回使う画像はこちらです。(サイズは320x180)

ドラッグアンドドロップで、Backgroundオブジェクトの「テクスチャ」に画像を設定します。

これで背景自体は完成ですが、今回は背景をスクロールさせてみようと思います。

背景のスクロール

背景のスクロールの制御と、後ほど追加するボタンの処理を書くためのスクリプトを作成します。

名前はTitleManagerにします。

コードは以下のようになります。

//TitleManager.cs
public class TitleManager : MonoBehaviour
{
    public RawImage BackgroundImage;
    public float ScrollSpeed;
    void Start()
    {
        
    }

    void Update()
    {
        BackgroundImage.uvRect = new Rect(BackgroundImage.uvRect.position + new Vector2(ScrollSpeed, 0) * Time.deltaTime, BackgroundImage.uvRect.size);
    }
}

RawImageのUVRectを取得し、そのX座標を変更することでスクロールを実装しています。スクロールの速度はScrollSpeedで設定します。

スクリプトをEventSystem(UI追加時に作成されるオブジェクト)に適用します。

BackgroundImageとScrollSpeedを設定します。(何故かインスペクターでは日本語で表示されている...)

これで、画像が横にスクロールするようになりました。スクリプトを書き換えれば、縦方向に動かすことも出来ます。

ボタンの追加

カスタムフォントの追加

ボタンを追加していきます。

まず、ボタンのテキストに使うフォントを新しく追加します。

フォント用のフォルダを作成します。

名前はFontsにします。

今回使用するフォントは「k8x12」という8x12のドットフォントです。フリーフォントなので自由に使用できます。ありがたい。

littlelimit.net

ttf(TrueTypeFont)形式でフォントをダウンロードし、ttfファイルをFontsフォルダに入れます。

その後、画面上部メニューのウィンドウ→「TextMeshPro」→「フォントアセットクリエーター」をクリック。

新しいウィンドウが開くので、「Source Font File」にttfファイルを指定して、「Atlas Resolution」をどちらも8192にします。

こちらのサイトから、文字リストを取得します。(https://gist.github.com/kgsi/ed2f1c5696a2211c1fd1e1e198c96ee4

右上の「Raw」をクリックして、

表示されたテキストを全て選択してコピーします。

これを、Font Asset Creatorの「Custom Character List」にペーストします。

先程貼ったリンクのページに半角カタカナ・全角英数字・波ダッシュの文字列もあるのでこちらも追加しておきます。

コピーして、

同様に「Custom Character List」に追加でペーストします。

その後「Generate Font Atlas」をクリックして、フォントアセットを新しく作成します。

生成が始まります。しばらく待ちましょう。

生成が終わったら(「Generation completed in: ...」と表示されたら)、「Save」をクリックして、

Fontsフォルダを指定して、assetファイルを保存します。

ボタンの追加

ボタンの背景用の画像を取り込んで、画像の設定をいつも通り行います。

Sprite Editorからスライスも行います。今回は上画像のように、通常時・選択時(ハイライト)・押下時のボタンのテクスチャを用意しました。

新しくボタンのオブジェクトを追加します。右クリック→UI→「ボタン - TextMeshPro」をクリック。

今回はタイトルにゲームスタート用ボタン(StartButton)とゲーム終了用ボタン(ExitButton)の2つを追加します。

ボタンのインスペクターから、「ソース画像」に通常時のボタンのスプライト(今回はbutton_0)を指定します。

その後Buttonコンポーネントの「遷移」を「スプライトスワップ」にして、

  • 「Highlighted Sprite」と「Selected Sprite」に選択時のボタンのスプライト(今回はbutton_1)を設定

  • 「Pressed Sprite」と「Disabled Sprite」に押下時のボタンのスプライト(今回はbutton_2)を設定

の2つの設定をします。

ボタンのテキストはそれぞれのボタンの子オブジェクトとして作られています。これを選択して、

インスペクターから「Font Asset」に先程作ったフォントアセットを指定して、「Font Size」を小さくして、表示されるテキストを設定します。

ExitButtonも同様に設定します。

ボタンの処理

ボタンを押したときの処理を設定します。

まず、先程作ったTitleManager.csにコードを追加します。

public void StartButton()
{
    SceneManager.LoadScene("MainScene");
}
public void ExitButton()
{
    Application.Quit();
}

上記の2つのメソッドを追加します。StartButton()ではMainSceneをロードする処理を、ExitButton()ではゲームを終了する処理をしています。

ボタンのインスペクターから、「クリック時()」の+ボタンを押して、

オブジェクトの部分にEventSystemを指定して、TitleManagerの該当するメソッドを設定します。上画像はスタートボタンなのでStartButton()を設定します。

ExitButtonも同様に設定します。

これで、TitleSceneでスタートボタンを押すと

ゲーム画面に移るようになります。ちなみに、エディタ内のテストプレイではExitボタンを押しても何も起きません。

ゲームオーバー画面・メニュー画面

ゲームオーバー画面の作成

ゲームオーバーになったときの画面と、ゲーム内からタイトルにアクセスできるようにメニュー画面も作ります。

MainSceneに戻って、Canvas内に新しくパネルを追加します。(右クリック→UI→パネル)

名前はGameoverPanelにします。

今回はパネル背景用の画像を用意するのがめんどくさかったので、真っ白の16:9の画像を用意しました。

アンカープリセットをこれに変えます。(これでXY座標・幅高さの数値でパネルの大きさ・位置を変えれるようになります)

XY座標、幅高さをこのように設定しました。(カメラにちょうど収まるサイズです)

色を半透明の黒にします。上画像のように設定しました。

次に、タイトル画面と同じようにテキスト・ボタンを追加します。GameoverPanelを右クリック→UI→テキストをクリックして、

タイトル画面と同じ要領で設定をします。

ボタンも同様に追加します。ボタンの処理は、GameManager.csに以下のメソッドを追加して作りました。

public void RetryButton() //リトライボタン
{
    SceneManager.LoadScene("MainScene");
    Time.timeScale = 1.0f;
}

public void TitleButton() //タイトルへ移動するボタン
{
    SceneManager.LoadScene("TitleScene");
    Time.timeScale = 1.0f;
}

シーンのロードは今までと同じです。Time.timeScaleを1に設定している理由は後ほど説明します。

ゲームオーバー画面はゲームスタート時は非表示なので、インスペクターの上部の名前欄の左のチェックを外しておきます。これでGameoverPanelは非アクティブ(Inactive)になりました。

Player.csを以下のように変更します。

//Player.cs

public class Player : MonoBehaviour
{

    public GameObject GameoverPanel;

    private void OnCollisionEnter2D(Collision2D collision)
    {
        GameObject obj = collision.gameObject;

        //InstaDeath
        if (obj.CompareTag("InstaDeath"))
        {
            GameoverPanel.SetActive(true);
            Time.timeScale = 0;
        }
}

第五回で設定した、InstaDeathに触れたときの処理を変更します。

GameoverPanelをアクティブ(チェックが入った状態)にして、Time.timeScaleを0にします。

Time.timeScaleはゲーム内の時間の流れる速さを表していて、通常時は1です。これを0にすることで重力や移動の処理が止まります。

これが0のままだとゲームを再開しても動けない状態になってしまうので、リトライボタンを押したときにこれを1に戻すようにしています。

インスペクターからGameoverPanelのオブジェクトを指定します。

これで実装自体は完了ですが、新しく作ったTitleSceneがまだゲームに登録されていないのでそれをします。

上部メニューのファイル→「ビルド設定」をクリックして、

「ビルドに含まれるシーン」にTitleSceneをドラッグアンドドロップで登録します。

これで、穴に落ちるとゲームオーバー画面が表示されるようになります。

メニュー画面の作成

次に、ゲーム内でEscapeキーを押すとメニュー画面(ポーズ画面)が表示されるようにします。

まず、メニュー画面を作ります。ゲームオーバー画面のようにテキストとボタンのみを配置するだけなので、さっき作ったGameoverPanelをコピーして作りました。

入力マネージャーから新しく入力軸を追加します。メニューの編集→「プロジェクト設定」をクリックして、

入力マネージャーで新たに「Escape」という軸を追加します。今回は初期設定で存在した「Fire2」という軸の正方向ボタンを「escape」に変更してEscapeとしました。

そして、Player.csのUpdate()を以下のように変更します。

void Update()
{
    if (Time.timeScale != 0) //timeScaleが0のとき移動できないようにする
    {
        //Player Movement
        if (!isDashing)
        {
            rb.velocity = new Vector2(Input.GetAxisRaw("Horizontal") * MoveSpeed, rb.velocity.y);
        }
        else
        {
            rb.velocity = new Vector2(Input.GetAxisRaw("Horizontal") * DashSpeed, rb.velocity.y);
        }

        if (Input.GetButtonDown("Jump") && isGrounded())
        {
            rb.velocity = new Vector2(rb.velocity.x, JumpForce);
            JumpSE.Play();
        }

        if (Input.GetButton("Dash"))
        {
            isDashing = true;
        }
        else
        {
            isDashing = false;
        }
    }

    if (Input.GetButtonDown("Escape"))
    {
        if (MenuPanel.activeSelf)
        {
            MenuPanel.SetActive(false);
            Time.timeScale = 1.0f;
        }
        else
        {
            MenuPanel.SetActive(true);
            Time.timeScale = 0;
        }
    }
}

移動を実装していた部分をif文で囲み、Time.timeScaleが0のときは移動が出来ないようにします。これでポーズを実装しています。

また、Escapeが押されたときにメニュー画面の表示の切り替えおよびtimeScaleの変更を行っています。(GameObjectのアクティブ状態は、GameObject.activeSelfで取得できます)

MenuPanelオブジェクトを設定して、完成です。

これで、Escキーを押したときに時間が止まり(ポーズ中に遷移)メニュー画面が表示されるようになりました。

今回の進捗 / ソースコード

今回は効果音の追加・タイトル画面の追加・メニュー画面の追加を行いました。

効果音は一応Magical 8bit Plugという8bitゲームの音を再現したシンセを使って作ったんですが、あまり出来が良くなかったです。普通にフリーの音源を使ったほうが良いですね。

今回は忘れていたので追加していませんが、ボタンを押したときの効果音もあったほうが良いですね。

ソースコードは例によって、こちらのリンクからアクセスできます。(TutorialGame/7にあります)

さいごに

今回は以上となります。

次回はUnityのパーティクルシステムをやると思います。多分。

次回:

www.umagame.info