UnityでFPSゲームを作ってみました その3

今回はUnityでFPSゲームを作成してみました。完成したゲームはこちらです。https://ma510884.com/FPS_Game

その2はこちらです

参考にした記事、使用したアセットは以下になります。

続いてはゲームクリアの条件とゲームオーバーの仕組みを組み込んでいきます。参考にした記事では一定数の敵を倒すとゲームクリアとなっていますが、それではスコアが似通ってしまうので、制限時間内でどれだけ倒せるかという風にしました。別の記事を参考に、void Update内に記述していきます。そのままではゲームスタート時のカウントダウン中も時間が過ぎてしまいますので、if文でカウントダウン後に計測開始するようにしました。詳しくは後述のスクリプトをご覧ください。

ゲームオーバーは記事をそのまま参考にしたものと、追加でフィールドから落ちてもゲームオーバーになるようにしました。フィールドの下にブロックを置き、Box Colliderを追加してIs Triggerにチェックをいれます。タグを設定し、ブロックに接触するとゲームオーバーになるように条件を追加しました。普段は見えないように、Mesh Rendererのチェックは外しておきます。そして、以下のスクリプトをアタッチしました。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DropScript : MonoBehaviour
{
    [SerializeField]
    GameManager gameManager;

    void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.tag == "Cube")
        {
            gameObject.GetComponent<Rigidbody>().isKinematic = true;
            StartCoroutine(gameManager.GameOver());
        }
    }

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

最後に、音の設定をしていきます。必要なものはBGM、ゲームクリア時とゲームオーバー時の音楽、銃の発砲音、空薬莢の落ちる音です。ゲーム終了時の音楽は以前使ったものを、それ以外はYouTubeクリエイターキット等のフリー素材を使用しました。

まずはゲーム終了時の音楽ですが、それぞれAudioClipとして読み込ませ、ゲーム終了時に他の音を止めてからPlayOneShotで再生しています。

銃の発砲音と空薬莢の落ちる音は、マウスをクリックしている間だけループ再生し、離したら止める必要があるため、BGMと共にAudioSourceとして配列で読み込ませました。配列を使う場合は、GetComponentsと複数型にしないとエラーが出ます。逆に、配列を使わないのにsをつけると「配列じゃない」とエラーが出ます。ここで少しつまずきました。

後は、ゲーム中だけBGMとSEが鳴るようにします。GameControler.csの if文にfirstPerson.playerCanMove == trueを追加して対応しました。SEにはLoopにチェックを入れておきます。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;

public class GameManager : MonoBehaviour
{

    [SerializeField]
    int maxScore = 999999;
    [SerializeField]
    Text scoreText;
    [SerializeField]
    FirstPersonAIO firstPerson;
    [SerializeField]
    FirstPersonGunController gunController;
    [SerializeField]
    Text centerText;
    [SerializeField]
    float waitTime = 2;
    [SerializeField]
    EnemySpawner[] spawners;

    public float time = 60;
    public Text timerText;
    public bool isTimeUp;
    public GameObject gameOverUI;
    private AudioSource[] audioSource;
    public AudioClip gameclear;
    public AudioClip gameover;
    private bool switchBool = false;

    int score = 0;
    bool gameOver = false;
    bool gameClear = false;

    public int Score
    {
        set
        {
            score = Mathf.Clamp(value, 0, maxScore);
            scoreText.text = score.ToString("D6");
        }
        get
        {
            return score;
        }
    }

    void Start()
    {
        InitGame();
        StartCoroutine(GameStart());
        isTimeUp = false;
        audioSource = GetComponents<AudioSource>();
        SetSpawners(false);
    }

    void Update()
    {
        if (0 < time & firstPerson.playerCanMove == true)
        {
            time -= Time.deltaTime;
            timerText.text = time.ToString("F2");
        }
        else if (time < 0)
        {
             isTimeUp = true;
             if (!switchBool)
             {
                switchBool = true;
                StartCoroutine(GameClear());
             }
        }

        if (Input.GetMouseButtonDown(0) & firstPerson.playerCanMove == true)
        {
            audioSource[1].Play();
            audioSource[2].Play();
        }

        if (Input.GetMouseButtonUp(0))
        {
            audioSource[1].Stop();
            audioSource[2].Stop();
        }
    }

