見出し画像

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

前回の記事はこちら
前回は拠点を作成しました。

売値の項目を追加

ExcelItemDataファイルにpriceの項目を追加します。

スクリーンショット 2020-11-04 21.49.29

多すぎるので一部抜粋しています。
データを取り込む前にItemクラスに追記します。

public class ItemData
{
   // パラメーターの追加
   public int price;

   public ItemData Copy()
   {
       ItemData p = new ItemData();
       /*   省略   */
       p.condition = condition;
       p.extra = extra;
       p.shot = shot;
       // 以下1行を追記
       p.price = price;
       p.detail = detail;
       return p;
   }
}

// パラメーターを追加
public int price { get { return baseData.price * (extPoint + 1); } }
public int bidprice { get { return price / 2; } }

<売値>×<+値>で価格が決まるようにしました。なお、店側の買値は売値の1/2になります。

今後のプレイヤーについて

ずっと悩んでいたのですが、ここにきて今まで使用してきたファイティングユニティちゃんからSDユニティちゃんにプレイヤーを変更しようかなと思います。すみません。
また、使用するアセットの関係でUnityのバージョンも上げます。
次は「2019.4.13f1」です。
(Unityのバージョンを上げる場合はプロジェクトのバックアップを取っておくことを強く推奨致します)
という訳で今後使用するアセットはこちら。

スクリーンショット 2020-11-05 14.25.49

「SD Unity-chan Action Adventure Pack」

初めての有料アセットですね。いろんな動作があり便利そうです。
もし他のモデルでこのアセットを使用する場合、一応以下のアセットで動作確認する必要があるそうです。

スクリーンショット 2020-11-05 10.08.54

「SD Unity-Chan Haon Custom Bundle」

ただ、やっぱり有料なので、使用するかどうかは個人の判断に任せたいと思います。使用しない場合はそのままファイティングユニティちゃんなどを使用して下さい。

スクリーンショット 2020-11-05 16.09.25

アニメーションの調節に骨が折れました......。

ショップウィンドウ

ショップウィンドウを作成します。イメージはこんな感じです。​

ショップサンプル

スクロールはしません。一度に買えるのは7個程度ということにします。
イメージができたところでUnityエディタで作成しましょう。
筆者は以下のようになりました。

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

ヒエラルキータブはこんな感じです。

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

MainSceneのInventoryWindowをそのまま流用することにしました。コピー&ペーストで持ってくると楽です。

ShopActionクラス

InventoryActionクラスの代わりとなる「ShopAction」スクリプトを作成します。そこに、以下のように記述して下さい。

using UnityEngine;

public class ShopAction : MonoBehaviour
{
   public ItemInventory itemInventory;
   public ItemSlotDisplay display;
   public SubMenu subMenu;
   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;
   private const string choiceOrder = "Buy,Cancel";

