2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Unity 公式チュートリアル「Space Shooter tutorial」をベースに自作ゲームを作る【後編】

Posted at

Unity 公式チュートリアル「Space Shooter tutorial」をコードから理解してみる という投稿をしましたが、今回はこれをベースに拡張し、簡易的なゲームエンジン的なものとして汎用化し、それを用いて独自のシューティングゲームを作成してみます。

【前編】 では、元のサンプルゲーム に対し、岩石に大きさと耐久度の差をつけ、真っすぐ高速に進んでくるミサイル、弾を斜めに発射する中ボス的な敵キャラ、赤い敵エース機などを追加し、敵の拡張を中心に実施しました。

この後編ではステージごとの難易度の上昇や、タイトル画面を作成し、ゲームとして最低限の体裁を整えたいと思います。完成品は コチラ でプレイ可能。

ステージの実装

まずはステージを実装してみます。Done_GameController スクリプトを対象に、まずは private 変数 stage の追加と初期化。

Done_GameController.cs
    private int stage; // 追加

    void Start()
    {
        gameOver = false;
        restart = false;
        restartText.text = "";
        gameOverText.text = "";
        score = 0;
        stage = 1; // 追加
        UpdateScore();
        StartCoroutine(SpawnWaves());
    }

サンプルゲームでは内部的に wave の概念があるので、これをステージにそのまま利用します。つまり、wave ごとにステージを1つ進めることになります。

Done_GameController.cs
    IEnumerator SpawnWaves()
    {
        yield return new WaitForSeconds(startWait);
        while (true)
        {
            // 中略
            if (gameOver)
            {
                restartText.text = "Press 'R' for Restart";
                restart = true;
                break;
            }
            stage++; // 追加
        }
    }

そして画面表示ですが、ちょっと手抜きしてスコア表示とあわせて実施してしまいましょう。

Done_GameController.cs
    void UpdateScore()
    {
        //scoreText.text = "Score: " + score;
        scoreText.text = "Score: " + score + "\nStage: " + stage; // 修正
    }

ただこれだと2行目になったステージ表示が画面に描画されないので、Score UIテキスト・オブジェクトのインスペクターから、高さを倍の 60 に変更しておきます。
image.png
これでゲーム実行時に、左上にのスコアの下に、ステージ数も表示されるようになりました。ゲームが進んでいくと、ステージ数も増加していきます。
image.png

ステージにあわせて敵の種類を増やす

最初から全ての種類の敵が出現すると難しすぎるため、最初のステージではサンプルゲームと同じ岩石と紫の敵だけ出現するようにし、ステージが1つ進むごとに新しい敵が1つ追加されるように修正します。

Done_GameController.cs
            for (int i = 0; i < hazardCount; i++)
            {
                //GameObject hazard = hazards[Random.Range(0, hazards.Length)];
                GameObject hazard = hazards[Random.Range(0, System.Math.Min(stage + 3, hazards.Length))]; // 修正
                Vector3 spawnPosition = new Vector3(Random.Range(-spawnValues.x, spawnValues.x), spawnValues.y, spawnValues.z);
                Quaternion spawnRotation = Quaternion.identity;
                Instantiate(hazard, spawnPosition, spawnRotation);
                yield return new WaitForSeconds(spawnWait);
            }

試しプレイしたところ、各ステージが短すぎる気がしたので、Done_Game Controller オブジェクトの Hazard Count を倍の 20 に変更して、ステージ中に表示されるオブジェクトの数を増やして調節してみました。
image.png

更に難易度の調整をする

上記の修正だけだと、ステージ4くらいで新キャラの登場がなくなり、難易度が変わらなくなってしまいます。もう少し調整してみましょう。

まずは細かいですが、各ステージ中に登場する敵オブジェクトの数を、ステージが進むにつれて増やしてみます。修正(1)の部分です。

