超初心者のゲーム開発記~Unity~

超初心者がUnityでゲーム(目標はAndroidアプリ )を開発していくブログです!

【Unity 2Dローグライク】公式チュートリアルをやってみる part.13~UIを作る

こんにちは!ヤギです!

Unityの2Dローグライクの
公式チュートリアルをやってみる part.13になります!
※前回(part.12)の記事はこちら
www.yagigame.com

記事概要

今回の記事では、食料を表示するUIやレベルの管理についてご説明していきます。
※part.13に対応する公式チュートリアルの章は、Enemy Animator Controllerです!
unity3d.com

※この記事は、ひよこのたまご様の記事をリスペクトしています。
hiyotama.hatenablog.com

UIの作成

まず初めに、背景から作成していきます。
1. GameObject > UI > Canvasをクリックし、UIを表示する空間を作成します。
f:id:yagigame:20181105222851p:plain
2. GameObject > UI > Imageをクリックし、背景を表示する空間を作成します。
※Canvasの子として作成されます。
f:id:yagigame:20181105223101p:plain
※Scene viewからCanvasとimageを確認すると、Canvasの一部にimageがあります。
f:id:yagigame:20181105223330p:plain
3. imageのAnchorをクリックし、option(またはalt)を押しながら、右下の広げるマークをクリックします。
※これでCanvas全体に、imageが広がります。
f:id:yagigame:20181105223657p:plainf:id:yagigame:20181105223724p:plain
4. Colorをクリックし、色を黒色に変更します。
f:id:yagigame:20181105223939p:plain
5. imageの名前をLevelImageとします。
6. GameObject > UI > Textをクリックし、レベルを表示する空間を作成します。
※名前をLevelTextとします
7. LevelTextのInspectorから以下の内容の修正を行います。
・Font Size を32にする。
・FontをPressStart2Pに変更する。
・Colorを白にする。
・Horizon OverflowとVectical OverflowをOverflowにする。
・Alignmentを真ん中にする。
・TextをDay 1とする。
※これを行うと、次の画面のようになります!
f:id:yagigame:20181105225218p:plain
8. Hierarchy上で、LevelTextをLevelImageの文字の上にドラッグします。
※これで、LevelTextがLevelImageの子になります。
f:id:yagigame:20181105225408p:plain
9. GameObject > UI > Textをクリックし、食料を表示する空間を作成します。
※名前をFoodTextとします。
10. Anchorをクリックし、option(またはalt)を押しながら画面中央の下をクリックします。
f:id:yagigame:20181106205313p:plain
11. FoodTextのInspectorから以下の内容の修正を行います。
・Font Size を24にする。
・FontをPressStart2Pに変更する。
・Colorを白にする。
・Horizon OverflowとVectical OverflowをOverflowにする。
・Alignmentを真ん中にする。
・TextをFood: 100とする。
・Anchor > Max > Y を0.05にする。
・Anchor > Min > Y を0.05にする。
・Pos Yを0にする。
※これで、画面下に食料が表示されます。
f:id:yagigame:20181106210453p:plainf:id:yagigame:20181106205719p:plain
12. Hierarchy上で、FoodTextをCanvasの直下にドラッグします。
※これで、FoodTextがCanvasの子になります。Canvasの下の方にあるオブジェクトから優先されるため、FoodTextが表示されなくなります。
f:id:yagigame:20181106210055p:plainf:id:yagigame:20181106210056p:plain
※一通りUIが完成しました。次にUIを制御するスクリプトを作成していきます。

UIを制御するスクリプト(レベルの表示)の作成

1. GameManager.csを開き、スクリプトにUIを制御する部分を追加していきます。
※GameManage.csにはレベル(Day)の表示を制御する部分を追加していきます。
※ソース内に解説を記載しました。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI; //part.13で追加 UIを使うために必要

public class GameManager : MonoBehaviour
{
    //ここからpart.13で追加
    public float levelStartDelay = 2f; //レベルスタート時の時間間隔
    private Text levelText; //レベルを表示するテキスト
    private GameObject levelImage; //UIの表示領域 表示のオンオフを切り替える
    private bool doingSetup; //設定中かどうかのフラグ
    //ここまでpart.13で追加

    //ここからpart.12で追加
    public float turnDelay = .1f; //1ターンの時間
    private List<Enemy> enemies; //複数の敵を管理
    private bool enemiesMoving; //敵の移動フラグ
    //ここまでpart.12で追加

    //クラスに属し、複数のシーンで使われる変数を宣言
    //Staticにすることで、他のスクリプトからも呼び出すことができます
    public static GameManager instance = null;

    //ここから:part.10で追加
    public int playerFoodPoints = 100; //プレイヤーの食料
    //HideInspectorをつけることで、Inspector viewに表示されなくなる
    [HideInInspector] public bool playerTurn = true; //プレイヤーのターンかの判定フラグ
    //ここまで:part.10で追加

    //BoardManager型の変数を宣言
    private BoardManager boardScript;

