LoginSignup
14
18

More than 5 years have passed since last update.

【Unity】ゲームマネージャークラスを作る:「はじめてのUnity」のブロック崩しを改造しながら学ぶ

Last updated at Posted at 2017-09-12

はじめに

JunShimuraさんの「[超初心者向け]Unityチュートリアル「はじめてのUnity」のブロック崩しと同等をC#で ::(1)ステージ配置~(6)ブロックを手作業で並べる)」をよりゲームらしく改造する。

作成する記事は下記の通りです。

ゲームマネージャークラスとは

ゲームの進行に関する情報を管理します。
例えば、「タイトル -> ゲームスタート -> (ミスしたら)ゲームオーバー -> タイトル」 といった進行となります。

クラス名は人によってマネージャー(Manager)ではなくコントローラー(Controller)かも知れません。

ゲームマネージャークラスを作成する前に幾つか準備が必要となります。
順々にこなしてください。

タイトルの表示

スコアの表示はScene上はカメラの影響で傾いて表示されますが、実際のGame上は垂直に表示されます。
Unity_TitleScene.png

Unity_TitleGame.png

テキストの追加

スコア表示の時とは違い、今回は間にパネル(Panel)を挟んでみます。パネルを挟むとゲーム画面全体に"もや"がかかったような表示になります。※靄(もや)とは霧(きり)よりも薄いものを指す。
参照:【Unity開発】uGUIのPanelの使い方(非表示の方法等)【ひよこエッセンス】

Hierarchyの下にある[Create]>[UI]>[Canvas]を選択して下さい。次に[Create]>[UI]>[Panel]を選択して下さい。同様に[Create]>[UI]>[Text]を選択して下さい。
今のままだとPanelTextが同一階層になっているので、TextPanelの子階層にするため、Textをドラッグ&ドロップでPanelに重ねてください。
Unity_TitleAdd.gif

Canvasの設定変更(Title GUI)

Canvasの[Inspector]タブにて名前を「Title GUI」にします。CanvasコンポーネントにあるRender ModeScreen Space - Cameraに変更します。次にRender Cameraのところにある○をクリックし、開いたリスト画面からMain Camera(Camera)を選択します。
このままだと、タイトルが壁の下に表示されてしまうので、カメラとGUIとの距離であるPlane Distance18に変更します。

Unity_TitleGUI2.png

Render ModeScreen Space - CameraではなくデフォルトのScreen Space - Overlayのままでもいいです。Screen Space - Overlayはゲームで使われる他のオブジェクトの描画が終わったあとにGUIが描画されます。つまりGUIが絶対に最前面になります。本来はこれが望ましいです。その場合、Scene上ではGUIが別の位置に配置されますが、Game上では重なって表示されます。CanvasをクリックすればScene上のGUI位置に移動するので、慣れればこちらを使う方がいいかも知れません。

タイトルの作成(Title)

今回、タイトル部分は「Game Start」と「Game Clear」と「Game Over」の3つをスクリプトで文字と色を書き換えて使いまわします。

Title GUI配下のTextの[Inspector]タブにて名前を「Title」にします。
[Rect Transform]と[Scale]を下図のように変更します。今回は目立つように文字の大きさを3倍にしておきます。
次にTextGame Startと入力します。Font StyleBold And ItalicFont Size48Alignmentを水平方向は中央寄せ/垂直方向は中央揃えにして、Colorを緑(0,255,0)にします。

Unity_Title2.png

タイトルに影やアウトラインを装飾する

そのままの文字だとそっけないので、影(Shadow)とアウトライン(Outline)のエフェクトを追加して装飾します。
参照:UnityのGUIの基本的な作り方 uGUIのPanel、Button、Text、Imageの使い方

Titleの[Inspector]タブの一番下にある[Add Compornent]ボタンをクリックして、UI>Effects>Shadowを追加します。同様にUI>Effects>Outlineを追加します。
影の大きさを変更したいので、Shadow(Script)Effect DistanceX4にします。

Unity_TitleEffect.gif
表示結果(通常->影->アウトライン)
Unity_TitleEffects.png

シーン表示が変になったら

シーンの向きや拡大・縮小などの変更はアンドゥ(CTRL+Z)やリドゥ(CTRL+Y)が効かないので、元に戻す方法が分からなくなる時があります。
その場合、Sceneビュータブの右クリックメニューの[Add Tab]-> [Scene]で、新たなSceneビューを作成します。問題の起こっているSceneビューは右クリックメニューの [Close Tab] で閉じてしまいましょう。
参照:【Unity】シーン表示上のシーンの向きに戸惑わない方法

