見出し画像

短編RPG制作(Unity2D)【第30回】メニューの作成その8


メニューへの表示

今日は時間が取れるので、じっくり調べていきたいと思います。

変数の確認

ひとまず、主要な変数すべてに「SerializeField」を付けて、ゲーム再生中に確認できるようにします。
変数の内容は以下のようになっています。

  • itemList:プレイヤーの持ち物の参照を格納する

  • itemPossessions:持ち物の個数の参照を格納する

  • itemCount:itemListの要素数

  • itemImages:「Panel_ItemIcon」の子要素に存在する「Image」コンポーネントの参照をすべて格納する

ボタンを押す前
ボタンを押した後
ボタンを2回押した後

画像の格納先

ボタンを押す前はいいとして、1回押した後「itemImage」には2つの要素が格納されています。Element1には、プレイヤーがアイテムを拾ったことにより生成されたオブジェクトが入っています。Element0は、「Panel_ItemIcon」そのものが入っています。
調べなおしてみると、どうやら"GetComponentsInChildren"は子要素だけでなく自分自身のコンポーネントも取得してしまうようです。

Panel_ItemIcon内に画像が格納されている

スクリプトでは、「itemList」における0番目の要素を「itemImages」の0番目に表示するよう記述しているので、「Panel_ItemIcon」のImageコンポーネントに画像が入っていました。これが、アイテムの画像が表示されない原因だと思われます。

これについては、「Panel_ItemImage」のImageコンポーネントを削除することで対応しました。

ボタンを2回押した後

また、ボタンを2回以上押すと、「itemImages」の要素1に新しい中身「Missing」が現れます。
どこから来ているのかさっぱり分からない……

各処理をコメントアウトしながら、どこに原因が探りましたが、どうやらアイテム枠をリセットする(Destroyする)ところでおかしくなっているようです。
GameObject.Destroy関数の処理が行われるのはフレーム終了時のようで、同じフレームでオブジェクトを生成したり参照したりしているのが原因だと推測されます。
というわけで、メソッドをコルーチンにして、1フレームの遅延を挟むことにしました。

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

public class MenuButton : MonoBehaviour
{
    [SerializeField] private PlayerStatus _playerStatus;
    [SerializeField] private Canvas _canvas;
    [SerializeField] private GameObject _panelItemIcon;
    [SerializeField] private GameObject _panelItemContent;
    [SerializeField] private Image imageItemIcon;
    [SerializeField] private Image imageItemContent;
    private bool canvasEnable = false;
    [SerializeField] private List<ItemData> itemList;
    [SerializeField] private List<int> itemPossessions;
    [SerializeField] private int itemCount;
    [SerializeField] private Image[] itemImages;
    [SerializeField] private Text[] itemContents;
    int j;

    public void OnClickMenuButton()
    {
        if (!canvasEnable)
        {

            _canvas.enabled = true;
            canvasEnable = true;
            StartCoroutine(MenuUpdate());
        }
        else
        {
            _canvas.enabled = false;
            canvasEnable = false;
        }
    }

    // メニュー表示の更新
    private IEnumerator MenuUpdate()
    {
        MenuReset();
        yield return null;
        MenuDisplay();
    }

    // アイテム枠をリセット
    private void MenuReset()
    {
        foreach (Transform child in _panelItemIcon.transform)
        {
            GameObject.Destroy(child.gameObject);
        }
        foreach (Transform child in _panelItemContent.transform)
        {
            GameObject.Destroy(child.gameObject);
        }
    }

    // アイテム内容を表示
    private void MenuDisplay()
    {
        itemList = _playerStatus.GetItemList();
        itemPossessions = _playerStatus.GetItemPossessions();
        itemCount = itemList.Count;

        // 所持アイテム種類数だけの枠を生成
        for (int i = 0; i < itemCount; i++)
        {
            Instantiate(imageItemIcon, _panelItemIcon.transform);
            Instantiate(imageItemContent, _panelItemContent.transform);
        }

        // 子オブジェクトのコンポーネントを取得する
        itemImages = _panelItemIcon.GetComponentsInChildren<Image>();
        //itemContents = _panelItemContent.GetComponentsInChildren<Text>();

        // 子オブジェクトにアイテム情報を表示する
        for (int i = 0; i < itemCount; i++)
        {
            itemImages[i].sprite = itemList[i].GetItemIcon();
        }
    }
}

実行結果

ボタン1回押し
ボタン2回押し

Missingが消え、狙い通りに表示されるようになりました!

まとめ

"GetComponentInChildren"の解説を調べても前線原因が分からなかったので、すごく時間がかかりました。
Destroyメソッドの取り扱いには要注意ですね。そもそも、Destroyメソッドを使わずに、先に全アイテム種類数分の枠を生成しておいて表示・非表示で対応すればよかったのでは、と思わなくもありませんが、結局表示のリセットは必要なのであまり変わらない気もします。
苦戦しましたが、いい勉強になったということで。

次回はアイテム画像の表示をシルエット状態から直すこと、アイテム詳細の表示を行っていきたいです。

この記事が気に入ったらサポートをしてみませんか?