    //テストとして、敵が出現するレベルの3とする
    //part.13で1に修正
    private int level = 1;

    // AwakeはStartよりも前、最初に呼ばれる
    void Awake()
    {
        //GameManagerが存在しなければ、このオブジェクトを設定する
        if (instance == null)
            instance = this;

        else if (instance != this)
            //すでに存在する場合、このオブジェクトは不要なため破壊する
            Destroy(gameObject);

        //シーン遷移時に、このオブジェクトは破壊せず引き継ぐ
        DontDestroyOnLoad(gameObject);

        //ここからpart.12で追加
        enemies = new List<Enemy>(); //敵を初期化
        //ここまでpart.12で追加

        //BoardManagerのコンポーネントを取得
        boardScript = GetComponent<BoardManager>();
        //ステージ生成の関数を呼ぶための、関数を呼ぶ
        InitGame();
    }

    //ここからpart.13で追加
    //シーンが飛び出されたタイミングで実行される
    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
    static public void CallbackInitialization()
    {
        //register the callback to be called everytime the scene is loaded
        SceneManager.sceneLoaded += OnSceneLoaded;
    }

    //シーンが呼び出されたタイミングで初期化する
    static private void OnSceneLoaded(Scene arg0, LoadSceneMode arg1)
    {
        instance.level++;
        instance.InitGame();
    }
    //ここまでpart.13で追加

    void InitGame()
    {
        //ここからpart.13で追加
        //設定中フラグをオンにする
        doingSetup = true;
        //levelImageにUIを設定する
        levelImage = GameObject.Find("LevelImage");
        //levelTextにUIのテキストを取得し設定する
        levelText = GameObject.Find("LevelText").GetComponent<Text>();
        //levelTextにゲーム内のlevelを設定する
        levelText.text = "Day " + level;
        //UIを表示する
        levelImage.SetActive(true);
        //2秒後にUIを非表示にする
        Invoke("HideLevelImage", levelStartDelay);
        //ここまでpart.13で追加

        //ここからpart.12で追加
        enemies.Clear(); //ステージ移動時は敵をリセットする
        //ここまでpart.12で追加

        //ステージ生成の関数を呼ぶ
        boardScript.SetupScene(level);
    }

    //ここからpart.13で追加
    //UIを非表示にする
    private void HideLevelImage()
    {
        levelImage.SetActive(false);
        doingSetup = false;
    }
    //ここまでpart.13で追加

    //ここまでpart.10で追加

    //ここからpart.12で追加
    void Update()
    {
        //プレイヤーのターンか、敵の動いている場合は、アップデートしない
        //part.13でdoigSetupを追加
        if (playerTurn || enemiesMoving ||doingSetup)
            return;
        //敵の動いていない、敵のターンのみ敵を動かす
        StartCoroutine(MoveEnemies());
    }

    //敵をリストに加える処理
    public void AddEnemyList(Enemy script){
        enemies.Add(script);
    }

   //敵を移動させる処理
    IEnumerator MoveEnemies()
    {
        //エネミー移動フラグをtrueにする
        enemiesMoving = true;
        //1ターン待つ
        yield return new WaitForSeconds(turnDelay);
        //敵がいなければ
        if (enemies.Count == 0)
        {
            //1ターン待つ
            yield return new WaitForSeconds(turnDelay);
        }
        //敵の数だけ、敵を移動させる
        for (int i = 0; i < enemies.Count; i++)
        {
            enemies[i].MoveEnemy();
            //1ターン待つ
            yield return new WaitForSeconds(turnDelay);
        }

        //プレイヤーのターンにする
        playerTurn = true;
        enemiesMoving = false;
    }
    //ここまでpart.12で追加

    //ここかpart.10で追加
    public void GameOver()
    {
        //ここからpart.13で追加
        //ゲームオーバー時のテキストを設定
        levelText.text = "After " + level + " days, you starved.";
        //UIを表示
        levelImage.SetActive(true);
        //enabledをfalseにすることで、GameManagerが無効になる
        enabled = false;
    }
}

※これで、Day 1などが表示されるようになりました!

UIを制御するスクリプト(食料の表示)の作成

1. Player.csを開き、スクリプトにUIを制御する部分を追加していきます。
※Player.csには食料(Food)の表示を制御する部分を追加していきます。
※ソースの中の解説をご参照ください!

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement; //シーンの読み込みに必要
using UnityEngine.UI; //part.13で追加

//MovingObjectクラスを継承する
public class Player : MovingObject {

    //ここからpart.13で追加
    public Text foodText; //食料を表示するテキスト
    //ここまでpart.13で追加

    public int wallDamage = 1; //壁へのダメージ量
    public int pointsPerFood = 10; //食べ物の回復量
    public int pointsPerSoda = 20; //ソーダの回復量
    public float restartLevelDeray = 1f; //ステージ移動時の時間

    private Animator animator; //アニメーション用変数
    private int food; //食料

