Unityに関連する記事です

 引き続き、アイテムの管理方法について実装を行います。今回でアイテムの管理については最後の実装になります。

 ベースとしましては、かめくめ様のこちらの記事を参考にさせていただいております。
ありがとうございます<(_ _)>

Unityを使った3Dゲームの作り方(かめくめ)
Unityでキーやゲームパッドで操作するステータス画面の作成
https://gametukurikata.com/ui/statuswindow


 上記の実装と前回までの2回分の手順の実装を行った上で、こちらの処理を追加で実装を行います。


<実装動画>
動画ファイルへのリンク


手順19 −アイテムの管理方法の運用−
33.GameData スクリプトと DataBaseManager スクリプトを修正する
34.ItemButtonManager スクリプトを作成する


 新しい学習内容は、以下の通りです。

 ・複数のスクリプトを経由する処理を読み解く
 ・Linqの機能の実装例  FirstOrDefault メソッド〜



 今回の実装は3つの手順に沿って実装を行います。そのため、まずは全体の流れを把握し、それを順番に作成していきます。

〇1.アイテムインベントリーに表示するアイテム1つ1つの情報を ItemData クラスとして作成し、それを ItemDataSO スクリプタブル・オブジェクトを作成して管理を行うための準備をする
〇2.アイテムインベントリー内に表示する個別のアイテムのボタン、およびデータ情報を表示するための ItemButtonDetail ゲームオブジェクトと、それらの情報を制御するための ItemButtonDetail スクリプトを作成する
〇3.GameData スクリプトと DataBaseManager スクリプトを修正して、ItemDataSO スクリプタブル・オブジェクトに登録したアイテムの情報を運用する処理を実装する
〇4.アイテムインベントリー内に表示する ItemButtonDetail ゲームオブジェクトを所持している数に合わせて生成を行う ItemButtonManager スクリプトを作成する

 今回の手順では、〇3と〇4を実装します。



33.GameData スクリプトと DataBaseManager スクリプトを修正する

1.設計


 プレイヤーが所持しているアイテムの情報を GameData スクリプトにおいて管理するようにします。
これは GameData はシングルトンクラスですので、いずれのスクリプトからでも参照が容易に行えるためです。

 アイテムの情報を管理するために、新しく ItemInventryData クラスを、入れ子クラスとして GameData スクリプト内に作成します。
これがアイテム1つの情報になります。ここに登録できる情報はアイテムの名前、所持数、通し番号の3つです。

 これを List にして管理することで、複数のアイテムを所持できる状態を作り出します。

 アイテムインベントリー内に作成されるアイテムのボタンは、この ItemInventryData の情報を利用して作成するようになります。
よって、ItemInventryData にないアイテムの情報は「所持していない」アイテムとなります。

 この情報を ItemDataSO スクリプタブル・オブジェクトに登録されている ItemData クラスの ItemName と照合して、合致したデータを利用するような設計にします。
例えば、ItemInventryData に "薬草" という情報が itemName 変数に登録されている場合、ItemDataSO スクリプタブル・オブジェクト内の各 ItemData の ItemName 内に存在するかを1つずつチェックしていきます。
そして合致したデータがあった場合、その ItemData を "薬草" のデータであるとして認識させて、利用するようにします。


2.GameData スクリプトを修正する


 新しく ItemInventryData クラスと、このクラスをまとめて管理するための List 用の変数を宣言します。

 メソッドは2つ作成します。どちらも Get 〜 から始まる名前で、ItemInventryData クラス関連の戻り値を持つ、ゲッターメソッドになります。
これらのメソッドは外部のクラスから呼び出されて利用させることを前提に作成しておきます。
外部のクラスで ItemInventryData クラスに関連する情報が必要になったときには、このメソッドを実行することで取得が可能になっている設計です。
これはシングルトンクラスであるため、メソッドを実行しやすく、利便性に富んでいます。


GameData.cs

<= クリックすると開きます


 スクリプトを修正したらセーブします。



 GameData ゲームオブジェクトを選択してインスペクターを確認します。
新しく public 修飾子で宣言した List の変数が表示されていれば問題ありません。

インスペクター画像




3.DataBaseManager スクリプトを修正する


 ItemDataSO スクリプタブル・オブジェクトを利用できるようにするために、新しく変数を宣言します。