Done_GameController.cs
            //for (int i = 0; i < hazardCount; i++)
            for (int i = 0; i < hazardCount + stage * 2; i++) // 修正 (1)
            {
                GameObject hazard = hazards[Random.Range(0, System.Math.Min(stage + 3, hazards.Length))];
                Vector3 spawnPosition = new Vector3(Random.Range(-spawnValues.x, spawnValues.x), spawnValues.y, spawnValues.z);
                Quaternion spawnRotation = Quaternion.identity;
                Instantiate(hazard, spawnPosition, spawnRotation);
                //yield return new WaitForSeconds(spawnWait);
                yield return new WaitForSeconds(spawnWait * Mathf.Clamp(5 / stage, 0.2f, 1f)); // 修正
            }

また敵の出現待ち時間を、ステージが進むに従って少しずつ短くしていきます。これにより敵の出現量が多くなり、難易度が上昇します。ステージ25ぐらいで難易度が頭打ちになりますが、現時点ではこれが超絶難しいので、それ以上は不要に思われます。

ゲームオーバー時の処理を改善する

各ステージを長くした結果、ゲームオーバー時にリスタート待ちの時間が長くなってしまいました。すぐ表示されるように修正してみましょう。

まず、wave の終了待ちをしていた部分の一部をコメントアウトし、

Done_GameController.cs
            if (gameOver)
            {
                //restartText.text = "Press 'R' for Restart";
                //restart = true;
                break;
            }
            stage++;

同じ処理を gameover() 関数の中にコピーします。

Done_GameController.cs
    public void GameOver()
    {
        gameOverText.text = "Game Over!";
        gameOver = true;
        restartText.text = "Press 'R' for Restart"; // 追加
        restart = true; // 追加
    }

これで自機が破壊された直後にリスタートの表示が出るようになりました。

ハイスコアを記録する

Unity では PlayerPrefs クラスを用いて、ゲームデータの保持ができます。これを利用して、ハイスコアを記録する仕組みを実装します。

まずは Done_GameController スクリプトに、ハイスコアの読み込み部分を実装します。保存キーは "hi-score" とし、初期値は 0 です。

Done_GameController.cs
    private int hiScore; // 追加

    void Start()
    {
        gameOver = false;
        restart = false;
        restartText.text = "";
        gameOverText.text = "";
        score = 0;
        hiScore = PlayerPrefs.GetInt("hi-score", 0); // 追加
        stage = 1;
        UpdateScore();
        StartCoroutine(SpawnWaves());
    }

後は GameOver() 関数に、表示と保存のロジックを追加します。

Done_GameController.cs
    public void GameOver()
    {
        gameOverText.text = "Game Over!";
        if (score > hiScore)                         // 追加
        {                                            // 追加
            hiScore = score;                         // 追加
            PlayerPrefs.SetInt("hi-score", hiScore); // 追加
            gameOverText.text += "\n Hi Score!!";    // 追加
        }                                            // 追加
        gameOver = true;
        restartText.text = "Press 'R' for Restart";
        restart = true;
    }

そうそう、gameOverText オブジェクトの高さも、忘れず倍にしておきましょう。
image.png
これでゲームオーバー時に、ハイスコアを獲得していれば「Hi Score!!」という文字列が画面中央に表示されるようになります。
image.png
なおハイスコアを削除するには、Unity エディタの編集メニューにある「すべてのPlayerPrefsを消去」を実行してください。

タイトル画面を作成する

ゲームとしての体裁も整ってきたので、いよいよタイトル画面を作成してみましょう。とはいえ、今回はお手軽に 公式アセット「Unity Samples: UI」Menu 3D シーンで uGUI の基礎を理解する で理解した宇宙っぽいメニューの部品を流用して楽しちゃいましょう!

まずは新規にシーンを作成し、
image.png
Title という名前で Done_Main と同じフォルダに保存します。
image.png
Unity Samples: UI アセットから SF Scene Elements PreFab をエクスポートし、
image.png
今回のゲームで読み込んで、新しく作成した Title シーンに配置します。
image.png

ゲームタイトルの表示

まず新規で UI 要素の Canvas を追加します。インスペクターの設定はそのままで大丈夫でしょう。
image.png

そして Canvas の子要素として TextMeshPro - Text を追加します。初回は以下のようなポップアップが表示されるので、ボタンをクリックして必要なファイルを読み込んでおきましょう。
image.png
表示するテキストをゲームタイトル(今回は Space Shooter tutorial+)に変更し、色やフォントのオプションを好みで設定します。

