見出し画像

【Unity】3Dローグライクゲームの作り方〜Step14-3〜

前回の記事はこちら
前回はショップメニュー画面を作成しました。

(オプション)店員を配置する場合

もし店員に話しかけたらプレイヤーに振り向くようにする場合、次のようにすればOKです。

// パラメーターを追加
public ItemInventory playerInventory;

// メソッドを変更
private void MoveBegin()
{
   isOpen = !isOpen;
   if (isOpen)
   {
       display.Show();
       gameObject.SetActive(true);
       itemInventory.transform.LookAt(playerInventory.transform);
       // カメラがある場合は以下も記述
       itemInventory.transform.GetChild(itemInventory.transform.childCount - 1).gameObject.SetActive(true);
   }
   action = EAct.Move;
}

private void MoveEnd()
{
   if (isOpen) action = EAct.ActBegin;
   else
   {
       gameObject.SetActive(false);
       // カメラがある場合は以下も記述
       itemInventory.gameObject.GetComponentInChildren<Camera>().gameObject.SetActive(false);
       action = EAct.TurnEnd;
   }
}

そういえば前回Shop ActionコンポーネントのItem Inventoryの項目に何を設定するか伝えるのを忘れていました。
このコードを使用する際はItemInventoryスクリプトを店員に、そうでなければどのオブジェクトにアタッチしてもいいです。
ただ、わかりやすくするのであればショップの建物にアタッチするのが無難かと思います。そしてそれを設定します。
また、店員を表示しているカメラがあれば、そのカメラを店員のオブジェクト内のヒエラルキー最後に入れます。

スクリーンショット 2020-11-06 19.22.30

店員が振り向いた

InventoryActionInTownクラス

今回は、悩んだのですが、拠点内でインベントリを開くと「使う」や「置く」などの代わりに「売る」項目が出てくるようにしたいと思います。
ということで新たにMainSceneからInventoryWindowをコピーし、Canvas以下に貼り付けて下さい。
そして、「InventoryActionInTown」スクリプトを作成します。

using UnityEngine;

public class InventoryActionInTown : MonoBehaviour
{
   public ItemInventory itemInventory;
   public StoneInventory stoneInventory;
   public ItemSlotDisplay display;
   public SubMenu subMenu;
   public InventoryAnimation anim;
   public float inputHoldDelay = 0.3f;

   private EAct action = EAct.KeyInput;
   private bool isOpen = false;
   private Item selectItem = null;
   private float inputTime = 0;
   private bool isCursorMoving = false;

   // 独自の更新メソッド
   public void Proc()
   {
       switch (action)
       {
           case EAct.KeyInput: KeyInput(); break;
           case EAct.ActBegin: ActBegin(); break;
           case EAct.Act: Act(); break;
           case EAct.ActEnd: ActEnd(); break;
           case EAct.MoveBegin: MoveBegin(); break;
           case EAct.Move: Move(); break;
           case EAct.MoveEnd: MoveEnd(); break;
           case EAct.TurnEnd: TurnEnd(); break;
       }
   }

   /**
   * 現在の行動状態を返す
   */
   public EAct GetAction() => action;

   /**
   * インベントリを開く
   */
   public void OpenInventory(bool isStone)
   {
       if (isStone)
       {
           display.inventory = stoneInventory;
           action = EAct.MoveBegin;
       }
       else
       {
           display.inventory = itemInventory;
           action = EAct.MoveBegin;
       }
   }

   /**
   * 選択するアイテムスロットを変更する
   */
   private bool MoveSelectItem()
   {
       if (isCursorMoving) return display.MoveSelect(EDir.Pause);
       if (Input.anyKeyDown) inputTime = 0;
       if (KeyConfig.GetKeyDown(KeyConfig.EType.INVENTORYMOVELEFT))
           return display.MoveSelect(EDir.Left);
       if (KeyConfig.GetKeyDown(KeyConfig.EType.INVENTORYMOVERIGHT))
           return display.MoveSelect(EDir.Right);
       inputTime += Time.deltaTime;
       if (inputTime < inputHoldDelay) return true;
       if (!Input.anyKey) return true;
       inputTime = 0;
       if (KeyConfig.GetKey(KeyConfig.EType.INVENTORYMOVELEFT))
           return display.MoveSelect(EDir.Left);
       if (KeyConfig.GetKey(KeyConfig.EType.INVENTORYMOVERIGHT))
           return display.MoveSelect(EDir.Right);
       return true;
   }

   /**
   * 選択するサブメニューの項目を変更する
   */
   private bool MoveSelectSubMenuItem()
   {
       if (Input.anyKeyDown) inputTime = 0;
       if (KeyConfig.GetKeyDown(KeyConfig.EType.INVENTORYSUBMENUUP))
           return subMenu.MoveSelect(EDir.Up);
       if (KeyConfig.GetKeyDown(KeyConfig.EType.INVENTORYSUBMENUDOWN))
           return subMenu.MoveSelect(EDir.Down);
       inputTime += Time.deltaTime;
       if (inputTime < inputHoldDelay) return true;
       if (!Input.anyKey) return true;
       inputTime = 0;
       if (KeyConfig.GetKey(KeyConfig.EType.INVENTORYSUBMENUUP))
           return subMenu.MoveSelect(EDir.Up);
       if (KeyConfig.GetKey(KeyConfig.EType.INVENTORYSUBMENUDOWN))
           return subMenu.MoveSelect(EDir.Down);
       return true;
   }

