top of page

3D脱出ゲーム編

Lesson4 UIを実装しよう

4-1

1

Sprite Editorの
​導入

4-1 アイテム欄の実装

4-1 アイテム欄の実装

 今所持しているアイテムを確認するためのアイテム欄を実装していきます。

​ まずは素材を仮配置していきましょう。お好みのウィンドウ素材を用意するか、サンプルの素材をダウンロードしてください。

​ ダウンロードできたらSpriteフォルダにドラッグ&ドロップして追加しましょう。

 3Dゲームでは、追加した画像をUIとして使用する際はTexture Typeを変更する必要があります。

 追加したウィンドウ画像を選択して、Texture Typeを「Sprite (2D and UI)」に変更してください。変更できたら、右下のApplyボタンを押して反映させましょう。

 ウィンドウを作成するときはSprite EditorのSlice機能を使うと便利です。

 普通にウィンドウを変形させると左の画像のように四隅が歪んでしまいますが、この機能を使うと右の画像のように四隅の大きさを固定したままウィンドウの大きさを変えることができます。

​(2Dランゲーム編Lesson4参照

 ウィンドウのスプライトの設定を変更します。SpriteModeをMultipleに、MeshTypeをFull Rectに変更してください。変更したら右下のApplyボタンをクリックします。

 Sprite Editorでウィンドウ画像をスライスすることができますが、Sprite Editorを開こうとすると警告が表示されてしまいます。Sprite​ Editorは2Dゲームの場合デフォルトで導入されていますが、3Dゲームの場合は手動でパッケージをインストールする必要があります。

 「Window」→「Package Manager」を開いてください。

 左上のPackagesを「Unity Registry」に変更して​​、右上の検索欄に「Sprite」と入力しましょう。「2D Sprite」が左側に表示されるので選択して、右下のInstallボタンをクリックしてください。

2

ウィンドウ画像の

​分割

 ここまでの操作が完了したらPackage Managerを閉じてください。

​ ウィンドウ画像を選択してSprite Editorを開きましょう。

 ウィンドウ画像全体を囲うようにマウスをドラッグしてください。

 右下にSpriteウィンドウが表示されるのでLeft、Top、Right、Bottom全てに40を入力してください。ウィンドウ画像が9つに分割されます。

​ 設定できたら右上のApplyボタンを押して、Sprite Editorを閉じてください。

3

空オブジェクトの
​追加

 実際にウィンドウやテキストを配置していきます。特に新しい要素はないので、サンプルの通りに配置していってください。

​ 

 まずはオブジェクトを管理しやすいように、Canvasの子オブジェクトに空オブジェクトを追加しましょう。名前はItemUIにしておきます。

​ このオブジェクトは特に設定は必要ありません。アイテム欄のUIはこのオブジェクトの子オブジェクトに配置していきましょう。

4

​ウィンドウの追加

 ItemUIの子オブジェクトにImageを追加してください。名前はMainWindowにしておきます。

 MainWindowのRect Transformを設定しましょう。まずはアンカーを左上に設定します。

 Rect Transform左上からアンカーを左上に設定してください。

 アンカーを設定してから座標を設定しないと、座標がずれるので注意してください。

 Rect Transformの座標や幅を調整してください。これでウィンドウ画像が画面左上に表示されます。

【サンプルの入力例】

PosX : -286

PosY : 138

Width/Height : 160

Scale : 0.8

 ImageコンポーネントのSauce Imageには、先ほどスライスしたウィンドウの画像を設定してください。

5

サブ​ウィンドウの

追加

 追加でサブウィンドウを2つ表示します。

​ MainWindowをコピーしてSubWindow1とSubWindow2を作成してください。

 SubWindow1とSubWindow2のRect Transformを設定してください。

【サンプルの入力例 SubWindow1】

Anchor : 左上

PosX : -310

PosY : 34

Width/Height : 100

Scale : 0.8
 

【サンプルの入力例 SubWindow2】

Anchor : 左上

PosX : -310

PosY : -48

Width/Height : 100

Scale : 0.8

6

アイテム説明欄の
​追加

 残りのUIを完成させましょう。

 Imageを追加して名前をItemDataBGにしておきます。ItemDataBGの子オブジェクトにTextMeshProを2つ追加して名前をNameとMessageにしましょう。

 別でTextMeshProを追加して名前をTextLBRBにしておきます。

 ItemDataBGのパラメータを設定してください。黒い半透明の背景が表示されます。


【サンプルの入力例 ItemDataBG】

Anchor : 左上

PosX : 12

PosY : 160

Width : 470

Height : 80

Sauce Image : None(なし)

Color : R=0 G=0 B=0 A=155

 次にアイテム名を表示する場所であるNameオブジェクトの設定をしていきます。ItemDataBGの子オブジェクトになっている点に注意しましょう。

【サンプルの入力例 Name】

Anchor : 中央

PosX : -14

PosY : 0

Width : 400

Height : 50

TextInput : (仮入力なのでなんでも可)

Font Asset : Corporate-Mincho-ver3 SDF

Font Size : 36

Alignment : 左寄せ 中央

 アイテムの説明を表示する場所であるMessageオブジェクトの設定をしていきます。こちらもItemDataBGの子オブジェクトになります。

【サンプルの入力例 Message】

Anchor : 中央

PosX : 130

PosY : 0

Width : 200

Height : 50

TextInput : (仮入力なのでなんでも可)

Font Asset : Corporate-Mincho-ver3 SDF

Font Size : 20
​Paragraph : -26

Alignment : 左寄せ 中央

3_4_20
3_4_19

 TextLBRBオブジェクトの設定をしていきます。

【サンプルの入力例 TextLBRB】

Anchor : 左端

PosX : -240

PosY : -70

Width : 200

Height : 50

TextInput : ↑LB (改行) ↓RB

Font Asset : Corporate-Mincho-ver3 SDF

Font Size : 18

​Paragraph : -40

Alignment : 左寄せ 中央

3_4_21
3_4_22

 これでアイテム欄の仮配置が完了しました。

7

サブカメラの追加

 ここからはアイテム欄の実装をしていきます。

 アイテム欄には所持しているアイテムの画像を表示したいところですが、全てのアイテムの画像を用意するのはかなりの手間です。また、アイテムを後から追加したい時も、その都度画像を用意しないといけなくなってしまいます。

​ 今回はメインカメラとは別にサブカメラを配置して、サブカメラに映った内容をUIに表示することでアイテム欄にアイテムの画像を表示してみましょう(EX 複数のカメラを扱う も参照してください)

 まずはUIに使用するカメラを配置しましょう。​

 Cameraを追加してください。名前はUICamera1にしておきます。

8

レイヤーの追加

 Cameraの設定をする前にレイヤーを追加しましょう。

 「Add Layer」を選択して、適当な場所にUI_Itemレイヤーを追加しておきます。

9

カメラの設定

 それではUI用Cameraの設定をしていきます。

​ Cameraの座標をX=300 Y=0 Z=0 に設定してください(回転は全て0、大きさは全て1のまま)

 CameraクラスのProjection内にあるNear(近平面)と0.1に、Far(遠平面)を8に設定してください。カメラの近くにアイテムを置くため近平面を近くして、遠くの情報は必要ないため遠平面も非常に近い距離に設定しています。

 RenderingのCulling Maskを設定しましょう。ここで設定したレイヤーのオブジェクトがカメラに表示されます。

​ デフォルトではEverythingになっていますが、一旦Nothingを選択して全てのチェックを外してから、UI_Itemを選択してください。これでUI_Itemレイヤーに設定されたアイテムだけがカメラに映るようになります。

 EmvironmentのBackground Typeを「Solid Color」に変更してください。後ほどアイテム画像を表示した際に背景の空が表示されないようにしています。

10

サブカメラの
​出力先を作成

 次にカメラの内容を出力するためのRenderTextureを作成します。RenderTextureフォルダを作成してください。

 「Create」→「Render Texture」を追加してください。​

 Render Textureが生成されるので、名前をItemUI1にしておいてください。​

​ インスペクターでRender Textureの設定ができますが、デフォルトのままで構いません。

 作成したRender TextureをカメラのOutput Textureにドラッグ&ドロップしてください。

​ これでカメラの内容がItemUI1へ出力されるようになります。

 ItemUI1の内容をUIとして表示してみましょう。

​ MainWindowの子オブジェクトにRaw Imageを追加してください。今まで使っていたImageとは異なるので注意しましょう。

 Raw ImageはSpriteではなくTextureを表示することができます。普段はあまり使いませんが、今回のようにCameraの内容を表示したい時などに使用します。

 Raw Imageの使用例として、ゲーム画面をキャプチャしてUIとして使用する処理を実装してみましょう。

 今回はサンプルとして以下のようなシーンを用意しました。

​【素材】Low-Poly Simple Nature Pack

 スクリプトを作成して、以下のように入力してください。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;   // UIを扱うので追加

 

public class Capture : MonoBehaviour
{
    [SerializeField]
    RawImage TargetImage;       // 表示先のUI
    [SerializeField]
    GameObject ObjectUI;        // UI(撮影中は消す)

 

    Texture2D CaptureTexture;   // 保存先のテクスチャ
 

    const float ROTATE_SPEED = 30.0f;    // カメラの回転速度
 

    private void Awake()
    {
        // 画面サイズのテクスチャを作成
        CaptureTexture = new Texture2D(Screen.width, Screen.height, TextureFormat.RGB24, false);
    }

 

    void Update()
    {
        // カメラの回転
        if (Input.GetKey(KeyCode.A))
        {
            transform.Rotate(new Vector3(0.0f, -ROTATE_SPEED * Time.deltaTime, 0.0f));
        }
        if (Input.GetKey(KeyCode.D))
        {
            transform.Rotate(new Vector3(0.0f, ROTATE_SPEED * Time.deltaTime, 0.0f));
        }

 

        // Enterキーを押して画面キャプチャ
        if (Input.GetKeyDown(KeyCode.Return))
        {
            StartCoroutine(TakeCapture());
        }
    }

 

    IEnumerator TakeCapture()
    {
        // UIを非表示にする
        ObjectUI.SetActive(false);

 

        // 描画が終わるまで待つ
        yield return new WaitForEndOfFrame();

 

        // テクスチャにゲーム画面の内容を書き込む
        CaptureTexture.ReadPixels(new Rect(0.0f, 0.0f, Screen.width, Screen.height), 0, 0, false);
        CaptureTexture.Apply();
        // UIにテクスチャを反映
        TargetImage.texture = CaptureTexture;

        // UIを表示する
        ObjectUI.SetActive(true);
    }
}

 このスクリプトはカメラにアタッチすることを想定しています。

 カメラ(自分)の回転処理と画面をキャプチャしてUIに反映する処理が実装されています。

 また、画面をキャプチャするためには描画が終わるまで待つ必要があるのですが、ここではそのためにコルーチンというものを使っています。

【コルーチンについて】はじめてのコルーチン!これさえ読めば基礎はカンペキ

 

 スクリプトをメインカメラにアタッチして、パラメータを設定しましょう。

 TargetImageには表示先のRawImageを指定して、ObjectUIにはUI用のキャンバスを指定します(キャプチャした画面にUIが映り込まないように)

 ゲームを実行して、Enterキーを押すと画面が撮影され、Raw Imageに反映されます。AキーとDキーでカメラを回転させて、撮影処理が動くことを確認してみてください。

 Raw ImageはImageと異なり、ゲーム内で動的に変化するものを描画することに適しています

 基本的にはImageを使うべきですが、行いたい処理に応じてRaw Imageに切り替えるようにしましょう。

 ちなみに撮影した画面をファイルとして保存したい場合はCaptureScreenshot関数が適しています。

【参考】スクリーンショットを保存する

Unity Tips!

 WidthとHeightを120に設定して、TextureをItemUI1(先程作成したRender Texture)に設定してください。

 UI用カメラにアタッチされているAudio Listenerを外しておきましょう(Cameraを作成した際に自動でついているコンポーネントですが、Main Cameraに既にアタッチされているため、このままだとエラーが出てしまいます)

​ Audio Listener右端の3つの点をクリックして「Remove Component」をクリックしてください。

 Audio Listenerが2つ以上あった場合、以下のような警告が表示されます。シーン上にAudio Listenerが2つ以上存在する時は1つのAudio Listenerが有効になり、それ以外のAudio Listenerは無効になります。

11

UIカメラの確認

 UIカメラが正常に動作しているかどうか確認してみましょう。適当にUIカメラの範囲内にCubeを配置してください。

 このままではカメラにCubeが映らないので、LayerをUI_Itemに設定するのを忘れないようにしましょう。

 カメラに映ったCubeがウィンドウ内に表示できていればOKです。​確認できたらCubeは削除してください。

12

サブウィンドウの
​更新

 残り2つのウィンドウも設定しましょう。

​ まずはUIカメラを複製してください。​UICamera2の座標をX=320に、UICamera3の座標をX=340にしてください。​他の設定はそのままで構いません。

 Render Textureを複製して、それぞれのカメラに設定しましょう。別々のRender Textureに3つのカメラの内容が出力されるようにします。

 SubWindowの子オブジェクトにもRaw Imageを追加しましょう。

 SubWindow1(上のウィンドウ)のRaw ImageにはItemUI2を、SubWindow2(下のウィンドウ)のRaw ImageにはItemUI3を設定してください。

13

アイテムにUI用

​パラメータを追加

 後は各カメラに合う座標にアイテムの3Dモデルを生成するだけになります。​​まずはアイテムごとにUI表示中の回転量や座標補正を指定できるようにしましょう。

 ItemDataスクリプトを開いて、赤い部分のコードを追加してください。

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

 

// アイテム用のデータベース
[CreateAssetMenu(fileName = "ItemDataBase", menuName = "CreateItemDataBase")]
public class ItemData : ScriptableObject
{
    // アイテム情報の構造体
    [System.Serializable]
    public struct Item
    {
        public string ItemName;         // アイテム名
        [Multiline(2)]
        public string ItemExplanation;  // アイテム欄で表示する説明文
        public GameObject ItemPrefab;   // アイテムを捨てた時に生成するオブジェクト

 

        // 設置用パラメータ
        public Vector3 ItemPivot;       // アイテムを置いたときに座標を補正する量

 

        // UI用パラメータ
        public Vector3 UI_Offset;       // UIで表示するときに補正する座標
        public Vector3 UI_Rotation;     // UIで表示するときの回転量
        public Vector3 UI_Scale;        // UIで表示するときの大きさ

    }

 

    // アイテムリストの可変長配列
    public Item[] Items;
}

14

UI用アイテムを

​生成する

 次にUI更新の処理を実装しましょう。

​ UI_Itemスクリプトを作成して、以下のように入力してください。少々長いですが、特別な処理はありません。

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

 

public class UI_Item : MonoBehaviour
{
    GameManager m_gameManager;

 

    [SerializeField]
    GameObject[] ItemCamera;    // UI用カメラ

 

    [SerializeField]
    TextMeshProUGUI ItemNameText, ItemMessageText;

 

    [SerializeField]
    Vector3 ItemOffset;     // アイテム生成座標の補正量(共通)

 

    // UI用に生成したアイテム
    GameObject[] m_itemObject;

 

    // 表示内容を更新する アイテムインベントリを更新したタイミングで呼ぶ
    public void UpdateUI()
    {
        // 初期化
        if (m_gameManager == null)
        {
            m_gameManager = 
                GameObject.FindGameObjectWithTag("GameController").GetComponent<GameManager>();
            // 要素数はカメラの数と同じ
            m_itemObject = new GameObject[ItemCamera.Length];
        }

 

        // 変数を準備する
        Vector3 itemPos;
        int itemNo;
        int selectNo = m_gameManager.GetSelectItemNo();

 

        // アイテム欄を更新する
        for (int i = 0; i < ItemCamera.Length; i++)
        {
            // 指定スロットのアイテム番号を取得
            itemNo = m_gameManager.GetItemID(selectNo);
            // アイテムを一旦削除
            Destroy(m_item
Object[i]);
 

            // 名前や説明欄の更新
            if (selectNo == m_gameManager.GetSelectItemNo())
            {

                // 表示の有無
                if (itemNo == -1)
                {
                    // アイテムがないため説明欄は非表示
                    ItemNameText.enabled = false;
                    ItemMessageText.enabled = false;
                }
                else
                {
                    // 説明欄を表示
                    ItemNameText.enabled = true;
                    ItemMessageText.enabled = true;

                    // テキスト更新
                    ItemNameText.text = m_gameManager.GetItemData().Items[itemNo].ItemName;
                    ItemMessageText.text = m_gameManager.GetItemData().Items[itemNo].ItemExplanation;
                }
            }

 

            // セレクト番号の更新
            selectNo++;
            if (selectNo > ItemCamera.Length - 1)
            {
                selectNo = 0;
            }

            if (itemNo == -1)
            {
                // アイテムがないためここで終了
                continue;
            }

 

            // アイテムを生成
            // まずは座標を決める

            itemPos = ItemCamera[i].transform.position;
            itemPos += ItemOffset;
            itemPos += m_gameManager.GetItemData().Items[itemNo].UI_Offset;

 

            // 生成
            GameObject item = Instantiate(m_gameManager.GetItemData().Items[itemNo].ItemPrefab,
                itemPos, Quaternion.identity);

            // アイテムを覚えておく
            m_itemObject[i] = item;

 

            // 回転
            item.transform.Rotate(m_gameManager.GetItemData().Items[itemNo].UI_Rotation,
                Space.World);
            // 大きさ調整
            item.transform.localScale = m_gameManager.GetItemData().Items[itemNo].UI_Scale;

 

            // レイヤーをUI用に変更(名前で検索)
            item.layer = LayerMask.NameToLayer("UI_Item");
        }

    }

}

 後はアイテム欄を更新したときにUIを更新させるだけです。

 GameManagerスクリプトを開いて、赤い部分のコードを追加してください。

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

 

public class GameManager : MonoBehaviour
{
    // アイテムデータ
    [SerializeField]
    ItemData Item_Data;
    public ItemData GetItemData()
    {
        return Item_Data;
    }

 

    // 選択中のアイテム番号(アイテム欄配列の番号)
    [SerializeField]
    int SelectItemNo = 0;
    public int GetSelectItemNo()
    {
        return SelectItemNo;
    }

 

    // アイテム欄
    [SerializeField]
    int[] ItemID;
    // 引数番スロットのアイテムを取得
    public int GetItemID(int no)
    {
        return ItemID[no];
    }

 

    // アイテム欄のUI
    [SerializeField]
    UI_Item ItemUI;

 

    // アイテムを取得する
    // アイテム欄に空きがあったらtrue なかったらfalseを返す
    public bool GetItem(int getItemID)
    {
        int selectID = SelectItemNo;

 

        // 順番にアイテム欄を確認していって、空いている場所にIDを格納
        for (int i = 0; i < ItemID.Length; i++)
        {
            if (ItemID[selectID] == -1)
            {
                // 空きがあるのでアイテムIDを格納
                ItemID[selectID] = getItemID;

                // UIを更新
                ItemUI.UpdateUI();

 

                return true;
            }

 

            selectID++;
            if (selectID > ItemID.Length - 1)
            {
                // オーバーしたので0に戻す
                selectID = 0;
            }
        }

        // 空きがなかった
        return false;
    }

 

    // 引数番スロットのアイテムを捨てる
    void ItemDrop()
    {
        // アイテムがあるか確認
        if (ItemID[SelectItemNo] == -1)
        {
            Debug.Log("【エラー】" + SelectItemNo + "番にアイテムがありません!");
            return;
        }

 

        // プレイヤーの移動量を取得
        Rigidbody playerRb =
            GameObject.FindGameObjectWithTag("Player").GetComponent<Rigidbody>();
        Vector3 velocity = playerRb.velocity;
        velocity.y = 0.0f;

 

        // 捨てるアイテムを生成
        Vector3 itemPos = Camera.main.transform.position;
        GameObject dropItem = Instantiate(Item_Data.Items[ItemID[SelectItemNo]].ItemPrefab,
            itemPos, Camera.main.transform.rotation);

        // 前方に発射
        dropItem.GetComponent<ItemObject>().ItemDrop(velocity);

        // アイテム欄のIDをリセット
        ItemID[SelectItemNo] = -1;

        // UIを更新
        ItemUI.UpdateUI();

    }

 

    // アイテムを置く
    public void ItemPlaced(GameObject platformObj, Vector3 addPos, Vector3 addRot)
    {
        // 選んでいるアイテムIDを取得
        int id = ItemID[SelectItemNo];

 

        // 座標を計算
        Vector3 pos = platformObj.transform.position + addPos;
        pos += Item_Data.Items[id].ItemPivot;

 

        // アイテムを生成 回転はプレハブの回転を使用
        GameObject placedItem = Instantiate(Item_Data.Items[id].ItemPrefab,
            pos, Item_Data.Items[id].ItemPrefab.transform.rotation);

 

        // アイテムを回転させる
        placedItem.transform.Rotate(addRot, Space.World);

 

        // 生成したアイテムに設置された土台を教える
        placedItem.GetComponent<ItemObject>().ItemPlaced(platformObj);

 

        // 生成したアイテムの親オブジェクトを土台にする
        placedItem.transform.parent = platformObj.transform;

        // 所持品から番号を削除
        ItemID[SelectItemNo] = -1;

        // UIを更新
        ItemUI.UpdateUI();

    }

 

    void Awake()
    {
        // UIを更新(初期化)
        ItemUI.UpdateUI();
    }


    void Update()
    {
        // Bボタンで捨てる
        if ((Input.GetKeyDown("joystick button 1") || Input.GetKeyDown(KeyCode.Alpha0)))
        {
            ItemDrop();
        }

 

        // 選択アイテムの変更
        if ((Input.GetKeyDown("joystick button 4") || Input.GetKeyDown(KeyCode.Alpha1)))
        {
            SelectItemNo++;
            if (SelectItemNo > ItemID.Length - 1)
            {
                SelectItemNo = 0;
            }
            // UIを更新
            ItemUI.UpdateUI();

        }

        if ((Input.GetKeyDown("joystick button 5") || Input.GetKeyDown(KeyCode.Alpha2)))
        {
            SelectItemNo--;
            if (SelectItemNo < 0)
            {
                SelectItemNo = ItemID.Length - 1;
            }
            // UIを更新
            ItemUI.UpdateUI();

        }

 

    }

}

15

パラメータの設定

 Unity側の設定をしていきましょう。

​ 

 Canvasの子オブジェクトであるItemUIに先ほど作成したUI_Itemスクリプトをアタッチしてください。

 Item Cameraは要素数を3にして、UI用カメラを1から順番に設定していきます(順番を間違えると表示がおかしくなるので注意)

 Item Name TextとItem Message Textにはアイテム名と説明文のTextMeshProを設定します。

​ Item OffsetはX=0、Y=-1、Z=4を入力してください。

 GameManagerのGameManagerコンポーネントにItemUIのパラメータが追加されているので、先ほど追加したItemUIコンポーネントを設定しましょう。

 ItemDataBaseを開いて、UI調節用のパラメータを設定してください。

【本(黄色い本、青い本、赤い本、緑の本)】

UI_Offset     : X=0 Y=-0.8 Z=0

UI_Rotation : X=0 Y=60 Z=20

UI_Scale     : X=7 Y=7 Z=7

【宝玉(青の宝玉、赤の宝玉、黄の宝玉、緑の宝玉)】

UI_Offset     : X=0 Y=1 Z=0

UI_Rotation : X=0 Y=0 Z=0

UI_Scale     : X=3 Y=3 Z=3

【銀のリンゴ】

UI_Offset     : X=0 Y=1 Z=0

UI_Rotation : X=-40 Y=20 Z=0

UI_Scale     : X=20 Y=20 Z=20

【銀のゴリラ】

UI_Offset     : X=-0.2 Y=-1.5 Z=2

UI_Rotation : X=-90 Y=140 Z=0

UI_Scale     : X=2 Y=2 Z=2

【銀のパイナップル】

UI_Offset     : X=0 Y=-1 Z=0

UI_Rotation : X=-90 Y=0 Z=0

UI_Scale     : X=6 Y=6 Z=6

 これでアイテム欄の完成です。実際にアイテムを拾って確かめてみてください。​アイテムのUI用パラメータはお好みで調整しましょう。

​ カメラを複数扱うことで表現の幅が広がりますが、処理的には重いものになるため多様しないように気をつけましょう。

4-2

4-2 アイテム情報の表示

4-2 アイテム情報の表示

1

UIの作成

 照準が合っているアイテムの名前を表示したり、調べたアイテムの説明文を表示するためのUIを実装しましょう。

​ まずはアイテム欄と同じようにCanvasの子オブジェクトに空オブジェクトを追加してください。名前はSearchUIにしておきます。

 SearchUIの子オブジェクトにImageを追加してください。

​ 名前はSearchBGにして、半透明の黒い四角形になるようにColorを調整しましょう(自分で用意したウィンドウ素材を使うのもOK)

【サンプルの入力例】

PosX  : 0      PosY   : -170

Width : 300  Height : 60

Color : R=0 G=0 B=0 A=200

 SearchBGの子オブジェクトに「UI」→「TextMeshPro」を選択して、名前をSearchTextにしましょう。​親子関係が少しややこしいので、画像の通りに設定してください。

 SearchTextを以下のように設定しましょう。

​【サンプルの入力例】

PosX  : 0      PosY   : 0

Width : 600  Height : 60

Text Input  : アイテムの名前(仮なので適当でOK)

Font Asset : Corporate-Mincho-ver3 SDF

Font Size   : 34

Alignment  : 中央

2

更新処理の実装

 UIを更新する処理を書いていきます。

 UI_Searchスクリプトを作成して、以下のように入力してください。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;            // TextMeshProを扱うときに必要

 

public class UI_Search : MonoBehaviour
{
    [SerializeField]
    GameObject SearchObject;
    RectTransform m_searchObjectRectTransform;

 

    [SerializeField]
    TextMeshProUGUI SearchText;

 

    [SerializeField]
    Vector2 NameSize, MessageSize;

 

    // メッセージ自動非表示
    const float AUTO_OFF_TIME = 3.0f;
    bool m_isAutoOff = false;

 

    void Awake()
    {
        // RectTransformを取得しておく
        m_searchObjectRectTransform = SearchObject.GetComponent<RectTransform>();
        // 最初は非表示
        SearchObject.SetActive(false);
    }

 

    // UIを表示&更新
    // mode=false…名前表示モード mode=true…説明文表示モード

    public void SearchUI_On(string text, bool mode)
    {
        if (m_isAutoOff)
        {
            return;
        }

 

        // テキストを表示
        SearchObject.SetActive(true);
        SearchText.text = text;

 

        // モードに応じて背景のサイズを変える
        if (mode)
        {
            m_searchObjectRectTransform.sizeDelta = MessageSize;
        }
        else
        {
            m_searchObjectRectTransform.sizeDelta = NameSize;
        }

 

        // Invokeをキャンセル
        CancelInvoke("SearchUI_Off");
    }

 

    // UIを非表示にする
    public void SearchUI_Off()
    {
        SearchObject.SetActive(false);
        m_isAutoOff = false;
    }

 

    // 自動でオフにする
    public void AutoOff()
    {
        Invoke("SearchUI_Off", AUTO_OFF_TIME);
        m_isAutoOff = true;
    }

}

 Invoke関数を用いてメッセージを自動で非表示にする処理は、アイテムを3つ所持した状態で4つ目を拾おうとした際の「これ以上アイテムを持てません!」というテキストを自動で非表示にするためのものです。

 後は適切なタイミングで関数を呼ぶだけになります。

 GameManagerスクリプトを開いて、赤い部分のコードを追加してください。

~前略~

    // アイテム欄のUI
    [SerializeField]
    UI_Item ItemUI;

 

    // アイテム名表示のUI
    [SerializeField]
    UI_Search SearchUI;
    public UI_Search GetSearchUI()
    {
        return SearchUI;
    }

 

    // アイテムを取得する
    // アイテム欄に空きがあったらtrue なかったらfalseを返す

    public bool GetItem(int getItemID)
    {

​~後略~

 次にItemObjectスクリプトを開いて、赤い部分のコードを追加してください。

3

パラメータの設定

~前略~
 

    // アイテムを調べた時の仮想関数
    public virtual void ItemCheck()
    {
        // 調べた時の処理を行う
        ItemGet();
    }
    // アイテムを調べた時の基本処理
    public void ItemGet()
    {
        // 調べられない状態
        if (IsCheck)
        {
            return;
        }

 

        if (ItemID == -1)
        {
            // アイテムを調べた

 

            // デバッグ用 アイテム名と説明文をコンソールに出力
            //Debug.Log("アイテム名:" + Name + "¥n説明文:" + Explanation);

 

            // 獲得できないアイテムなので説明を表示
            m_gameManager.GetSearchUI().SearchUI_On(Explanation, true);
            m_gameManager.GetSearchUI().AutoOff();

        }
        else
        {
            // ② アイテムを取得
            // 【ヒント】ゲームマネージャーのGetItem関数を使おう

            bool isGet = m_gameManager.GetItem(ItemID);

 

            // アイテム欄に空きがあったかどうかで分岐
            if (isGet)
            {
                // アイテムを獲得できた

 

                // デバッグ用 獲得したアイテム名をコンソールに出力
                //Debug.Log(ItemDataBase.Items[ItemID].ItemName + "を取得");

 

                // 自分が台に置かれたアイテムだった場合、土台の設定も変更
                if (m_platformObj != null)
                {
                    m_platformObj.GetComponent<ItemPlatform>().ItemTook();
                    m_platformObj = null;
                }

 

                // アイテム名を削除する
                m_gameManager.GetSearchUI().SearchUI_Off();

 

                // 自身を削除する
                Destroy(gameObject);
            }
            else
            {
                // アイテム欄がいっぱいだった

 

                // デバッグ用
                //Debug.Log("アイテム欄がいっぱいです");

 

                // アイテムがいっぱいなので拾えない
                m_gameManager.GetSearchUI().SearchUI_On("これ以上アイテムを持てません!", true);
                m_gameManager.GetSearchUI().AutoOff();

            }

        }
    }

 

    // アイテムを捨てた時の処理
    public void ItemDrop(Vector3 playerVelocity)
    {

​~後略~

~前略~
 

    // アイテムを使用した時の仮想関数
    public virtual void ItemUse() { }

 

    // 自分にカーソルが合った時
    public virtual void StartSelect()
    {
        // 調べられない状態
        if (IsCheck)
        {
            return;
        }

 

        // アウトラインを表示する
        m_outline.enabled = true;

 

        // アイテム名を表示
        string name;
        if (ItemID == -1)
        {
            // 調べる系のアイテムなので指定した名前を使う
            name = Name;
        }
        else
        {
            // 獲得できるアイテムなのでアイテムデータベースから名前を引っ張ってくる
            name = m_gameManager.GetItemData().Items[ItemID].ItemName;
        }
        m_gameManager.GetSearchUI().SearchUI_On(name, false);

    }

    // 自分がカーソルから外れた時
    public virtual void EndSelect()
    {
        // アウトラインを削除する
        m_outline.enabled = false;

        // アイテム名を非表示にする
        m_gameManager.GetSearchUI().SearchUI_Off();

    }

 

    // アイテムを置いた時の処理
    public void ItemPlaced(GameObject platform)



​~後略~

 SearchUIオブジェクトにUI_Searchスクリプトをアタッチして、パラメータを設定しましょう。

 SearchObjectにはSearchBG(文字の背景)を、SearchTextにはSearchText(文字のテキストオブジェクト)を設定しましょう。

 Name Sizeは名前を表示する時の背景の大きさ、Message Sizeは説明文を表示する時の背景の大きさです。Name SizeはX=300 Y=60に、Message SizeはX=600 Y=100に設定してください。

 GameManagerのGameManagerコンポーネントにSearchUIの項目が追加されているので、先ほどアタッチしたSearchUIを設定しましょう。

 これでアイテムにカーソルを合わせた際に名前が表示され、調べた際に対応した説明文が表示されるようになります。アイテム欄がいっぱいの状態でアイテムを取得しようとした場合は「これ以上アイテムを持てません!」と表示されます。

​ 実際にゲームを動かして確認してみましょう。

4-3

4-3 操作説明の表示

4-3 操作説明の表示

1

UIの作成

 このゲームは少し操作が複雑なので、今できる動作が何か視覚的にわかるようにしましょう。

​ まずはCanvasの子オブジェクトに空オブジェクトを追加してください。名前はOperationUIにしておきます。

 OperationUIの子オブジェクトにTextMeshProを追加してください。名前はTextAにしておきます。

 TextAのパラメータを設定しましょう。

【サンプルの入力例】

PosX  : 250  PosY   : 230

Width : 80    Height : 40

Text Input  : A: (右側に操作の名前が入る)

Font Asset : Corporate-Mincho-ver3 SDF

Font Size   : 22

​Alignment  : 左寄せ 高さは中央

 これで右上に操作説明が表示されます。

 ​残りのBボタンとXボタンの表示も追加しましょう。

​ TextAをコピーしてTextBとTextXを作成してください。これらもOperationUIの子オブジェクトにしておきましょう。

【サンプルの入力例 TextB】

PosX         : 350      PosY   : 230

Text Input  : B: (右側に操作の名前が入る)

​【サンプルの入力例 TextX】

PosX         : 250      PosY   : 190

Text Input  : X: (右側に操作の名前が入る)

 これでテキストの仮配置が完了しました。

 Aボタンを押せるときは「A:調べる」と表示して、押せないときは灰色にすることで今できる操作をわかりやすくします。

2

更新処理の実装

 TextMeshProを更新する処理を書きましょう。

 UI_Operationスクリプトを作成して、以下のように入力してください。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;        // TextMeshProを扱うときに必要

 

public class UI_Operation : MonoBehaviour
{
    [SerializeField, Header("0=Aボタン 1=Bボタン 2=Xボタン")]
    TextMeshProUGUI[] Texts;

 

    [SerializeField, Header("押せない時の不透明度")]
    float NoActiveAlpha = 0.4f;

 

    // ボタンの種類
    public enum Button
    {
        enButton_A,
        enButton_B,
        enButton_X,
    }
    // 説明欄の変更
    // mode=false …黒&半透明 true …白&不透明

    public void SetOpe
ration(Button button, string text, bool mode)
    {
        // テキストを更新
        switch (button)
        {
            case Button.enButton_A:
                Texts[(int)button].text = "A:" + text;
                break;
            case Button.enButton_B:
                Texts[(int)button].text = "B:" + text;
                break;
            case Button.enButton_X:
                Texts[(int)button].text = "X:" + text;
                break;
        }

 

        // 色を更新
        if (mode)
        {
            // 白&不透明
            Texts[(int)button].color = Color.white;
            Texts[(int)button].alpha = 1.0f;
        }
        else
        {
            // 黒&半透明
            Texts[(int)button].color = Color.black;
            Texts[(int)button].alpha = NoActiveAlpha;
        }

    }
 

    void Awake()
    {
        // 最初は全て暗くしておく
        for (int i = 0; i < Texts.Length; i++)
        {
            SetOperation((Button)i, "", false);
        }
    }

}

 特別新しい処理はありませんが、Button(列挙型)とint型の型変換が時々行われている点には注意しましょう。

​ 次はGameManagerを開いて、赤い部分のコードを追加してください。

~前略~

    // アイテム名表示のUI
    [SerializeField]
    UI_Search SearchUI;
    public UI_Search GetSearchUI()
    {
        return SearchUI;
    }

 

    // 操作説明のUI
    [SerializeField]
    UI_Operation OperationUI;
    public UI_Operation GetOperationUI()
    {
        return OperationUI;
    }

 

    // アイテムを取得する
    // アイテム欄に空きがあったらtrue なかったらfalseを返す

    public bool GetItem(int getItemID)
    {

​~後略~

 最後に操作が切り替わったタイミングでSetOperation関数を呼びましょう。

​ ItemObjectスクリプトを開いて、赤い部分のコードを追加してください。

~前略~
 

        else
        {
            // ② アイテムを取得
            // 【ヒント】ゲームマネージャーのGetItem関数を使おう

            bool isGet = m_gameManager.GetItem(ItemID);

 

            // アイテム欄に空きがあったかどうかで分岐
            if (isGet)
            {
                // アイテムを獲得できた

                // デバッグ用 獲得したアイテム名をコンソールに出力
                //Debug.Log(ItemDataBase.Items[ItemID].ItemName + "を取得");

 

                // 自分が台に置かれたアイテムだった場合、土台の設定も変更
                if (m_platformObj != null)
                {
                    m_platformObj.GetComponent<ItemPlatform>().ItemTook();
                    m_platformObj = null;
                }

 

                // アイテム名を削除する
                m_gameManager.GetSearchUI().SearchUI_Off();

 

                // Aボタン表示を暗くする
                m_gameManager.GetOperationUI().SetOperation(UI_Operation.Button.enButton_A,
                    "", false);

 

                // 自身を削除する
                Destroy(gameObject);
            }
            else
            {
                // アイテム欄がいっぱいだった


​~後略~

~前略~
 

    // 自分にカーソルが合った時
    public virtual void StartSelect()
    {
        // 調べられない状態
        if (IsCheck)
        {
            return;
        }

 

        // アウトラインを表示する
        m_outline.enabled = true;

 

        // アイテム名を表示
        string name;
        if (ItemID == -1)
        {
            // 調べる系のアイテムなので指定した名前を使う
            name = Name;
            // 操作説明の更新
            m_gameManager.GetOperationUI().SetOperation(UI_Operation.Button.enButton_A,
                "調べる", true);

        }
        else
        {
            // 獲得できるアイテムなのでアイテムデータベースから名前を引っ張ってくる
            name = m_gameManager.GetItemData().Items[ItemID].ItemName;
            // 操作説明の更新
            m_gameManager.GetOperationUI().SetOperation(UI_Operation.Button.enButton_A,
                "拾う", true);

        }

        m_gameManager.GetSearchUI().SearchUI_On(name, false);
    }

    // 自分がカーソルから外れた時
    public virtual void EndSelect()
    {
        // アウトラインを削除する
        m_outline.enabled = false;
        // アイテム名を非表示にする
        m_gameManager.GetSearchUI().SearchUI_Off();
        // 操作説明の更新
        m_gameManager.GetOperationUI().SetOperation(UI_Operation.Button.enButton_A,
            "", false);

    }

 

    // アイテムを置いた時の処理
    public void ItemPlaced(GameObject platform)
    {
        // 自分が置かれている土台を保存する
        m_platformObj = platform;
    }
}

~前略~
 

    [SerializeField]
    bool IsCheck = false;    // 調べることが可能かどうか
    public void SetIsCheck(bool flag)
    {
        IsCheck = flag;
    }

 

    // ゲームマネージャー
    protected GameManager m_gameManager;

 

    GameObject m_platformObj = null;    // 自分が置かれている土台

    void Awake()
    {


​~後略~

 次にItemPlatformスクリプトを開いて、赤い部分のコードを追加してください。
 m_gameManagerをprotectedにしたため、継承先のItemPlatformでも使えるようになっています。その影響で
一部のコードが変化しているので注意してください。

~前略~
 

    // 置かれているアイテムが正解ならtrueになる
    bool m_isAnswer = false;
    public bool GetIsAnswer()
    {
        return m_isAnswer;
    }

 

    [SerializeField]
    Vector3 AddPos, AddRot = Vector3.zero;  // アイテムを置く座標と回転に加算する値

 

    // 自分が選択中かどうか
    bool m_isSelect = false;

 

    // 自分が選択された際の処理
    public override void StartSelect()
    {
        // 元の機能も同時に使う
        base.StartSelect();

        // 選択中にする
        m_isSelect = true;
    }
    void Update()
    {
        if (m_isSelect == false)
        {
            return;
        }

        // 何かしらのアイテムを選択中 かつアイテムが置かれていない
        if (m_gameManager.GetItemID(m_gameManager.GetSelectItemNo()) != -1 &&
            m_isItemPlaced == false)
        {
            // 使う表示を行う
            m_gameManager.GetOperationUI().SetOperation(UI_Operation.Button.enButton_X,
                "使う", true);
        }
        else
        {
            // 操作説明の表示を暗くする
            m_gameManager.GetOperationUI().SetOperation(UI_Operation.Button.enButton_X,
                "", false);
        }

    }
    // 選択が終わった時の処理
    public override void EndSelect()
    {
        // 元の機能も同時に使う
        base.EndSelect();

        // 操作説明の表示を暗くする
        m_gameManager.GetOperationUI().SetOperation(UI_Operation.Button.enButton_X,
            "", false);

        // 選択状態を戻す
        m_isSelect = false;
    }

 

    // アイテムを使用した時の処理
    public override void ItemUse()
    {
        // アイテムが既に置かれているなら中断
        if (m_isItemPlaced)
        {
            return;
        }

        // ゲームマネージャーの取得
        // →不要になる
        //GameManager gameManager =
        //    GameObject.FindGameObjectWithTag("GameController").GetComponent<GameManager>();

 

        // 選択中のアイテムを取得する
        int selectItemID = m_gameManager.GetItemID(m_gameManager.GetSelectItemNo());

 

        // 選択中のアイテムがないなら中断する
        if (selectItemID == -1)
        {
            return;
        }

 

        // アイテムを設置
        m_gameManager.ItemPlaced(gameObject, AddPos, AddRot);

 

        // 置かれたアイテムのIDを保存して、アイテムが置かれているフラグを立てる
        m_placedItemID = selectItemID;
        m_isItemPlaced = true;

 

        // 答えが合っているか確認する
        if (AnswerItemID == m_placedItemID)
        {
            m_isAnswer = true;
        }

 

        // 操作説明の表示を暗くする
        m_gameManager.GetOperationUI().SetOperation(UI_Operation.Button.enButton_X,
            "", false);

    }

 

    // アイテムを取る
    public void ItemTook()
    {
        // パラメータを初期化
        m_placedItemID = -1;
        m_isAnswer = false;
        m_isItemPlaced = false;
    }

}

 UI_Itemスクリプトを開いて、赤い部分のコードを追加してください。

~前略~
 

        // アイテム欄を更新する
        for (int i = 0; i < ItemCamera.Length; i++)
        {
            // 指定スロットのアイテム番号を取得
            itemNo = m_gameManager.GetItemID(selectNo);

            // アイテムを一旦削除
            Destroy(m_itemObject[i]);

 

            // 名前や説明欄の更新
            if (selectNo == m_gameManager.GetSelectIte
mNo())
            {
                // 表示の有無
                if (itemNo == -1)
                {
                    // アイテムがないため説明欄は非表示
                    ItemNameText.enabled = false;
                    ItemMessageText.enabled = false;

 

                    // 操作説明を更新
                    m_gameManager.GetOperationUI().SetOperation
                        (UI_Operation.Button.enButton_B, "", false);

                }
                else
                {
                    // 説明欄を表示
                    ItemNameText.enabled = true;
                    ItemMessageText.enabled = true;

                    // 内容更新
                    ItemNameText.text = m_gameManager.GetItemData().Items[itemNo].ItemName;
                    ItemMessageText.text = m_gameManager.GetItemData().Items[itemNo].ItemExplanation;

 

                    // 操作説明を更新
                    m_gameManager.GetOperationUI().SetOperation
                        (UI_Operation.Button.enButton_B, "捨てる", true);

                }
            }

 

            // セレクト番号の更新
            selectNo++;
            if (selectNo > ItemCamera.Length - 1)
            {
                selectNo = 0;
            }


​~後略~

3

パラメータの設定

 これでスクリプトの記述は完了です。

 OperationUIオブジェクトにUI_Operationスクリプトをアタッチしてください。

 Textsの要素数を3にして、上からTextA、TextB、TextXの順番にTextMeshProを設定しましょう。これはUI_Operation内で定義した列挙型Buttonの順番に対応しています。

 次にGameManagerを選択して、OperationUIに先ほどアタッチしたUI_Operationコンポーネントを設定してください。

 これで現在可能な操作が表示されるようになりました。​ゲームを実行して確認してください。

評価テスト

 これでUIの処理は一通り完成しました。

 次のLessonではシーンの実装や効果音を追加することでゲームを完成させていきましょう。
 

【評価テスト】

https://forms.gle/Sero694G4nmHQNyc7

評価テスト

河原電子ビジネス専門学校
​ゲームクリエイター科

bottom of page