また、このスクリプタブル・オブジェクトにある ItemData の情報を検索して必要な情報を取得するためのメソッドを新しく3つ作成します。

 GetItemDataFromItemNo メソッドと GetItemDataFromItemName メソッドは、それぞれ引数で届いた情報を検索元とし、
この値と合致するデータを持つ ItemData をスクリプタブル・オブジェクト内で検索した上で1つだけ取得して、呼び出し元のスクリプトへと値を戻すゲッターメソッドになります。
この処理を行うことにより、ItemNo、あるいは ItemName のいずれかの情報を指定することが出来れば、スクリプタブル・オブジェクト内の ItemData の情報を特定できる設計になっています。

 GetItemDataFromItemNo メソッドと GetItemDataFromItemName メソッドにおける処理の内容ですが、
片方は foreach + if 文を組み合わせた処理、もう片方は FirstOrDefault メソッドを利用した処理になっていますが、
引数で検索している情報が異なるだけで、処理の内容はまったく同じものです。

 GetEventDataFromEvnetTypeAndEventNo メソッドは、すでにある GetEventDataFromNPCEvnet メソッドを汎用的にしたメソッドです。
GetEventDataFromNPCEvnet メソッドは NPC の会話のイベントのみを照合して取得していますが、こちらのメソッドでは
EventType によって照合先を変更できるようになっているため、会話イベント以外のイベントも検索出来るようになっています。

 処理の内容は GetEventDataFromNPCEvnet メソッドと引数の指定以外は同じのため、コメントは省略しています。
自分でコメントを記述して、処理の理解を深めてください。


DataBaseManager.cs

<= クリックすると開きます


 スクリプトを修正したらセーブします。



 DataBaseManager ゲームオブジェクトを選択してインスペクターを確認します。
新しく SerializeField 属性で宣言した各 List の変数が表示されていれば問題ありません。

 ItemDataSO 変数に、ItemDataSO スクリプタブル・オブジェクトをドラッグアンドドロップしてアサインしてください。


インスペクター画像




4.Linqの機能の実装例 〜FirstOrDefault メソッド〜


 Linq(リンク)とは、コレクション(Dictionary や List など)の要素を操作して、検索したり集計する処理を簡潔に記述することができるライブラリ(複数の機能をまとめたもの)です。

 Linqを使用するためには宣言が必要になります。 

using System.Linq;



 Linqを記述する際にはラムダ式の記述を用います。ラムダ式についてはこちらをご確認ください。
SamuraiBlog様
【C#入門】LINQの使い方総まとめ(Select、Where、GroupByなど)
https://www.sejuku.net/blog/56519

 Linqには多くの機能がありますが今回利用している機能についてまとめておきます。
そのほかの機能については記事がたくさんありますが、こちらのサイトも参考になります。
地平線に行く様
LINQの拡張メソッド一覧と、ほぼ全部のサンプルを作ってみました。
https://yujisoftware.hatenablog.com/entry/20111031...


FirstOrDefault メソッド

 FirstOrDefault メソッドは指定したコレクション内を、指定した引数の値を基準に、コレクション内の要素から先頭の値を探して返します。もしも該当する値がなければ Null を返します。
このとき FirstOrDefault メソッドの引数に探したい情報の条件を指定することにより、先頭の値ではなく、条件に合致した先頭の値を取得する処理が実装出来ます。

<条件に合った先頭の要素を取得>
 return itemDataSO.itemDataList.FirstOrDefault(x => x.itemName == itemName);

 上記のケースでは、コレクション内の各 ItemData の itemName (x.itemName)が、引数で届いている itemName と合致するかどうかを判定し、その内に最初に条件に合致した値を戻しています。

 この処理はつまり、その前に記述されている GetItemDataFromItemNo メソッド内で実行されている処理を同じ内容になります。

    /// <summary>
    /// ItemNo から ItemData を取得
    /// </summary>
    /// <param name="itemNo"></param>
    /// <returns></returns>
    public ItemDataSO.ItemData GetItemDataFromItemNo(int itemNo) {
        foreach (ItemDataSO.ItemData itemData in itemDataSO.itemDataList) {  // ここから
            if (itemData.itemNo == itemNo) {
                return itemData;
            }
        }
        return null;                              // ここまで
    }

 上記のメソッド内の5行分を処理を1行で実行しているものが、FirstOrDefault メソッドの実行内容になります。


