見出し画像

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

前回の記事はこちら
前回は足踏み、方向転換、ダッシュを実装しました。

パラメーターを2つに分ける

+付きアイテムがあればアイテムに多様性が出ますね。という訳で実装していきたいと思います。
Itemスクリプトを以下のように変更して下さい。

[System.Serializable]
public class Item
{
   public EItem id { get { return baseData.id; } }
   public EItemType type { get { return baseData.type; } }
   public string name {
       get { return baseData.name + (extPoint > 0 ? "+" + extPoint : ""); }
       set { baseData.name = value; }
   }
   public string prefab { get { return baseData.prefab; } }
   public string sprite { get { return baseData.sprite; } }
   public bool isEquip = false;
   public int atk { get{ return baseData.atk + extData.atk; } set { baseData.atk = value; } }
   public int def { get { return baseData.def + extData.def; } set { baseData.def = value; } }
   public int hp { get { return baseData.hp + extData.hp; } set { baseData.hp = value; } }
   public int food { get { return baseData.food + extData.food; } set { baseData.food = value; } }
   public int dmg { get { return baseData.dmg; } set { baseData.dmg = value; } }
   public ECondition condition { get { return baseData.condition; } }
   public string extra { get { return baseData.extra; } }
   public EItem shot { get { return baseData.shot; } }
   public string detail { get { return baseData.detail; } }

   [UnityEngine.SerializeField]
   private ItemData baseData;
   [UnityEngine.SerializeField]
   private ItemData extData;
   private int extPoint { get { return extData.GetPlus(); } }

   /**
   * アイテムデータのコピーを返す
   */
   public Item Get()
   {
       Item p = new Item();
       p.isEquip = isEquip;
       p.baseData = baseData.Copy();
       p.extData = extData.Copy();
       return p;
   }

   [System.Serializable]
   public class ItemData
   {
       public EItem id;
       public EItemType type;
       public string name;
       public string prefab;
       public string sprite;
       public int atk;
       public int def;
       public int hp;
       public int food;
       public int dmg;
       public ECondition condition;
       public string extra;
       public EItem shot;
       public string detail;

       /**
       * アイテムデータを返す
       */
       public Item Get()
       {
           Item p = new Item();
           p.baseData = Copy();
           p.extData = new ItemData();
           return p;
       }

       /**
       * このオブジェクトのコピーを返す
       */
       public ItemData Copy()
       {
           ItemData p = new ItemData();
           p.id = id;
           p.type = type;
           p.name = name;
           p.prefab = prefab;
           p.sprite = sprite;
           p.atk = atk;
           p.def = def;
           p.hp = hp;
           p.food = food;
           p.dmg = dmg;
           p.condition = condition;
           p.extra = extra;
           p.shot = shot;
           p.detail = detail;
           return p;
       }

       /*
       * プラスの計算をし、返す
       */
       public int GetPlus()
       {
           return atk + def + hp + food;
       }
   }
}

基本の値と追加の値を分けました。
これに伴い、ExcelItemDataクラスを変更します。

[ExcelAsset]
public class ExcelItemData : ScriptableObject
{
	public List<Item.ItemData> Equipments;
	public List<Item.ItemData> Goods;
}

これでビルドすると、Fieldクラスにエラーが出るので、以下を変更します。

private void SetItem(string name, int xgrid, int zgrid)
{
   Item itemData;
   EItem itemId = EItem.NONE;
   if (name.Equals("Random"))
   {
       /*   省略   */
   }
   else if (!System.Enum.TryParse(name, out itemId)) return;
   // 以下2行の最後に.Get()を追記
   if ((int)itemId > 1000) itemData = itemDatabase.Goods.Find(n => n.id == itemId).Get();
   else itemData = itemDatabase.Equipments.Find(n => n.id == itemId).Get();
   if (itemData == null) return;
   GameObject itemObj = (GameObject)Resources.Load("Prefabs/" + itemData.prefab);
   GameObject item = Instantiate(itemObj, items.transform);
   item.GetComponent<ItemMovement>().SetPosition(xgrid, zgrid);
   item.GetComponent<ItemParamsController>().SetParams(itemData);
}

ビルドに成功したら、/Asset/Resources/Datas/ExcelItemDataオブジェクトを一旦削除します。
ここで一旦Unityエディターを再起動して下さい。
そしてExcelItemDataファイルを再インポートしましょう。
するとInventoryEditorクラスにエラーが出ると思うので、以下のように変更して下さい。

