見出し画像

【Unity】3Dローグライクゲームの作り方〜Step9&10-9〜

前回の記事はこちら
前回はアイテムを使ったらインベントリから削除して、ターンを経過させるところまでを行いました。

アイテムを置く

今回はアイテムを置けるようにするところから始めたいと思います。
まず最初にItemクラスに以下のパラメーターを追加します。

public string prefab;

次にItemParamsControllerスクリプトを開いて下さい。以下のメソッドを追記します。

/**
* アイテムパラメーターを設定する
*/
public void SetParams(Item it)
{
   Item p = new Item();
   p.id = it.id;
   p.name = it.name;
   p.prefab = it.prefab;
   p.sprite = it.sprite;
   parameter = p;
}

最後にActorUseItemsスクリプトを開きましょう。Putメソッドの内容を以下に変更します。

Message.Add(11, it.name);
GameObject item = GetComponentInParent<Field>().GetExistItem(move.grid.x, move.grid.z);
if (item != null) Destroy(item);
GameObject items = GetComponentInParent<Field>().items;
GameObject itemObj = (GameObject)Resources.Load("Prefabs/" + it.prefab);
item = Instantiate(itemObj, items.transform);
item.GetComponent<ItemMovement>().SetPosition(move.grid.x, move.grid.z);
item.GetComponent<ItemParamsController>().SetParams(it);
inventory.Remove(it);
return true;

ID11番のメッセージはアイテムを置くというものです。
最初にその場所にアイテムがないかどうかを調べて、もしあったらそのアイテムを削除するようにしています(アイテムが2重になるのを防ぐため)。そして対応するプレハブをResourcesフォルダから取ってきて、それをインスタンス化してそのキャラクターがいる場所に置き、インベントリから削除しています。
SetParamsメソッドは今後特殊なアイテム(例えば効果が+1されているものなど)が出てきても対応できるように今ここで実装しました。
ビルドしたら、Item1のアイテムパラメーターのプレハブ名に「Item1」を入力することを忘れないようにして下さい。
テストプレイしてみます。以下のように、プレイヤーのいる場所にアイテムが置かれればOKです。

アイテム置く

(ところでなんですけど、地面にアイテムを置いた時って一般的にターン経過してましたっけ......?)

地面にあるアイテムリストの表示

アイテムをインベントリいっぱいに持っている時、当然新たにアイテムを拾うことはできませんが、拾い食いをしたり、投げたり持っているアイテムと交換したりといったことはできます。
つまりどういうことかと言いますと、地面にあるアイテムをインベントリウィンドウから操作できるようにし、また他とは別のサブメニューを表示しないといけません。
これを実装するために、まずは地面にあるアイテムをインベントリウィンドウに表示できるようにしていこうと思います。
Inventoryクラスに以下のメソッドを追加します。

/**
* もしあれば足下にあるアイテムを返す
*/
public Item GetFootItem()
{
   Pos2D grid = GetComponent<ActorMovement>().grid;
   GameObject obj = GetComponentInParent<Field>().GetExistItem(grid.x, grid.z);
   if (obj == null) return null;
   return obj.GetComponent<ItemParamsController>().parameter;
}

ItemSlotDisplayスクリプトを以下のように編集します。

// 以下のパラメーターを追加
private int inventoryNum;

// 以下のメソッドを変更する
public void Show()
{
   maxShowItemNum = content.transform.childCount;
   for (int i = inventory.itemNumMax; i < maxShowItemNum; i++)
       content.transform.GetChild(i).gameObject.SetActive(false);
   footItem = inventory.GetFootItem();
   if (selectItemIndex == inventory.itemNumMax && footItem == null)
   {
       while (!MoveSelect(EDir.Right));
       while (!MoveSelect(EDir.Left)) ;
   }
   inventoryNum = (footItem == null ? 0 : 1) + inventory.itemNumMax;
   maxShowItemNum = maxShowItemNum > inventoryNum ? inventoryNum : maxShowItemNum;
   ShowItemInfo();
}

public bool MoveSelect(EDir d)
{
   /*        省略       */
   if (viewSelectItemIndex == prevViewSelectItemIndex + 1)
   {
       if (viewSelectItemIndex > maxShowItemNum - 2 && selectItemIndex < inventoryNum - 2)
       {
           /*        省略       */
       }
   }
   /*        省略       */
   if (viewSelectItemIndex == prevViewSelectItemIndex - 1)
   {
       if (viewSelectItemIndex < 1 && selectItemIndex > 1)
       {
           /*        省略       */
       }
   }
   if (prevViewSelectItemIndex == 0 && viewSelectItemIndex == maxShowItemNum - 1)
   {
       leadShowItemIndex = inventoryNum - maxShowItemNum;
       ShowItemInfo();
   }
   /*        省略       */
}

private void ShowItemInfo()
{
   for (int i = 0; i < maxShowItemNum; i++)
   {
       Transform slot = content.transform.GetChild(i);
       slot.gameObject.SetActive(true);
       int idx = i + leadShowItemIndex;
       Item it = idx == inventory.itemNumMax ? footItem : inventory.Get(idx);
       if (it == null)
       {
           /*        省略       */
       }
       else
       {
           /*        省略       */
       }
   }
}

テストしてみましょう。InventoryコンポーネントのItem Num Maxパラメーターの値を0にし、インベントリのスロットが表示されていないのを確認してから、アイテムの上に乗ってみます。すると、このように地面のアイテムが(スロット0個にも関わらず)インベントリに表示されるはずです。

スクリーンショット 2020-05-13 22.18.09

ただし、今この段階では、アイテムを選択してもサブメニューは表示されません。

地面にあるアイテム用のサブメニュー表示

という訳で表示できるようにしていきましょう。
まずはItemSlotDisplayクラスに以下のメソッドを追加します。

/**
* 現在地面にあるアイテムが選択されている時は、そのアイテムを返す
*/
public Item GetSelectFootItem() => selectItemIndex == inventory.itemNumMax ? footItem : null;

次にInventoryActionクラスのSelectItemメソッドを以下のように変更して下さい。

private void SelectItem()
{
   if (Input.anyKeyDown && Input.GetKeyDown(KeyCode.Space))
   {
       // 以下6行を追記
       selectItem = display.GetSelectFootItem();
       if (selectItem != null)
       {
           subMenu.Show();
           return;
       }
       selectItem = display.GetSelectItem();
       if (selectItem != null) subMenu.Show();
   }
}

それではテストしてみましょう。先ほどと同じ状況を作り、地面のアイテムを選択してみて下さい。次の画像のようになればOKです。

スクリーンショット 2020-05-14 3.29.46

今はサブメニュー側に変更を加えていないので、普通のサブメニューが表示されます。なお、この状態で「使う」や「置く」を選択すると、アイテムが地面に残ったままメッセージが表示されてしまいます。

という訳で、次回はこれの改善をしていこうと思います。

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