参考サイト
陰干し中のゲーム開発メモ 様
【C#,LINQ】First,FirstOrDefault〜配列やリストの先頭の要素がほしいとき〜
https://www.urablog.xyz/entry/2018/05/29/070000

 
 スクリプトの修正は以上で完了です。最後に新しいスクリプトの作成を行います。


34.ItemButtonManager スクリプトを作成する

1.設計


 アイテムインベントリーを開いた際に、GameData ゲームオブジェクトの GameData スクリプトに登録されているアイテムの情報を元に
インベントリー内に ItemButtonDetail ゲームオブジェクトを作成し、それらにアイテムの情報を与えて振る舞いを自動的に変更するように制御を行います。

 ItemButtonDetail ゲームオブジェクトには ItemButtonDetail スクリプトがアタッチされていますので、このスクリプトに準備しているメソッドを呼び出すことで
ItemButtonDetail ゲームオブジェクトを生成すると同時に、そのItemButtonDetail スクリプトに「アイテムの情報= ItemData」を設定させる処理を実行します。

 このときに設定される情報は GameData スクリプトに登録されているアイテムの情報、つまり、所持しているアイテムのみになります。
以前活用している CreateItemButton スクリプトでは、アイテムのデータ分だけすべて生成しておいて、所持していないアイテムのゲームオブジェクトを見えないようにする、という制御で管理していました。
今回の実装では、所持しているアイテムのみ生成されることになりますので、この部分の処理の流れが大きく異なります。


2.ItemButtonManager スクリプトを作成する


 宣言する変数には ItemButtonDetail ゲームオブジェクトのプレファブを登録する変数や、生成された ItemButtonDetail ゲームオブジェクトを管理するための List の変数、
ItemButtonDetail ゲームオブジェクトを生成する位置の情報の変数、そして Button コンポーネントの制御を行うための変数を用意しています。

 Start メソッド内では各 Button コンポーネントの OnClick イベントにメソッドを登録します。

 CreateItemButtonDetail メソッドを実行することで、GameData クラスに登録されているアイテムの情報を元にして、ItemButtonDetail ゲームオブジェクトを生成し、
ItemButtonDetail スクリプトに対してアイテムの情報を設定する SetUpItemButtonDetail メソッドの命令を実行します。
生成されたこれらの ItemButtonDetail ゲームオブジェクトを List に登録し、管理できる状態にしておきます。


ItemButtonManager.cs

<= クリックすると開きます


 スクリプトを作成したらセーブを行います。

 Start メソッドで Button コンポーネントの OnClick イベントを登録していますが、EventTrigger との兼ね合いで処理が動かない場合があります。
その場合には、Start メソッドの処理はコメントアウトし、MenuWindow => MenuArea => Item ゲームオブジェクトにアタッチされている EventTrigger に CreateItemButtonDetails メソッドを2か所に追加してください。


<MenuWindow / MenuArea / Item ゲームオブジェクト>



 同じように MenuWindow => MenuArea => Exit ゲームオブジェクトにアタッチされている EventTrigger に DestroyItemButtonDetails メソッドを2か所に追加してください。

<MenuWindow / MenuArea / Exit ゲームオブジェクト>




3.Property ゲームオブジェクトの CreateItemButton スクリプトを削除し、 ItemButtonManager スクリプトをアタッチして設定を行う


 ヒエラルキーにある Property ゲームオブジェクトに ItemButtonManager スクリプトをドラッグアンドドロップしてアタッチしてください。
アタッチを行ったら必ずインスペクターを確認します。

 public 修飾子で宣言している変数がインスペクター上に表示されていますので、必要な情報をアサインします。

 itemButtonDetailPrefab 変数にはプレファブにした ItemButtonDetail ゲームオブジェクトをドラッグアンドドロップしてアサインしてください。

 itemAreaTran 変数には Property ゲームオブジェクト内にある ItemArea ゲームオブジェクトをドラッグアンドドロップしてアサインしてください。
この位置に ItemButtonDetail ゲームオブジェクトが生成されることになります。

 btnItemSelectWindow 変数と btnExitItemWindow 変数は CreateItemButton スクリプトでも利用されているものですので、