private void DrawElement(Rect rect, int index, bool isActive, bool isFocused)
{
   if (isFocused)
   {
       ExcelItemData database = Resources.Load<ExcelItemData>("Datas/ExcelItemData");
       Item item;
       // 以下2行の最後に.Get()を追記
       if ((int)id > 1000) item = database.Goods.Find(n => n.id == id).Get();
       else item = database.Equipments.Find(n => n.id == id).Get();
       if (item != null)
       {
           SetItem(item, ref item);
           inventory.items[index] = item;
       }
   }
   EditorGUI.LabelField(rect, inventory.items[index].name);
}

まずはこれで実行して、不具合がないか確かめます。
なおインベントリにアイテムを入れている場合は全て削除してからもう一度入れ直しましょう。また、FieldコンポーネントにExcelItemDataを再設定することも忘れずに行っておきます。

+付きアイテムの出現

それでは+付きのアイテムを出現させたいと思います。
まず、ExcelAppearDataファイルに新しいシートを作成し、名前を「ItemPlusMax」にします。
そして以下のようにファイルに記述します。

スクリーンショット 2020-10-23 1.46.18

typeにはEItemTypeを、それ以外にはそれそれの+値の最大値を記述します(typeをidにしてEItemを記述するようにしても良いかもしれません)。
筆者は合計で99になるようにしましたが、その辺りは自由です。
次にExcelAppearDataスクリプトを以下のように変更します。

using System.Collections.Generic;
using UnityEngine;

[ExcelAsset]
public class ExcelAppearData : ScriptableObject
{
   public List<EnemyAppearData> EnemyAppear;
   public List<ItemAppearData> ItemAppear;
   public List<ItemPlusMaxData> ItemPlusMax;

   [System.Serializable]
   public class EnemyAppearData
   {
       public int floor;
       public EActor id1;
       public int lvmin1;
       public int lvmax1;
       public int rate1;
       public EActor id2;
       public int lvmin2;
       public int lvmax2;
       public int rate2;
   }

   [System.Serializable]
   public class ItemAppearData
   {
       public EItem id;
       public int start;
       public int end;
       public int rate;
       public bool shoponly;
   }

   [System.Serializable]
   public class ItemPlusMaxData
   {
       public EItemType type;
       public int atk;
       public int def;
       public int hp;
       public int food;
   }
}

そして新しいExcelAppearDataファイルと置き換えます。
Itemクラスに新しいメソッドを追記します。

/**
* アイテムデータの+値を設定する
*/
public void SetExtParams(ExcelAppearData.ItemPlusMaxData d)
{
   if (baseData.atk > 0)
       extData.atk = UnityEngine.Random.Range(0, d.atk + 1);
   if (baseData.def > 0)
       extData.def = UnityEngine.Random.Range(0, d.def + 1);
   if (baseData.hp > 0)
       extData.hp = UnityEngine.Random.Range(0, d.hp + 1);
   if (baseData.food > 0)
       extData.food = UnityEngine.Random.Range(0, d.food + 1);
}

元々の設定値が0の場合は+値を付けないことにしました。
付けたい人はif文を削除して下さい。
最後にFieldクラスのSetItemメソッドに以下を追記します。

private void SetItem(string name, int xgrid, int zgrid)
{
   Item itemData;
   EItem itemId = EItem.NONE;
   if (name.Equals("Random"))
   {
       /*   省略   */
   }
   else if (!System.Enum.TryParse(name, out itemId)) return;
   if ((int)itemId > 1000) itemData = itemDatabase.Goods.Find(n => n.id == itemId).Get();
   else itemData = itemDatabase.Equipments.Find(n => n.id == itemId).Get();
   if (itemData == null) return;
   // 以下1行を追記
   // (idにした方はtypeをidに置き換えて下さい)
   itemData.SetExtParams(appearDatabase.ItemPlusMax.Find(n => n.type == itemData.type));
   GameObject itemObj = (GameObject)Resources.Load("Prefabs/" + itemData.prefab);
   GameObject item = Instantiate(itemObj, items.transform);
   item.GetComponent<ItemMovement>().SetPosition(xgrid, zgrid);
   item.GetComponent<ItemParamsController>().SetParams(itemData);
}

ビルドして実行してみます。

スクリーンショット 2020-10-22 22.42.31

無事+付きアイテムを拾うことができました。
置いてみたりしても値が変わらないことを確認しておきましょう。

魔法弾にも反映する

