見出し画像

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

前回の記事はこちら
前回はメニューを作成しました。

メニューの仕様変更

メニューを円形に並べていましたが、横に並べた方がやっぱりいいかなと思いました。という訳で少し変更します。

スクリーンショット 2020-11-01 10.33.41

INVENTORYの項目を忘れていたので付け足しました。
その為、Sub Menuコンポーネントを以下のように設定する必要があります。

スクリーンショット 2020-11-01 11.12.48

DungeonMenuActionクラスも以下のようにします。

private const string choiceOrder =
   "Status,Inventory,Save,Stairs,Achieve,Setting,Exit,Title";

private bool MoveSelectSubMenuItem()
{
   if (Input.anyKeyDown)
   {
       inputTime = 0;
       if (Input.GetKeyDown(KeyCode.LeftArrow)) return subMenu.MoveSelect(EDir.Up);
       if (Input.GetKeyDown(KeyCode.RightArrow)) return subMenu.MoveSelect(EDir.Down);
   }
   inputTime += Time.deltaTime;
   if (inputTime < inputHoldDelay) return true;
   if (!Input.anyKey) return true;
   inputTime = 0;
   if (Input.GetKey(KeyCode.LeftArrow)) return subMenu.MoveSelect(EDir.Up);
   if (Input.GetKey(KeyCode.RightArrow)) return subMenu.MoveSelect(EDir.Down);
   return true;
}

ビルドしたら、左右キーで選択できるか確認しておいて下さい。

スクリーンショット 2020-11-01 11.16.54

ステータス画面の作成

それではステータス画面の作成に移りたいと思います。
まずは外形から作ります。イメージはこんな感じです。

ステータス画面サンプル

早速作っていきましょう。
まず背景画像を用意します。

画像4

ユニティちゃんの画像を使用する場合は上の背景画像と合成して使って下さい。
用意した背景画像をSpritesフォルダの中に入れ、インポート設定のテクスチャタイプを「スプライト (2DとUI)」にし、適用ボタンを押して下さい。
次にMessageWindowがある方のCanvasに子オブジェクトを配置し、名前を「StatusWindow」にします。そしてその階層下に「StatusPanel」という名前のパネルを作成します。位置などの設定はどちらもこのようにして下さい。

スクリーンショット 2020-11-01 13.07.09

StatusPanelの画像に先ほど用意した背景画像を設定します。そして「アスペクト比を保存」にチェックを入れ、色の透明度を上げると以下のようになります。

スクリーンショット 2020-11-01 13.08.23

ここにテキストなどを当てはめていきます。
「Parameters」という子オブジェクトをStatusPanelに作成し、その中にテキストや画像を入れて下さい。そして階層がこんな感じになるようにします。

スクリーンショット 2020-11-01 13.47.05

位置などの調節はお任せします。

スクリーンショット 2020-11-01 13.47.10

最後にHPバーなどを作成していきましょう。
画像は以下を使用して下さい。

画像9

HPバー

画像10

EXPバー

まずStatusPanelの階層下に空のオブジェクト「Bars」を作成します。更に中に空のオブジェクト「ExpBar」を作成します。その階層下に画像を二つ作成し、名前をそれぞれ「Background」、「Fill」にし、Expバーの画像を設定、色を設定します。そしてFillの方の画像タイプを以下のように設定します。

スクリーンショット 2020-11-01 14.19.09

アスペクト比を保存はBackgroundの方にもチェックを入れておきましょう。
そして位置やサイズを調節したらこんな感じになります。

スクリーンショット 2020-11-01 14.21.16

同様にHPや満腹度のバーも作成します。

スクリーンショット 2020-11-01 14.48.09

筆者は上記のようになりました。

StatusWindowActionスクリプト

ようやっとコードを書く段まできました。
まずActorParamsControllerクラスにメソッドを追加して下さい。

/**
* このレベルになるために必要な経験値の総量を返す
*/
public int GetNextLvExp(int lv)
{
   int expmax = 0;
   for (int i = 1; i < lv; i++) expmax += paramsData[i].exp;
   return expmax;
}

次に「StatusWindowAction」スクリプトを作成し、以下を記述して下さい。

using UnityEngine;
using UnityEngine.UI;