    void InitGame()
    {
        Score = 0;
        firstPerson.playerCanMove = false;
        firstPerson.enableCameraMovement = false;
        gunController.shootEnabled = false;
        Cursor.lockState = CursorLockMode.Locked;
        Cursor.visible = false;
    }

    public IEnumerator GameStart()
    {
        yield return new WaitForSeconds(waitTime);
        centerText.enabled = true;
        centerText.text = "3";
        yield return new WaitForSeconds(1);
        centerText.text = "2";
        yield return new WaitForSeconds(1);
        centerText.text = "1";
        yield return new WaitForSeconds(1);
        centerText.text = "GO!!";
        yield return new WaitForSeconds(1);
        // centerText.text = "";
        centerText.enabled = false;
        firstPerson.playerCanMove = true;
        firstPerson.enableCameraMovement = true;
        gunController.shootEnabled = true;
        SetSpawners(true);
        audioSource[0].Play();
    }

    public IEnumerator GameOver()
    {
        if (!gameOver)
        {
            gameOver = true;
            firstPerson.playerCanMove = false;
            firstPerson.enableCameraMovement = false;
            gunController.shootEnabled = false;
            SetSpawners(false);
            yield return new WaitForSeconds(1);
            DestroyEnemies();
            centerText.enabled = true;
            centerText.text = "Game Over";
            gameOverUI.SetActive(true);
            isTimeUp = false;
            // centerText.text = "";
            // centerText.enabled = false;
            gameOver = false;
            Cursor.lockState = CursorLockMode.None;
            Cursor.visible = true;
            audioSource[0].Stop();
            audioSource[0].PlayOneShot(gameover);
        }
        else
        {
            yield return null;
        }
    }

    public IEnumerator GameClear()
    {
        if (!gameClear)
        {
            gameClear = true;
            firstPerson.playerCanMove = false;
            firstPerson.enableCameraMovement = false;
            gunController.shootEnabled = false;
            SetSpawners(false);
            yield return new WaitForSeconds(1);
            DestroyEnemies();
            centerText.enabled = true;
            centerText.text = "Game Clear!!";
            gameOverUI.SetActive(true);
            // centerText.text = "";
            // centerText.enabled = false;
            gameClear = false;
            Cursor.lockState = CursorLockMode.None;
            Cursor.visible = true;
            // yield return SceneManager.LoadSceneAsync(SceneManager.GetActiveScene().buildIndex);
            audioSource[0].Stop();
            audioSource[0].PlayOneShot(gameclear);
        }
        else
        {
            yield return null;
        }
    }

    void DestroyEnemies()
    {
        GameObject[] enemies = GameObject.FindGameObjectsWithTag("Enemy");

        foreach (GameObject enemy in enemies)
        {
            Destroy(enemy);
        }
    }

    void SetSpawners(bool isEnable)
    {
        foreach (EnemySpawner spawner in spawners)
        {
            spawner.spawnEnabled = isEnable;
        }
    }
}

これでUnityを実行してみましょう。「3、2、1、Go!!」の後にBGMが再生され、射撃時にSEが鳴るはずです。また、StartボタンやRetryボタンを押した時には音が出なくなっていると思います。ゲームクリア時、ゲームオーバー時の音楽も確認しましょう。以上で問題がなければゲームは完成です。

余談ですが、M4A1の銃声音は5.56×45mm NATO弾を使用していますのでもっと軽いのですが、銃を乱射している雰囲気を出したかったので.50BMG 12.7×99mm NATO弾クラスの迫力あるものに変更しました。個人的にはとても気に入っています。

弾は小さいものから9mmパラベラム弾(拳銃用)、5.56×45mm弾(アサルトライフル用)、7.62×51mm弾(バトルライフル用)、12.7×99mm弾(対物ライフル・重機関銃用)となっています。火薬、雷管を外した実物です。

以上、最後までお読みいただきありがとうございました。

  • 7
  • 0
  • 2

おすすめのタグ

Laffey(ラフェィ)

色々な物・もの・モノを作って記事にしています

作者のページを見る

関連記事

寄付について

「novalue」は、‟一人ひとりが自分らしく働ける社会”の実現を目指す、
就労継続支援B型事業所manabyCREATORSが運営するWebメディアです。

当メディアの運営は、活動に賛同してくださる寄付者様の協賛によって成り立っており、
広告記事の掲載先をお探しの企業様や寄付者様を随時、募集しております。

寄付についてのご案内