シーンの保存

現在、シーン名は初期値の「Untitled」になったままなので、ここらへんで名前を付けて保存しましょう。
メニューの[File]>[Save Scenes]を選択すると、保存ダイアログ画面が表示されるので「main」で保存してください。

ラケットの位置をずらす

現在のラケットの位置が高めなので、少し下げましょう。
PositionZ-6.0から-8.5に変更します。
Unity_RacketZ.png

ボールの位置をずらす

現在のボールの位置が高めなので、ラケットの乗せた感じにするように下げましょう。
PositionZ0.0から-7.5に変更します。
Unity_BallZ.png

ブロックのタグを追加

ブロックを色で分けるように今後はしていきたいのですが、その前にどの色でもブロックと判定されるように、タグを追加します。

Blockの[Inspector]タブのTagのところでUntaggedをクリックして出るプルダウンから、Add Tag...を選びます。タグ&レイヤー画面(Tag & Layers)のTagの一覧の左にある+をクリックしBlockを追加します。
※名前を間違えたら-をクリックします。この際に(Removed)となりますが、プロジェクトをリロードした時に一覧から削除されるので気にせず、+をクリックして再追加してください。
Unity_BlockTagAdd.gif

タグ&レイヤー画面を閉じる方法が見当たらないのでHierarchyBlockを再選択します。[Inspector]タブのTagのプルダウンを選ぶと、Blockが現れるので選択します。
Unity_BlockTag.gif
※ブロックが複数ある場合、全てのTagをBlockに変更してください。(複数選択で一気に変更可能)

スクリプトの作成

先に空のゲームオブジェクトを作成します。
Hierarchyの下にある[Create]>[Create Empty]を選択して下さい。
Unity_EmptyAdd.png
名前をManagerとします。PositionはXYZを0にしておきます。
Unity_Manager.png

Managerの[Inspector]タブの一番下にある[Add Compornent]ボタンをクリックして、Scriptアセットを作成、追加します。ファイル形式はcsを選びます。
Unity_MangerScriptAdd.gif

ファイル名をManagerとします。そうすると、Assetsの中に「Manager.cs」が出来ています。
※先頭は英大文字です。間違えた場合、ファイル名と中身のクラス名の両方を修正してください。
Unity_ManagerScript.png

Manger.csを編集する