   /**
   * アイテムを選択
   */
   private void SelectItem()
   {
       if (KeyConfig.GetKeyDown(KeyConfig.EType.INVENTORYACTION))
       {
           string choiceOrder = "";
           selectItem = display.GetSelectItem();
           if (selectItem != null)
           {
               if (selectItem.isEquip) choiceOrder = "Remove";
               else
               {
                   choiceOrder = "Sell";
                   if ((int)selectItem.id < 1001) choiceOrder += ",Equip";
               }
               subMenu.SetChoices(choiceOrder);
               subMenu.Show();
           }
       }
   }

   /**
   * サブメニューの項目を選択
   */
   private void SelectSubMenuItem()
   {
       if (KeyConfig.GetKeyDown(KeyConfig.EType.INVENTORYACTION))
       {
           action = EAct.Act;
           subMenu.Hide();
       }
   }

   /**
   * アイテムの選択を解除
   */
   private void UnSelectItem()
   {
       if (KeyConfig.GetKeyDown(KeyConfig.EType.INVENTORYCANCEL))
       {
           selectItem = null;
           subMenu.Hide();
       }
   }


   /**
   * 待機中
   */
   private void KeyInput()
   {
       if (!isOpen && KeyConfig.GetKeyDown(KeyConfig.EType.INVENTORYSTONEOPEN))
           OpenInventory(true);
       else if (KeyConfig.GetKeyDown(KeyConfig.EType.INVENTORYSORT))
       {
           display.inventory.Sort();
           display.Show();
       }
       else if (!isOpen && KeyConfig.GetKeyDown(KeyConfig.EType.INVENTORYITEMOPEN))
           OpenInventory(false);
       else if (isOpen && KeyConfig.GetKeyDown(KeyConfig.EType.INVENTORYCLOSE))
           OpenInventory(false);
   }

   /**
   * アクションを始める
   */
   private void ActBegin()
   {
       if (isCursorMoving)
       {
           isCursorMoving = !MoveSelectItem();
           return;
       }
       if (selectItem == null)
       {
           isCursorMoving = !MoveSelectItem();
           if (isCursorMoving) return;
           KeyInput();
           SelectItem();
       }
       else
       {
           MoveSelectSubMenuItem();
           SelectSubMenuItem();
           UnSelectItem();
       }
   }

   /**
   * アクション中
   */
   private void Act()
   {
       if (selectItem == null)
       {
           action = EAct.ActBegin;
           return;
       }
       ActorUseItems actorUse = display.inventory.GetComponentInParent<ActorUseItems>();
       ActorUseItems.UseItem use = actorUse.GetDelegate(subMenu.GetSelectItemMethod());
       if (use(selectItem))
       {
           display.Show();
           selectItem = null;
           action = EAct.ActBegin;
       }
   }

   /**
   * アクションが終わった
   */
   private void ActEnd()
   {
       action = EAct.TurnEnd;
   }

   /**
   * 移動を始める
   */
   private void MoveBegin()
   {
       isOpen = !isOpen;
       if (isOpen)
       {
           display.Show();
           gameObject.SetActive(true);
       }
       action = EAct.Move;
   }

   /**
   * 移動中
   */
   private void Move()
   {
       if (isOpen)
       {
           if (anim.Open())
           {
               action = EAct.MoveEnd;
               return;
           }
       }
       else
       {
           if (anim.Close())
           {
               action = EAct.MoveEnd;
               return;
           }
       }
       action = EAct.Move;
   }

   /**
   * 移動が終わった
   */
   private void MoveEnd()
   {
       if (isOpen) action = EAct.ActBegin;
       else
       {
           gameObject.SetActive(false);
           action = EAct.ActEnd;
       }
   }

   /**
   * ターンが終わった
   */
   private void TurnEnd()
   {
       action = EAct.KeyInput;
   }
}

これをInventory Actionコンポーネントの代わりにアタッチします。
そしてPlayerにアタッチしたItem InventoryコンポーネントとStone Inventoryコンポーネントを設定します。
また、Sub Menuコンポーネントの項目を以下のように変更します。

スクリーンショット 2020-11-06 20.11.27

SequenceManagerInTownに追記します。

using UnityEngine;

public class SequenceManagerInTown : MonoBehaviour
{
   public PlayerMovementInTown playerMovement;
   public InventoryActionInTown inventoryAction;
   public ShopAction shopAction;