public class StatusWindowAction : MonoBehaviour
{
   public ActorParamsController actorParamsController;
   public GameObject parameters;
   public GameObject bars;

   private EAct action = EAct.KeyInput;

   // 独自の更新メソッド
   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.TurnEnd: TurnEnd(); break;
       }
   }

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

   /**
   * 画面を開く
   */
   private void Show()
   {
       Params param = actorParamsController.GetParameter();
       ActorParamsController.Equipment equipment = actorParamsController.equipment;
       parameters.transform.GetChild(0).GetComponent<Text>().text = param.lv + "";
       parameters.transform.GetChild(1).GetComponent<Text>().text = param.exp + "";
       parameters.transform.GetChild(2).GetComponent<Text>().text =
           param.str + " (+" + equipment.GetAllAtk() + ")";
       parameters.transform.GetChild(3).GetComponent<Text>().text =
           param.def + " (+" + equipment.GetAllDef() + ")";
       ShowEquipment(parameters.transform.GetChild(4).gameObject, equipment.weapon);
       ShowEquipment(parameters.transform.GetChild(5).gameObject, equipment.armor);
       ShowEquipment(parameters.transform.GetChild(6).gameObject, equipment.accessory);
       int exp = param.exp - actorParamsController.GetNextLvExp(param.lv);
       int expmax = actorParamsController.GetNextLvExp(param.lv + 1);
       FillBar(bars.transform.GetChild(0), exp, expmax);
       FillBar(bars.transform.GetChild(1), param.hp, param.hpmax);
       FillBar(bars.transform.GetChild(2), param.food, param.foodmax);
       gameObject.SetActive(true);
   }

   /**
   * 装備を表示
   */
   private void ShowEquipment(GameObject equipSlot, Item it)
   {
       if (it.id == EItem.NONE) equipSlot.SetActive(false);
       else
       {
           equipSlot.GetComponent<Image>().sprite = Resources.Load<Sprite>("Sprites/" + it.sprite);
           equipSlot.GetComponentInChildren<Text>().text = it.name;
           equipSlot.SetActive(true);
       }
   }

   /**
   * バーを更新
   */
   private void FillBar(Transform bar, float point, float max)
   {
       bar.GetComponentsInChildren<Image>()[1].fillAmount = point / max;
   }

   /**
   * 画面を閉じる
   */
   private void Hide()
   {
       gameObject.SetActive(false);
   }

   /**
   * 待機中
   */
   private void KeyInput()
   {
       Show();
       action = EAct.ActBegin;
   }

   /**
   * アクションを始める
   */
   private void ActBegin()
   {
       if (Input.anyKeyDown && Input.GetKeyDown(KeyCode.Escape))
       {
           Hide();
           action = EAct.Act;
       }
   }

   /**
   * アクション中
   */
   private void Act()
   {
       action = EAct.ActEnd;
   }

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

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

最後にDungeonMenuActionクラスを変更します。
変更点が多いので全て載せます。

using UnityEngine;

public class DungeonMenuAction : MonoBehaviour
{
   public SubMenu subMenu;
   public StatusWindowAction statusWindowAction;
   public StairsMenuAction stairsMenuAction;
   public float inputHoldDelay = 0.3f;

   private const string choiceOrder =
       "Status,Inventory,Save,Stairs,Achieve,Setting,Exit,Title";
   private EAct action = EAct.KeyInput;
   private float inputTime = 0;