	// Use this for initialization
    //MovingObjectクラスのStartを継承する
    protected override void Start () {
        //animatorのコンポーネントを設定
        animator = GetComponent<Animator>();
        //foodをステージ間で引き継げるように、GameManagerから設定
        food = GameManager.instance.playerFoodPoints;
        //ここからpart.13で追加
        //foodTextを初期化
        foodText.text = "Food: " + food;
        //ここまでpart.13で追加
        //GameObjectのStartを呼び出す
        base.Start();
	}

    //PlayerのfoodをGameManageに保存する
    public void OnDisable()
    {
        GameManager.instance.playerFoodPoints = food;
    }

    protected override void AttemptMove<T>(int xDir, int yDir)
    {
        //移動するたびに食料が減る
        food--;
        //ここからpart.13で追加
        //減らした食料をUIに表示する
        foodText.text = "Food: " + food;
        //ここまでpart.13で追加
        //MovingObjectのAttemptMoveを呼び出す
        base.AttemptMove<T>(xDir, yDir);

        RaycastHit2D hit;

        //ゲームオーバーか確認
        CheckIfGameOver();
        //プレイヤーのターン終了
        GameManager.instance.playerTurn = false;
    }

    //プレイヤーが壁にぶつかった場合、壁をチョップする
    protected override void OnCantMove<T>(T component)
    {
        //Wallスクリプトを使えるように設定
        Wall hitWall = component as Wall;
        //壁にダメージを与える
        hitWall.DamageWall(wallDamage);
        //チョップするアニメーションを呼び出す
        animator.SetTrigger("PlayerChop");
    }

    //プレイヤーが、Exit、Food、Sodaと接触した場合に呼び出す
    private void OnTriggerEnter2D(Collider2D other)
    {
        //Exitと接触した場合
        if(other.tag == "Exit"){
            //ステージ移動の時間分待ってから、次のツテージに移動する
            Invoke("Restart", restartLevelDeray);
            enabled = false;
        }else if(other.tag == "Food"){ //食料と接触した場合
            //食料を回復
            food += pointsPerFood;
            //ここからpart.13で追加
            //増えた食料をUIに表示する
            foodText.text = "+"+pointsPerFood+" Food: " + food;
            //ここまでpart.13で追加
            //食料を削除
            other.gameObject.SetActive(false);
        }else if(other.tag == "Soda"){
            //食料を回復
            food += pointsPerSoda;
            //ここからpart.13で追加
            //増えた食料をUIに表示する
            foodText.text = "+" + pointsPerFood + " Food: " + food;
            //ここまでpart.13で追加
            //ソーダを削除
            other.gameObject.SetActive(false);
        }
    }

    //プレイヤーがExitに到達した場合、次のステージを呼び出す
    private void Restart(){
        //シーンを呼び直す
        SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex,LoadSceneMode.Single);
    }

    //プレイヤーが敵に攻撃された場合、食料を減らす
    public void LoseFood(int loss){
        //攻撃を受けたアニメーションを呼び出す
        animator.SetTrigger("PlayerHit");
        //食料を減らす
        food -= loss;
        //ここからpart.13で追加
        //減らした食料をUIに表示する
        foodText.text = "-" + loss+ " Food: " + food;
        //ここまでpart.13で追加
        //ゲームオーバーか判定
        CheckIfGameOver();
    }

    // Update is called once per frame
    void Update () {
        //プレイヤーのターンではない場合、何も実行しない
        if (!GameManager.instance.playerTurn)
        {
            return;
        }
      
        //左右の移動
        int horizontal = 0;
        //上下の移動
        int vertical = 0;
        //左右の移動量を受け取る
        horizontal = (int)Input.GetAxisRaw("Horizontal");
        //上下の移動量を受け取る
        vertical = (int)Input.GetAxisRaw("Vertical");

        //上下左右のいずれかに移動を制限する
        if(horizontal != 0){
            vertical = 0;
        }

        //左右上下のいずれかに移動する場合
        if(horizontal != 0 || vertical != 0){
            //プレイヤーの侵攻方向に壁があるか確認
            AttemptMove<Wall>(horizontal, vertical);
        }
	}

    //食料が0いかになった場合、ゲームオーバーにする
    private void CheckIfGameOver(){
        if(food <= 0){
            //GameManagerのGameOverを呼び出す
            GameManager.instance.GameOver();
        }
    }
}

2. Hierarchy view > PlayerのInspector内の Player Script > FoodTextに、Canvas > FoodTextをドラッグして設定します。
f:id:yagigame:20181106215354p:plain
※これで、ゲーム内で食料が表示されるようになります。

ゲームを実行してみよう!

ゲームを実行してみましょう!
プレイヤーが移動すると、食料が減り、Exitにたどり着くと、ステージが遷移することができると思います!
f:id:yagigame:20181106215858p:plain

次回予告

次回の記事では、ゲーム内の効果音などサウンドについて書いていきたいと思います。
※次回の記事はこちら
www.yagigame.com

※Unityのチュートリアルの章としては、次回は(Audio and Sound Manager)となります!

読んでいただきありがとうございました!