   private void Update()
   {
       EAct iAct = inventoryAction.GetAction();
       if (iAct != EAct.KeyInput)
       {
           inventoryAction.Proc();
           return;
       }
       EAct sAct = shopAction.GetAction();
       if (sAct != EAct.KeyInput)
       {
           shopAction.Proc();
           return;
       }
       if (KeyConfig.GetKeyDown(KeyConfig.EType.FACILITYACTION))
       {
           switch (playerMovement.facility)
           {
               case "Shop":
                   shopAction.Proc(true);
                   break;
           }
       }
       inventoryAction.Proc();
       iAct = inventoryAction.GetAction();
       if (iAct != EAct.KeyInput) return;
       playerMovement.Move();
   }
}

これで一回テストしてみます。開いたり閉じたりできればOKです。
次はアイテムを入れてテストします。できれば装備品がいいでしょう。
この際、PlayerにActorUseItemsスクリプトとActorParamsControllerスクリプトをアタッチしておく必要があります(ActorMovementスクリプトはアタッチしなくて大丈夫です)。
また、MainSceneからMessageWindowをコピーして持ってきましょう。
ちゃんと装備できるか確認しておいて下さい。
なお、装備してもインベントリは閉じないはずです。閉じたらバグなのでお知らせください。

プレイヤーにお金を持たせる

売る処理を書く前に、プレイヤーにお金を持たせてみたいと思います。
ActorParamsControllerクラスに追記して下さい。

public int money = 0;

更にセーブ・ロードできるようにします。
ActorSaveDataクラスに追記して下さい。

public int money;

SaveDataManagerクラスにも追記します。

private ActorSaveData MakeActorData(Transform actor)
{
   ActorMovement move = actor.GetComponent<ActorMovement>();
   ActorSaveData actorSaveData = new ActorSaveData();
   actorSaveData.grid = new Pos2D();
   actorSaveData.grid.x = move.grid.x;
   actorSaveData.grid.z = move.grid.z;
   actorSaveData.direction = move.direction;
   ActorParamsController param = actor.GetComponent<ActorParamsController>();
   actorSaveData.parameter = param.GetParameter();
   // 以下を追記
   actorSaveData.money = param.money;
   actorSaveData.conditions = param.GetConditions();
   actorSaveData.itemInventory = MakeInventoryData(actor.GetComponent<ItemInventory>());
   if (param.parameter.id == EActor.PLAYER)
       actorSaveData.stoneInventory = MakeInventoryData(actor.GetComponent<StoneInventory>());
   return actorSaveData;
}

private void LoadActorData(ActorSaveData data, Transform actor)
{
   ActorMovement move = actor.GetComponent<ActorMovement>();
   move.SetPosition(data.grid.x, data.grid.z);
   move.SetDirection(data.direction);
   ActorParamsController param = actor.GetComponent<ActorParamsController>();
   param.SetParameter(data.parameter);
   // 以下を追記
   param.money = data.money;
   param.SetConditions(data.conditions);
   LoadInventoryData(data.itemInventory, actor.GetComponent<ItemInventory>());
   if (data.parameter.id == EActor.PLAYER)
       LoadInventoryData(data.stoneInventory, actor.GetComponent<StoneInventory>());
}

セーブ・ロードした時の処理を確認したいのですが、今のままだとセーブできません。なので確認は後回しにします。まあ恐らく大丈夫でしょう。

アイテムを売る処理

最後にアイテムを売れるようにしたいと思います。ActorUseItemsクラスを以下のように変更して下さい。

// メソッドを追加
/**
* 引数で渡されたアイテムを売る
*/
public bool Sell(Item it)
{
   bool isEnd = false;
   if (it.type == EItemType.Stone)
   {
       StoneInventory si = GetComponent<StoneInventory>();
       isEnd = si.Remove(it);
   }
   else isEnd = inventory.Remove(it);
   if (isEnd)
   {
       param.money += it.bidprice;
       Message.Add(39, it.name);
       Message.Add(40, it.bidprice + "");
   }
   return true;
}

// メソッドを変更
public UseItem GetDelegate(string method)
{
   switch (method)
   {
       case "Use": return new UseItem(Use);
       case "Equip": return new UseItem(Equip);
       case "Remove": return new UseItem(Remove);
       case "Put": return new UseItem(Put);
       case "Throw": return new UseItem(Throw);
       case "PickUp": return new UseItem(PickUp);
       case "PickUpUse": return new UseItem(PickUpUse);
       case "Replace": return new UseItem(Replace);
       case "PickUpThrow": return new UseItem(PickUpThrow);
       case "Sell": return new UseItem(Sell);
   }
   return new UseItem(DoNothing);
}

今回追加したメッセージは以下の通りです。

スクリーンショット 2020-11-06 22.00.21

実行してみます。

スクリーンショット 2020-11-06 22.14.46

魔石+10なので単価100*(10+1)/2で550枚になります。ちゃんと計算式が正しいかも確認しておきましょう。

売る処理を書いたところで今回はここまでです。次回は買う処理の実装をする予定です。

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