折角の+付きアイテムなのですから、杖や巻物が出す魔法弾の攻撃力を上げたりなどしてみようと思います。
具体的には杖や巻物のatkの値を魔法弾のdmgの値にします。
ActorUseItemsクラスのShotとReadメソッドに追記します。

private bool Shot(Item it)
{
   if (usingItem == null)
   {
       if (it.type == EItemType.Wand) Message.Add(21, param.actorName, it.name);
       ExcelItemData database = Resources.Load<ExcelItemData>("Datas/ExcelItemData");
       Item item = database.Goods.Find(n => n.id == it.shot).Get();
       item.dmg = it.atk; // これを追記
       return Throw(item);
   }
   if (Throw(null))
   {
       /*   省略   +/
   }
   return false;
}

private bool Read(Item it)
{
   Field field = GetComponentInParent<Field>();
   if (it.extra.Contains("Shuffle"))
   {
       /*   省略   +/
   }
   if (it.extra.Contains("RoomEffect"))
   {
       if (usingItem != null) return Shot(it);
       if (usingItems == null)
       {
           Message.Add(31, param.actorName, it.name);
           List<ActorMovement> actors = field.GetOtherActorInRoom(move);
           if (actors == null) return Shot(it);
           ExcelItemData database = Resources.Load<ExcelItemData>("Datas/ExcelItemData");
           Item item = database.Goods.Find(n => n.id == it.shot).Get();
           item.dmg = it.atk; // これを追記
           GameObject itemObj = (GameObject)Resources.Load("Prefabs/" + item.prefab);
           usingItems = new List<GameObject>();
           foreach (ActorMovement actor in actors)
           {
               /*   省略   +/
           }
           return false;
       }
       if (usingItems.Count > 0)
       {
           /*   省略   +/
       }
       if (usingItems.Count == 0)
       {
           /*   省略   +/
       }
   }
   return false;
}

そして、ExcelItemDataファイルのGoodsシートにatkの列を追加し、杖と巻物の該当箇所を1以上にします。またその際、アイテム詳細の攻撃力もその値に置き換えるように変更しておくと良いでしょう。

スクリーンショット 2020-10-23 2.20.29

ちゃんと攻撃できていますね。

おまけ:階数が保存されていない

ところで今更気付いたのですが、階数を保存することをすっかり忘れていました。すみません。
これでは意味がないので修正します。
まずMapSaveDataクラスにパラメーターを追加します。

public int floorNum;

そしてSaveDataManagerクラスに追記します。

private MapSaveData MakeMapData()
{
   MapSaveData mapSaveData = new MapSaveData();
   Rect2D[] dummyranges = new Rect2D[0];
   // 以下1行を追記
   mapSaveData.floorNum = Field.floorNum;
   MakeMapObjectData(stairs.transform, ref mapSaveData.stairs, ref dummyranges);
   MakeMapObjectData(rooms.transform, ref mapSaveData.roompositions, ref mapSaveData.roomranges);
   MakeMapObjectData(connections.transform, ref mapSaveData.connections, ref dummyranges);
   mapSaveData.map = field.GetMapData();
   return mapSaveData;
}

private void LoadMapData(SaveData saveData)
{
   // 以下1行を追記
   Field.floorNum = saveData.mapData.floorNum;
   field.Reset();
   field.Create(saveData.mapData.map);
   for (int i = 0; i < 2; i++)
   {
       /*   省略   */
   }
   for (int i = 0; i < saveData.mapData.roompositions.Length; i++)
   {
       /*   省略   */
   }
   for (int i = 0; i < saveData.mapData.connections.Length; i++)
   {
       /*   省略   */
   }
}

実行してみて、階数が保存されればOKです。

スクリーンショット 2020-10-22 23.04.49

おまけ:階段についた時、ダッシュ時のスピードのままになる

すみません、記述漏れです!
筆者の方では直していたのですがすっかり忘れていました。
SequenceManagerクラスのUpdateSequenceメソッドの該当箇所に以下を記述して下さい。

if (actEnemies.Count < 1 && moveEnemies.Count < 1 && pAct == EAct.MoveEnd)
{
   stairsMenuAction.Proc();
   EAct sAct = stairsMenuAction.GetAction();
   if (sAct == EAct.KeyInput) AllOperatedProc(false);
   else
   {
       ActorAction.runSpeedRate = 1; // これを追記
       playerAction.StopWalkingAnimation();
       AllEnemyStopWalkingAnimation();
   }
   return;
}

というところで今回はここまでです。
次回は装備品にアクセサリーを追加してみたいと思います。

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