   // 独自の更新メソッド
   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.TurnEnd: TurnEnd(); break;
       }
   }

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

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

   /**
   * メニューの項目を選択
   */
   private void SelectSubMenuItem()
   {
       if (Input.anyKeyDown)
       {
           if (Input.GetKeyDown(KeyCode.Escape))
           {
               while (subMenu.GetSelectItemMethod() != "Exit")
                   subMenu.MoveSelect(EDir.Up);
               action = EAct.Act;
               Hide();
               return;
           }
           if (Input.GetKeyDown(KeyCode.Space))
           {
               string method = subMenu.GetSelectItemMethod();
               switch (method)
               {
                   case "Status":
                       if (ShowStatusWindow())
                       {
                           Hide();
                           action = EAct.Act;
                       }
                       break;
                   case "Stairs":
                       if (ShowStairsMenu())
                       {
                           Hide();
                           action = EAct.Act;
                       }
                       break;
                   default:
                       Hide();
                       action = EAct.Act;
                       break;
               }
           }
       }
       
   }

   /**
   * メニューを開く
   */
   private void Show()
   {
       subMenu.SetChoices(choiceOrder);
       subMenu.Show();
       gameObject.SetActive(true);
       action = EAct.ActBegin;
   }

   /**
   * 階段メニューを開く
   */
   private bool ShowStairsMenu()
   {
       stairsMenuAction.Proc();
       EAct sAct = stairsMenuAction.GetAction();
       return sAct != EAct.KeyInput;
   }

   /**
   * ステータス画面を開く
   */
   private bool ShowStatusWindow()
   {
       statusWindowAction.Proc();
       EAct sAct = statusWindowAction.GetAction();
       return sAct != EAct.KeyInput;
   }

   /**
   * メニューを閉じる
   */
   private void Hide()
   {
       gameObject.SetActive(false);
   }

   /**
   * 待機中
   */
   private void KeyInput()
   {
       if (Input.anyKeyDown && Input.GetKeyDown(KeyCode.Escape)) Show();
   }

   /**
   * アクションを始める
   */
   private void ActBegin()
   {
       MoveSelectSubMenuItem();
       SelectSubMenuItem();
   }

   /**
   * アクション中
   */
   private void Act()
   {
       string method = subMenu.GetSelectItemMethod();
       switch (method)
       {
           case "Status":
               if (!ShowStatusWindow()) Show();
               break;
           case "Stairs":
               if (!ShowStairsMenu()) action = EAct.ActEnd;
               break;
           default:
               if (Input.GetKeyUp(KeyCode.Space) || Input.GetKeyUp(KeyCode.Escape)) action = EAct.ActEnd;
               break;
       }
   }

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

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

ビルドしたら、StatusWindowにStatusWindowActionスクリプトをアタッチするようにして下さい。また、コンポーネントの新しく追加した項目を設定するのも忘れずに行っておきましょう。
実行してみます。

スクリーンショット 2020-11-01 18.10.30

敵を2体倒した状態

ちゃんと反映されるかいろいろ試してみましょう。

各種インベントリの表示

メニュー画面にインベントリの項目を増やしたので、インベントリも開けるようにしてみたいと思います。
InventoryActionクラスを変更します。

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

// メソッドを変更
private void KeyInput()
{
   if (Input.anyKeyDown)
   {
       if (!isOpen && Input.GetKeyDown(KeyCode.R))
       {
           OpenInventory(true);
       }
       else if (Input.GetKeyDown(KeyCode.R))
       {
           display.inventory.Sort();
           display.Show();
       }
       else if (Input.GetKeyDown(KeyCode.E))
       {
           OpenInventory(false);
       }
   }
}

次にDungeonMenuActionクラスを変更します。

// パラメーターを追加
public InventoryAction inventoryAction;

private void SelectSubMenuItem()
{
   if (Input.anyKeyDown)
   {
       if (Input.GetKeyDown(KeyCode.Escape))
       {
           while (subMenu.GetSelectItemMethod() != "Exit")
               subMenu.MoveSelect(EDir.Up);
           action = EAct.Act;
           Hide();
           return;
       }
       if (Input.GetKeyDown(KeyCode.Space))
       {
           string method = subMenu.GetSelectItemMethod();
           switch (method)
           {
               case "Status":
                   if (ShowStatusWindow())
                   {
                       Hide();
                       action = EAct.Act;
                   }
                   break;
               // 以下5行を追記
               case "Inventory":
                   Hide();
                   action = EAct.KeyInput;
                   inventoryAction.OpenInventory(Input.GetKey(KeyCode.R));
                   break;
               case "Stairs":
                   if (ShowStairsMenu())
                   {
                       Hide();
                       action = EAct.Act;
                   }
                   break;
               default:
                   Hide();
                   action = EAct.Act;
                   break;
           }
       }
   }
       
}

実行してみます。

メニューからインベントリを開く

インベントリを開くことができ、アイテムも通常通り使えることを確認しておきましょう。

というところで今回はここまでです。
次回はセーブ(と言ってもダンジョン内なので一時的なものですが)を実装したいと思います。

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