CreateItemButton スクリプトの各変数と同じゲームオブジェクトをこれらの変数にアサインしてください。


インスペクター画像



 この手順より、ItemButtonManager スクリプトを利用して ItemButtonDetail ゲームオブジェクトを作成し、それをアイテムインベントリーに表示する処理に変更しますので、
いままで利用していた CreateItemButton スクリプトは利用しなくなりますので、これを削除します。


4.所持しているアイテムを設定する


 GameData スクリプトに新しく追加した itemInventryDatasList 変数が現在プレイヤーが所持しているアイテム群の情報です。

 GameData ゲームオブジェクトの itemInventryDatasList 変数のサイズを 1 以上に変更してください。
新しく所持しているアイテムの情報を設定できるようになりますので、ItemDataSO スクリプタブル・オブジェクトに登録されているアイテムの名前を登録してください。
この情報を元にして、アイテムインベントリーに表示するアイテムのボタンが変更になります。


インスペクター画像(参考)



 なお、今回のように手動で所持しているアイテムの情報を登録する場合には、各 Number の値は 0 から連番になるように採番してください。
ゲーム内で取得する場合には、自動的に 0 から順番に採番されます。

 以上でゲームを実行するための準備が整いました。


5.ゲームを実行して動作を確認する


 ゲームを実行して、アイテムのインベントリーを開いてみてください。
GameData スクリプトに登録した所持しているアイテムの情報を元に、アイテムのボタンが生成されてインベントリー内に並んで居れば制御成功です。


<実装動画>
動画ファイルへのリンク


 以上で完成です。


6.<応用>


 現在の処理ですと、アイテムのインベントリーが開いていても、インベントリー内のカーソル移動に合わせて、後ろでプレイヤーキャラも一緒に移動してしまいます
アイテムのインベントリーが開いている間はプレイヤーキャラが移動せず、向きも変わらない状態になるように制御処理を実装してみてください。


<実装動画 アイテムインベントリー内をカーソルが移動してもプレイヤーキャラが移動せず、向きも変わらない>
動画ファイルへのリンク


 以上でこの手順は終了です。

 次は 手順20 −マスターデータの抽出方法− です。

このページへのコメント

ご活用いただきまして、ありがとうございます。
また、ご質問もありがとうございます。
返信にお時間をいただいてしまいまして、誠に申し訳ございません。

DataBaseManager.cs 内の GetEventDataFromNPCEvent メソッドなのですが、手順13内で最初に作成を行っていただいている際に、このメソッドの引数は設定されております。
そのため、DataBaseManager.csでの今回の修正部分には含まれていない状況です。

ちなみに、どのようなエラーがでておりますでしょうか?
よろしければご返信いただけますよう、よろしくお願いいたします。

0
Posted by  orika_ex_miyako orika_ex_miyako 2021年09月27日(月) 15:58:17 返信

Unity初心者です。
わかりやすいサイトで非常に助かっています。

今回 DataBaseManager.cs の etEventDataFromNPCEvnet メソッドの引数が増えましたが、
これにより NonPlayerCharacter.cs 内から呼び出している箇所がエラーにならないでしょうか?
自分の環境だとエラーになってしまうのですが。

0
Posted by Unity初心者 2021年04月07日(水) 19:29:16 返信

コメントをかく


「http://」を含む投稿は禁止されています。

利用規約をご確認のうえご記入下さい

Menu



技術/知識(実装例)

2Dおはじきゲーム(発展編)

2D強制横スクロールアクション(発展編)

3Dダイビングアクション(発展編)

2Dタップシューティング(拡張編)

レースゲーム(抜粋)

2D放置ゲーム(発展編)

3Dレールガンシューティング(応用編)

3D脱出ゲーム(抜粋)

2Dリアルタイムストラテジー

2Dトップビューアドベンチャー(宴アセット使用)

3Dタップアクション(NavMeshAgent 使用)

2Dトップビューアクション(カエルの為に〜、ボコスカウォーズ風)

VideoPlayer イベント連動の実装例

VideoPlayer リスト内からムービー再生の実装例(発展)

AR 画像付きオブジェクト生成の実装例

AR リスト内から生成の実装例(発展)

private



このサイト内の作品はユニティちゃんライセンス条項の元に提供されています。

管理人/副管理人のみ編集できます