ついでにハイスコア表示用と、キー操作説明(Press 'S' for Start\nPress 'Q' for Quit)用に UI Text を二つ、その下に配置しておきましょう。シーンを実行して配置を確かめます。
image.png
さて、タイトル用のコードを書きましょう。SF Scene Elements のインスペクターで「コンポーネントを追加」から「新しいスクリプト」を選択して TitleScript と名前をつけます。
image.png
Done_GameController スクリプトのコードを参照して、以下のようにハイスコア読み込みと、キーに応じた処理を記述します。

TitleScript.cs
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class TitleScript : MonoBehaviour
{
    public Text hiScoreText;

    void Start()
    {
        hiScoreText.text += PlayerPrefs.GetInt("hi-score", 0);
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.S))
        {
            SceneManager.LoadScene("Done_Main");
        }
        if (Input.GetKeyDown(KeyCode.Q))
        {
#if UNITY_EDITOR
            UnityEditor.EditorApplication.isPlaying = false;
#else
            Application.Quit();
#endif
        }
    }
}

hiScoreText にはハイスコア表示用の UI Text をインスペクターで指定しておきます。
image.png
これでハイスコアの表示とタイトル画面からの遷移はokです。あとはゲーム画面からタイトル画面へ戻れるように修正する必要がありますね。

ゲーム終了時にタイトルに戻る

Done_GameController スクリプトの GameOver 関数で表示メッセージを追加します。

Done_GameController.cs
    public void GameOver()
    {
        gameOverText.text = "Game Over!";
        if (score > hiScore)
        {
            hiScore = score;
            PlayerPrefs.SetInt("hi-score", hiScore);
            gameOverText.text += "\n Hi Score!!";
        }
        gameOver = true;
        //restartText.text = "Press 'R' for Restart";
        restartText.text = "Press 'R' for Restart\nPress 'Q' to Title"; // 修正
        restart = true;
    }

表示される行数が増えるので、例によって Restart テキストの高さを倍の 60 に変更しておきます。
image.png
ゲームオーバー時に右上に表示されるテキストが変更されていることを確認しておきましょう。
image.png
そして Done_GameController スクリプトに、ゲームオーバー時に Q キーを押した場合の処理を追加します。

Done_GameController.cs
    void Update()
    {
        if (restart)
        {
            if (Input.GetKeyDown(KeyCode.R))
            {
                SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
            }
            if (Input.GetKeyDown(KeyCode.Q))     // 追加
            {                                    // 追加
                SceneManager.LoadScene("Title"); // 追加
            }                                    // 追加
        }
    }

これでゲーム用の Done_Main シーンと、タイトル画面の Title シーンの間の遷移が設定できました。ゲームとしての体裁が整った感じですね。

いよいよビルド

「さぁ、実験を始めようか。」と、いよいよビルドのフェーズです。今回は WebGL で「ビルドして実行」を実施してみます。
image.png
問題なくビルドできたようで、ローカルで Web サービスが立ち上がってWeb ブラウザでゲームをプレイすることができました。
image.png
Web ブラウザで実行される場合、PlayerPrefs データはブラウザの IndexedDB に格納されている模様。
image.png
ビルド結果は以下のように指定したフォルダに出力されており、サイズは13Mバイトぐらいでした。
image.png
生成されるのは通常の Web コンテンツなので、生成されたフォルダ内で webserv などのWeb サーバーを起動させれば、そちらでも動作確認が可能です。

そして生成されたフォルダの中身をWeb サーバーにアップロードすれば、ゲームを公開可能です。実際に http://rinco.jp/unity/sst1/ に公開してみましたので、よろしかったらプレイしてみてください。
image.png

というわけで!

GW は過ぎちゃいましたが、公式チュートリアルをベースとして簡単なシューティングゲームを作成してみました。シンプルですが、ゲームとして必要な要素はだいたい揃っていると思いますので、独自ゲームを作成する際に参考にしていただければ嬉しいです。

それではまた!

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?