   // 独自の更新メソッド
   public void Proc(bool isAction = false)
   {
       switch (action)
       {
           case EAct.KeyInput: KeyInput(isAction); 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 OpenShopMenu()
   {
       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.SHOPMENUMOVELEFT))
           return display.MoveSelect(EDir.Left);
       if (KeyConfig.GetKeyDown(KeyConfig.EType.SHOPMENUMOVERIGHT))
           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.SHOPMENUMOVELEFT))
           return display.MoveSelect(EDir.Left);
       if (KeyConfig.GetKey(KeyConfig.EType.SHOPMENUMOVERIGHT))
           return display.MoveSelect(EDir.Right);
       return true;
   }

   /**
   * 選択するサブメニューの項目を変更する
   */
   private bool MoveSelectSubMenuItem()
   {
       if (Input.anyKeyDown) inputTime = 0;
       if (KeyConfig.GetKeyDown(KeyConfig.EType.SHOPSUBMENUUP))
           return subMenu.MoveSelect(EDir.Up);
       if (KeyConfig.GetKeyDown(KeyConfig.EType.SHOPSUBMENUDOWN))
           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.SHOPSUBMENUUP))
           return subMenu.MoveSelect(EDir.Up);
       if (KeyConfig.GetKey(KeyConfig.EType.SHOPSUBMENUDOWN))
           return subMenu.MoveSelect(EDir.Down);
       return true;
   }

   /**
   * アイテムを選択
   */
   private void SelectItem()
   {
       if (KeyConfig.GetKeyDown(KeyConfig.EType.SHOPMENUACTION))
       {
           selectItem = display.GetSelectItem();
           if (selectItem != null)
           {
               subMenu.SetChoices(choiceOrder);
               subMenu.Show();
           }
       }
   }

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

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


   /**
   * 待機中
   */
   private void KeyInput(bool isAction)
   {
       if (!isOpen && isAction)
           OpenShopMenu();
       else if (isOpen && KeyConfig.GetKeyDown(KeyConfig.EType.SHOPMENUCLOSE))
           OpenShopMenu();
   }

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

   /**
   * アクション中
   */
   private void Act()
   {
       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()
   {
       action = EAct.MoveEnd;
   }

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

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

基本はInventoryActionクラスと同じです。
Inventory Actionコンポーネントをこれに差し替えて下さい。
KeyConfigクラスに追記します。

// 施設メニュー操作
FACILITYACTION = KeyCode.Space,
// ショップメニュー操作
SHOPMENUMOVELEFT = KeyCode.LeftArrow,
SHOPMENUMOVERIGHT = KeyCode.RightArrow,
SHOPSUBMENUUP = KeyCode.UpArrow,
SHOPSUBMENUDOWN = KeyCode.DownArrow,
SHOPMENUACTION = KeyCode.Space,
SHOPMENUCANCEL = KeyCode.Escape,
SHOPMENUCLOSE = KeyCode.Escape

プレイヤーが接触したらショップメニューを出す

店(あるいは店員)の周りで特定のキーを押すとショップメニューを出すことにします。
PlayerMovementInTownクラスを変更します。

// パラメーターを追加
public Transform[] facilities;
public string[] facilityName;
public string facility = "";

// メソッドを追加
private void InFacility()
{
   Vector3 pos = transform.position;
   for (int i = 0; i < facilities.Length; i++)
   {
       Vector3 fpos = facilities[i].position;
       Vector3 scale = facilities[i].localScale;
       if (pos.x > fpos.x - scale.x * 5 && pos.x < fpos.x + scale.x * 5
           && pos.z > fpos.z - scale.z * 5 && pos.z < fpos.z + scale.z * 5)
       {
           facility = facilityName[i];
           return;
       }
   }
   facility = "";
}

// Updateメソッドを以下に変更
public void Move()
{
   float x = Input.GetAxis("Horizontal");
   float z = Input.GetAxis("Vertical");

   Vector3 playerPos = new Vector3(x, 0, z);

   if (playerPos.magnitude > 0.1)
   {
       transform.rotation = Quaternion.LookRotation(playerPos);
       transform.Translate(Vector3.forward * speed);
       transform.position = new Vector3(Mathf.Clamp(transform.position.x,
           canMoveFloor.position.x - canMoveFloor.localScale.x * 5,
           canMoveFloor.position.x + canMoveFloor.localScale.x * 5),
           transform.position.y, Mathf.Clamp(transform.position.z,
           canMoveFloor.position.z - canMoveFloor.localScale.z * 5,
           canMoveFloor.position.z + canMoveFloor.localScale.z * 5));
       animator.SetFloat(hashSpeedPara, speed * 16, speedDampTime, Time.deltaTime);
       InFacility(); // これを追記
   }
   else
   {
       animator.SetFloat(hashSpeedPara, 0.0f, speedDampTime, Time.deltaTime);
   }
}

Facilitiesにはお店の範囲を表すPlaneオブジェクトを設定します。
Facility Namesには施設の種類を入力します。
とりあえずこんな感じにしておいて下さい。

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

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

拠点内のシーケンス管理

これらを利用するには何らかの形で呼び出さないといけません。拠点内のシーケンスを管理するクラスのUpdateメソッドにて呼び出しましょう。
「SequenceManagerInTown」スクリプトを作成して下さい。そこに、以下のように記述します。

using UnityEngine;

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

   private void Update()
   {
       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;
           }
       }
       playerMovement.Move();
   }
}

後、Inventoryクラスも変更します。

public Item GetFootItem()
{
   ActorMovement move = GetComponent<ActorMovement>();
   if (move == null) return null;
   Pos2D grid = move.grid;
   GameObject obj = GetComponentInParent<Field>().GetExistItem(grid.x, grid.z);
   if (obj == null) return null;
   return obj.GetComponent<ItemParamsController>().parameter;
}

ビルドしたら、SequenceManagerオブジェクトを作成し、このスクリプトをアタッチします。
実行してみます。

アイテムショップ

ちゃんと表示されましたか?

というところで今回はここまでにします。
次回は実際にアイテムを売ることができるようにしたいです。

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