Asset内のMangerスクリプト(C#のアイコン)をダブルクリックすると、エディタが開きます。
優先的に開かれるエディタは、多くの場合はMonoDevelopというソフトが起動されます。

少し長いのでコピー&ペーストで入力してしまいましょう。
※MonoDevelopでペーストが効かないに時はMonoDevelopを閉じ直してください。

Manager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Manager : MonoBehaviour {


    // ゲームステート
    public enum GameState{
        Opening,
        Playing,
        Clear,
        Over
    }

    // 現在のゲーム進行状態
    public GameState currentState = GameState.Opening;
    // パネル
    private GameObject panel;
    // タイトル
    private GameObject title;
    // ラケット
    private GameObject racket;
    // ボール
    private GameObject ball;

    // テキスト
    private Text text;
    // ステージ
    private int stage = 1;

    // Use this for initialization
    void Start () {
        // Panelゲームオブジェクトを検索し取得する
        panel = GameObject.Find("Panel");       
        // Titleゲームオブジェクトを検索し取得する
        title = GameObject.Find("Title");       
        // ラケットゲームオブジェクトを検索し取得する
        racket = GameObject.Find("Racket");     
        // ボールゲームオブジェクトを検索し取得する
        ball = GameObject.Find("Ball");
        // テキスト
        text = title.GetComponent<Text> ();

        // オープニング
        GameOpening ();
    }

    // Update is called once per frame
    void Update () {
        // ゲーム中ではなく、Spaceキーが押されたらtrueを返す。
        if(currentState == GameState.Opening && Input.GetKeyDown (KeyCode.Space)) {
            dispatch (GameState.Playing);                   
        }

        if (currentState == GameState.Playing) {
            // ゲームクリアーの判定
            if (GameObject.FindGameObjectsWithTag ("Block").Length == 0) {
                dispatch (GameState.Clear);                 
            }
        }
    }

    // 状態による振り分け処理
    public void dispatch (GameState state){
        GameState oldState = currentState;

        currentState = state;
        switch (state) {
            case GameState.Opening:
                GameOpening ();
                break;
            case GameState.Playing:
                GameStart ();
                break;
            case GameState.Clear :
                GameClear ();
                break;
            case GameState.Over:
                if (oldState == GameState.Playing) {
                    GameOver ();
                }
                break;
        }

    }

    // オープニング処理
    void GameOpening ()
    {
        currentState = GameState.Opening;

        // ボールの動作停止
        Time.timeScale = 0;

        // タイトル名のセット
        SetTitle ("Game Start", Color.green);

        // ラケットの初期位置をセット
        racket.transform.position = new Vector3 (0.0f, 0.0f, -8.5f);
        // ボールの初期位置をセット
        ball.transform.position = new Vector3 (0.0f, 0.0f, -7.5f);

    }

    // ゲームスタート処理
    void GameStart ()
    {
        // パネル非活性化
        panel.SetActive (false);

        // ボールの動作開始
        Time.timeScale = 1.0f;

        // ボールの初期化
        FindObjectOfType<Ball> ().Init ();
    }

    // ゲームクリアー処理
    void GameClear ()
    {
        stage++;

        // タイトル名のセット
        SetTitle ("Game Clear", Color.yellow);

        // 3秒後にオープニング処理を呼び出す
        Invoke("GameOpening", 3f);
    }

    // ゲームオーバー処理
    void GameOver ()
    {
        // タイトル名のセット
        SetTitle ("Game Over", Color.red);
        // ステージ初期値
        stage = 1;

        // ハイスコアの保存
        FindObjectOfType<Score> ().Save();

        // 3秒後にオープニング処理を呼び出す
        Invoke("GameOpening", 3f);
    }

    // オープニング処理
    void SetTitle (string message, Color color)
    {
        // タイトル名のセット
        text.text = message;
        text.color = color;
        // パネル活性化
        panel.SetActive (true);
    }
}

少し説明します。

  • Start処理で使用するオブジェクトを予め格納しておくことで、都度オブジェクト検索するのを防いでいます。
  • ブロックのタグ検索をして何も見つからない場合にゲームクリアーと判定しています。
  • Time.timeScale = 0として、ボールの動きを停止しています。
  • dispatch処理はpublic扱いにして他スクリプトから呼べるようにしています。
  • タイトル表示有無はPanelを活性/非活性で実現させています。

Ball.csを編集する

一番下にある壁にボールが当たったら、ミスとしたとしてゲームオーバーにします。
ボールの衝突判定処理(OnCollisionEnter)を追加します。

また、ゲームスタート時にボールの加速値を初期化したいため、Startにある処理をManagerスクリプトから呼べるようpublicを付けたInit処理を作成し、処理内部を移動させます。

Ball.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Ball : MonoBehaviour {

    private float speed = 20.0f;    //これを追加

    // Use this for initialization
    void Start () {

    }

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

    }

    void OnCollisionEnter(Collision collision) {
        //衝突判定
        if (collision.gameObject.name == "BottomWall") {
            // ゲームオーバー処理を呼ぶ
            FindObjectOfType<Manager>().dispatch(Manager.GameState.Over);
        }
    }

    public void Init(){
        // 加速値を初期化
        Rigidbody rd = this.GetComponent<Rigidbody> ();
        rd.velocity = Vector3.zero;
        rd.AddForce(
            (transform.forward + transform.right) * speed, 
            ForceMode.VelocityChange);
    }
}

実装を確認する

エディタ画面中央の実行ボタンを押してみましょう。
オープニング画面でスペースキーで押すとゲームスタートとなります。
ボールが一番下の壁に当たるとゲームオーバーになり、3秒後にオープニング画面に自動で戻ります。また、ブロックが全て無くなるとゲームクリアーになり、これも3秒後にオープニング画面に自動で戻ります。
Unity_GameStart.png

すぐ気が付くと思いますが、実はブロックの初期化処理が出来ていません。
ブロックをスクリプトで配置する上でPrefab(プレハブ)を使いたいのですが、説明が長くなるので次回にします。

学んだこと

  • Panelを使うことで背景に"もや"がかかるように表示できること
  • 空のゲームオブジェクトを作成して管理するスクリプトを使うこと
  • タグを使うことで複数種類があっても判定がタグ1つで済むこと
  • マネージャクラスにて進行状況により処理を振り分けること

参照

14
18
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
14
18