Related Posts Plugin for WordPress, Blogger...

5-20 Change Weapon Config When Equipping Weapon

前面我們花了大量的篇幅製作了背包系統,現在就要來將背包系統跟我們遊戲中的武器系統進行整合,首先,我的背包系統換了一個新的介面,所以不要覺得奇怪為什麼變漂亮了XDD我是從這邊買的,如果大家也喜歡的話歡迎購買唷~我沒有在廣告,這介面也不是我做的。
https://assetstore.unity.com/packages/2d/gui/arrow-mobile-ui-for-ugui-64369


這次的修改沒有很複雜,主要在InventorySystem.cs的WearingEquipment方法中,呼叫WeaponSystem用來更換武器的方法。所以,重點在於要將JSON資訊跟Weapon綁定在一起。如果不懂我是怎麼在WeaponSystem更換武器的話,可以先看看以前的文章:
4-3 Weapon Pickup Points
https://rpgcorecombat.blogspot.tw/2018/01/4-3-weapon-pickup-points.html

首先,我先將本次要測試更換的Simple Bow跟Orc Sword放到Resources資料夾。

然後修改JSON格式,多新增一個欄位EquipmentAssetsPath。

以下提供我目前完整的JSON格式:

{"ConsumableEntityList":[
    {
        "ID":1,
        "Name":"血量瓶",
        "ItemTypeString":"Consumable",
        "ItemQualityString":"Common",
        "Description":"+ 25 HP",
        "Capacity":10,
        "BuyPrice":10,
        "SellPrice":10,
        "Sprite":"Sprites/Items/hp",
        "HP":10,
        "MP":0
    },
    {
        "ID":2,
        "Name":"能量瓶",
        "ItemTypeString":"Consumable",
        "ItemQualityString":"Common",
        "Description":"+ 50 MP",
        "Capacity":10,
        "BuyPrice":10,
        "SellPrice":10,
        "Sprite":"Sprites/Items/mp",
        "HP":0,
        "MP":10
    }
],
"EquipmentEntityList":[
    {
        "ID":3,
        "Name":"胸甲",
        "ItemTypeString":"Equipment",
        "ItemQualityString":"Rare",
        "Description":"+ 50 DEF",
        "Capacity":1,
        "BuyPrice":20,
        "SellPrice":10,
        "Sprite":"Sprites/Items/armor",
        "Strength":10,
        "Intellect":0,
        "Agility":10,
        "Stamina":50,
        "EquipmentTypeString":"Armor",
  "EquipmentAssetsPath":""
    },
    {
        "ID":4,
        "Name":"皮腰帶",
        "ItemTypeString":"Equipment",
        "ItemQualityString":"Epic",
        "Description":"+ 20 AGI +50 STA",
        "Capacity":1,
        "BuyPrice":20,
        "SellPrice":10,
        "Sprite":"Sprites/Items/belts",
        "Strength":0,
        "Intellect":0,
        "Agility":20,
        "Stamina":50,
        "EquipmentTypeString":"Belt",
  "EquipmentAssetsPath":""
    },
    {
        "ID":5,
        "Name":"靴子",
        "ItemTypeString":"Equipment",
        "ItemQualityString":"Legendary",
        "Description":"+ 50 AGI",
        "Capacity":1,
        "BuyPrice":20,
        "SellPrice":10,
        "Sprite":"Sprites/Items/boots",
        "Strength":0,
        "Intellect":0,
        "Agility":50,
        "Stamina":0,
        "EquipmentTypeString":"Boots",
  "EquipmentAssetsPath":""
    },
    {
        "ID":6,
        "Name":"護腕",
        "ItemTypeString":"Equipment",
        "ItemQualityString":"Rare",
        "Description":"+ 50 STA",
        "Capacity":1,
        "BuyPrice":20,
        "SellPrice":10,
        "Sprite":"Sprites/Items/bracers",
        "Strength":0,
        "Intellect":0,
        "Agility":0,
        "Stamina":50,
        "EquipmentTypeString":"Bracer",
  "EquipmentAssetsPath":""
    },
    {
        "ID":7,
        "Name":"手套",
        "ItemTypeString":"Equipment",
        "ItemQualityString":"Common",
        "Description":"+20 STR + 50 STA",
        "Capacity":1,
        "BuyPrice":20,
        "SellPrice":10,
        "Sprite":"Sprites/Items/gloves",
        "Strength":20,
        "Intellect":0,
        "Agility":0,
        "Stamina":50,
        "EquipmentTypeString":"OffHand",
  "EquipmentAssetsPath":""
    },
    {
        "ID":8,
        "Name":"頭盔",
        "ItemTypeString":"Equipment",
        "ItemQualityString":"Artifact",
        "Description":"+20 STR + 100 STA",
        "Capacity":1,
        "BuyPrice":20,
        "SellPrice":10,
        "Sprite":"Sprites/Items/helmets",
        "Strength":20,
        "Intellect":0,
        "Agility":0,
        "Stamina":100,
        "EquipmentTypeString":"Head",
  "EquipmentAssetsPath":""
    },
    {
        "ID":9,
        "Name":"項鍊",
        "ItemTypeString":"Equipment",
        "ItemQualityString":"Rare",
        "Description":"+20 STR + 50 STA",
        "Capacity":1,
        "BuyPrice":20,
        "SellPrice":10,
        "Sprite":"Sprites/Items/necklace",
        "Strength":20,
        "Intellect":0,
        "Agility":0,
        "Stamina":50,
        "EquipmentTypeString":"Neck",
  "EquipmentAssetsPath":""
    },
    {
        "ID":10,
        "Name":"戒指",
        "ItemTypeString":"Equipment",
        "ItemQualityString":"Common",
        "Description":"+30 INT",
        "Capacity":1,
        "BuyPrice":20,
        "SellPrice":10,
        "Sprite":"Sprites/Items/rings",
        "Strength":0,
        "Intellect":30,
        "Agility":0,
        "Stamina":0,
        "EquipmentTypeString":"Ring",
  "EquipmentAssetsPath":""
    },
    {
        "ID":11,
        "Name":"褲子",
        "ItemTypeString":"Equipment",
        "ItemQualityString":"Uncommon",
        "Description":"+50 STA",
        "Capacity":1,
        "BuyPrice":20,
        "SellPrice":10,
        "Sprite":"Sprites/Items/pants",
        "Strength":0,
        "Intellect":0,
        "Agility":0,
        "Stamina":50,
        "EquipmentTypeString":"Leg",
  "EquipmentAssetsPath":""
    },
    {
        "ID":12,
        "Name":"護肩",
        "ItemTypeString":"Equipment",
        "ItemQualityString":"Legendary",
        "Description":"+50 STA",
        "Capacity":1,
        "BuyPrice":20,
        "SellPrice":10,
        "Sprite":"Sprites/Items/shoulders",
        "Strength":0,
        "Intellect":0,
        "Agility":0,
        "Stamina":50,
        "EquipmentTypeString":"Shoulder",
  "EquipmentAssetsPath":""
    },
    {
        "ID":13,
        "Name":"斧子",
        "ItemTypeString":"Equipment",
        "ItemQualityString":"Rare",
        "Description":"+50 STR",
        "Capacity":1,
        "BuyPrice":20,
        "SellPrice":10,
        "Sprite":"Sprites/Items/axe",
        "Damage":50,
        "EquipmentTypeString":"OffHand",
  "EquipmentAssetsPath":""
    },
    {
        "ID":14,
        "Name":"劍",
        "ItemTypeString":"Equipment",
        "ItemQualityString":"Rare",
        "Description":"+60 STR",
        "Capacity":1,
        "BuyPrice":50,
        "SellPrice":50,
        "Sprite":"Sprites/Items/sword",
        "Damage":60,
        "EquipmentTypeString":"MainHand",
  "EquipmentAssetsPath":"WeaponConfig/Orc Sword"
    },
 {
        "ID":15,
        "Name":"弓",
        "ItemTypeString":"Equipment",
        "ItemQualityString":"Rare",
        "Description":"+60 STR",
        "Capacity":1,
        "BuyPrice":50,
        "SellPrice":50,
        "Sprite":"Sprites/Items/bow",
        "Damage":60,
        "EquipmentTypeString":"MainHand",
  "EquipmentAssetsPath":"WeaponConfig/Simple Bow"
    }
],
"MaterialEntityList":[
    {
        "ID":315,
        "Name":"鍛造秘笈",
        "ItemTypeString":"Material",
        "ItemQualityString":"Artifact",
        "Description":"合成一把斧子的鍛造秘笈",
        "Capacity":10,
        "BuyPrice":100,
        "SellPrice":10,
        "Sprite":"Sprites/Items/book"
    },
    {
        "ID":316,
        "Name":"鍛造秘笈",
        "ItemTypeString":"Material",
        "ItemQualityString":"Artifact",
        "Description":"合成一個頭盔的鍛造秘笈",
        "Capacity":10,
        "BuyPrice":100,
        "SellPrice":10,
        "Sprite":"Sprites/Items/scroll"
    },
    {
        "ID":317,
        "Name":"鐵塊",
        "ItemTypeString":"Material",
        "ItemQualityString":"Rare",
        "Description":"合成裝備使用的鐵塊",
        "Capacity":100,
        "BuyPrice":20,
        "SellPrice":10,
        "Sprite":"Sprites/Items/ingots"
    }
]}

然後記得在Equipment.cs中也要跟著多增加一個變數:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Text;

namespace RPG.Inventory{
 [Serializable]
 public class Equipment : Item {
  public int Strength;
  public int Intellect;
  public int Agility;
  public int Stamina;
  public string EquipmentTypeString;
  public string EquipmentAssetsPath;
  [NonSerialized] public EquipmentType TheEquipmentType;

  public override void OnAfterDeserialize(){
   TheItemType = (ItemType)System.Enum.Parse (typeof(ItemType), ItemTypeString);
   TheItemQuality = (ItemQuality)System.Enum.Parse (typeof(ItemQuality), ItemQualityString);
   TheEquipmentType = (EquipmentType)System.Enum.Parse (typeof(EquipmentType), EquipmentTypeString);
  }

  public override string GetToolTipText (){
   string text = base.GetToolTipText ();
   StringBuilder newText = new StringBuilder (text);
   string equipmentTypeText = String.Empty;
   switch (TheEquipmentType) {
   case EquipmentType.Armor:
    equipmentTypeText = "胸部";
    break;
   case EquipmentType.Belt:
    equipmentTypeText = "腰帶";
    break;
   case EquipmentType.Boots:
    equipmentTypeText = "靴子";
    break;
   case EquipmentType.Bracer:
    equipmentTypeText = "護腕";
    break;
   case EquipmentType.Head:
    equipmentTypeText = "頭部";
    break;
   case EquipmentType.Leg:
    equipmentTypeText = "腿部";
    break;
   case EquipmentType.Neck:
    equipmentTypeText = "脖子";
    break;
   case EquipmentType.OffHand:
    equipmentTypeText = "副手";
    break;
   case EquipmentType.Ring:
    equipmentTypeText = "戒指";
    break;
   case EquipmentType.Shoulder:
    equipmentTypeText = "護肩";
    break;
   }

   newText.AppendFormat ("\n\n裝備類型:{0}\n",equipmentTypeText);
   newText.AppendFormat ("力量:{0}\n", Strength);
   newText.AppendFormat ("智力:{0}\n", Intellect);
   newText.AppendFormat ("敏捷:{0}\n", Agility);
   newText.AppendFormat ("體力:{0}\n", Stamina);

   return newText.ToString();
  }
 }

 public enum EquipmentType{
  Head,
  Neck,
  Armor,
  Ring,
  Leg,
  Bracer,
  Boots,
  Shoulder,
  Belt,
  OffHand,
  MainHand
 }
}

最後是換裝的程式碼,我用Resources.Load方法讀取放置在Resources資料夾內的武器設定(Weapon.cs),然後使用WeaponSystem.PutWeaponInHand方法更換武器。

這樣就完成啦!以下提供完整的InventorySystem.cs程式碼:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System.Text;
using RPG.Character;
using RPG.Weapons;

namespace RPG.Inventory{
 public class InventorySystem : MonoBehaviour {

  [SerializeField] ToolTip toolTip;
  [SerializeField] ItemUI pickedItem;
  [SerializeField] CharacterProperty characterProperty;

  public bool isPickedItem = false;
  ItemList itemList;
  ForgeFormulaList forgeFormulaList;
  Slot[] slotList;

  int coinAmount = 1000;
  Text coinText;

  void Start () {
   ParseItemsJson ();
   ParseForgeFormulaJson ();
   slotList = GetComponentsInChildren ();
   coinText = GameObject.Find ("Coin").GetComponentInChildren();
   coinText.text = coinAmount.ToString();
   InitialVendorItem ();
  }

  void Update(){
   PressGToAddItem ();
   MovePickedItemByMousePosition ();
   DiscardPickedItem ();
  }

  public void SaveInventory(){
   StringBuilder saveData = new StringBuilder ();
   foreach (Slot slot in slotList) {
    if (slot is VendorSlot) {
     // 如果是商店類型的Slot則不需要儲存狀態,所以儲存為沒有物品的0
     saveData.Append ("0-");
    } else if (slot.transform.childCount > 0) {
     // Slot內有物品,儲存ID跟數量
     ItemUI itemUI = slot.GetComponentInChildren ();
     saveData.Append (itemUI.Item.ID + "," + itemUI.Amount + "-");
    } else {
     // Slot內沒有物品,儲存0
     saveData.Append ("0-");
    }
   }
   PlayerPrefs.SetString (this.gameObject.name, saveData.ToString());
   PlayerPrefs.SetInt ("CoinAmount", coinAmount);
  }

  public void LoadInventory(){
   if (PlayerPrefs.HasKey (this.gameObject.name)) {
    string savaData = PlayerPrefs.GetString (this.gameObject.name);
    string[] itemArray = savaData.Split ('-');
    // Length - 1是因為最後一個沒有資料
    for (int i = 0; i < itemArray.Length - 1; i++) {
     if (itemArray [i] != "0") {
      string[] itemInfo = itemArray [i].Split (',');
      // 取得物品的ID跟數量
      int id = int.Parse (itemInfo[0]);
      int amount = int.Parse (itemInfo[1]);
      Item item = GetItemByID (id);
      // 將物品生成在指定的Slot
      for (int j = 0; j < amount; j++) {
       slotList [i].StoreItem (item);
      }
     }
    }
   }
   if (PlayerPrefs.HasKey ("CoinAmount")) {
    coinAmount = PlayerPrefs.GetInt ("CoinAmount");
    coinText.text = coinAmount.ToString ();
   }
  }

  private bool ConsumeCoin(int amount){
   if (coinAmount > 0) {
    coinAmount -= amount;
    coinText.text = coinAmount.ToString ();
    return true;
   }
   return false;
  }

  private void EarnCoin(int amount){
   coinAmount += amount;
   coinText.text = coinAmount.ToString ();
  }

  public void BuyItem(Item item){
   bool isSuccess = ConsumeCoin (item.BuyPrice);
   if (isSuccess) {
    StoreItem (item);
   }
  }

  public void SellItem(int sellAmount){
   int sellPrice = pickedItem.Item.SellPrice * sellAmount;
   ReducePickedItem (sellAmount);
   EarnCoin (sellPrice);
  }

  public void PickupItem(Item item, int amount){
   pickedItem.SetItem (item, amount);
   pickedItem.Show ();
   toolTip.Hide ();
   isPickedItem = true;
  }

  public void ReducePickedItem(int amount){
   pickedItem.ReduceAmount(amount);
   if (pickedItem.Amount <= 0) {
    isPickedItem = false;
    pickedItem.Hide ();
   }
  }

  public void WearingEquipment(ItemUI equipmentToWear){
   if ((equipmentToWear.Item is Equipment) == false) {
    return;
   }

   foreach(Slot slot in slotList){
    EquipmentSlot equipmentSlot = (slot as EquipmentSlot);
    if(equipmentSlot && equipmentSlot.EquipmentTypeIsEqual (equipmentToWear.Item)){

     // 換裝備的時候更換Player的武器
     Weapon weaponConfig = Resources.Load
      ((equipmentToWear.Item as Equipment).EquipmentAssetsPath);
     if (weaponConfig != null) {
      FindObjectOfType ()
       .GetComponent ().PutWeaponInHand (weaponConfig);
     }

     if (equipmentSlot.transform.childCount > 0) {
      // 人物已配戴裝備了,將裝備與背包的裝備交換
      equipmentSlot.GetComponentInChildren ().ExchangeItem(equipmentToWear);
     } else {
      if (isPickedItem == true) {
       // 若使用Drag-Drop的方式穿上裝備,要記得再將pickedItem設定為false
       isPickedItem = false;
       pickedItem.Hide ();
      }
      // 人物未配戴裝備,直接穿上
      equipmentSlot.StoreItem (equipmentToWear.Item);
     }
     break;
    }
   }
   UpdatePropertyText ();
  }

  public ItemUI GetPickedItem(){
   return pickedItem;
  }

  public Slot[] GetSlotList(){
   return slotList;
  }

  public void ShowToolTip(string content){
   toolTip.Show (content);
  }

  public void HideToolTip(){
   toolTip.Hide ();
  }

  public void UpdatePropertyText(){
   characterProperty.UpdatePropertyText ();
  }

  public bool StoreItem(int id){
   Item item = GetItemByID (id);
   return StoreItem (item);
  }

  public bool StoreItem(Item item){
   if (item == null) {
    return false;
   }

   // 若物品的儲存容量為1,則將該物品直接放進空的Slot
   if (item.Capacity == 1) {
    return StoreItemInEmptySlot (item);
   }
   // 將相同的Item放在同一個Slot
   return StoreItemInSameSlot(item);
  }

  private bool StoreItemInSameSlot(Item item){
   foreach (Slot slot in slotList) {
    // 避開VendorSlot
    if (slot is VendorSlot) {
     continue;
    }
    if (slot.transform.childCount >= 1 && slot.GetItemID () == item.ID &&
     slot.IsFilled () == false) {
     // 將新Item與同一個Item放在一起
     slot.StoreItem (item);
     return true;
    }
   }
   // 若背包內不存在相同的Item,則放進空的Slot
   return StoreItemInEmptySlot (item);
  }

  private bool StoreItemInEmptySlot(Item item){
   foreach (Slot slot in slotList) {
    // 避開VendorSlot
    if (slot is VendorSlot) {
     continue;
    }
    if (slot.transform.childCount == 0) {
     // 將Item存進該Slot
     slot.StoreItem (item);
     return true;
    }
   }
   Debug.LogError ("No Empty Slot.");
   return false;
  }

  public void ForgeItem(){
   List materialsIDList = new List ();
   List forgeSlotList = new List ();
   // 取得Forge Slot內的Item
   foreach (Slot slot in slotList) {
    ForgeSlot forgeSlot = (slot as ForgeSlot);

    if (forgeSlot && slot.transform.childCount > 0) {
     ItemUI currentItemUI = slot.transform.GetComponentInChildren ();
     for (int i = 0; i < currentItemUI.Amount; i++) {
      materialsIDList.Add (currentItemUI.Item.ID);
      forgeSlotList.Add (forgeSlot);
     }
    } 
   }
   // 檢查item組合是否匹配任一鍛造公式
   foreach (ForgeFormula formula in forgeFormulaList.ForgeFormulaEntityList) {
    if (formula.MatchFormula (materialsIDList)) {
     // 符合公式,存入鍛造物品到背包內
     StoreItem (formula.ResultID);
     // 消耗材料
     ConsumeMaterials(formula, forgeSlotList);
     break;
    }
   }
  }

  private void ConsumeMaterials(ForgeFormula formula, List forgeSlotList){
   List requireList = formula.GetRequireList();
   foreach (int ID in requireList) {
    foreach (ForgeSlot forgeSlot in forgeSlotList) {
     if (forgeSlot.transform.childCount > 0 && forgeSlot.GetItemID() == ID) {
      ItemUI itemUI = forgeSlot.GetComponentInChildren ();
      itemUI.ReduceAmount (1);
      if (itemUI.Amount <= 0) {
       DestroyImmediate (itemUI.gameObject);
      }
      break;
     }
    }
   }
  }

  private void PressGToAddItem(){
   // 測試程式碼,手動生成物品
   if (Input.GetKeyDown (KeyCode.G)) {
    StoreItem (Random.Range(1,18));
   }
  }

  private void InitialVendorItem(){
   Item[] vendorItemList = new Item[8];
   // 手動初始化要放入商店的物品
   vendorItemList [0] = GetItemByID (1);
   vendorItemList [1] = GetItemByID (2);
   vendorItemList [2] = GetItemByID (3);
   vendorItemList [3] = GetItemByID (4);
   vendorItemList [4] = GetItemByID (5);
   vendorItemList [5] = GetItemByID (6);
   vendorItemList [6] = GetItemByID (7);
   vendorItemList [7] = GetItemByID (8);
   foreach (Item item in vendorItemList) {
    foreach (Slot slot in slotList) {
     // 限定Slot類型為VendorSlot
     VendorSlot vendorSlot = (slot as VendorSlot);
     // 如果同類型物品疊加在一起
     if (vendorSlot && slot.transform.childCount >= 1 
      && slot.GetItemID () == item.ID &&
      slot.IsFilled () == false) {
      slot.StoreItem (item);
      break;
     } // 如果沒有同類物品,則放入空Slot
     else if (vendorSlot && slot.transform.childCount == 0) {
      slot.StoreItem (item);
      break;
     }
    }
   }
  }

  private void MovePickedItemByMousePosition(){
   if (isPickedItem) {
    // 將滑鼠座標轉換成Canvas上的座標
    Vector2 position;
    Canvas canvas = GetComponentInParent ();
    RectTransformUtility.ScreenPointToLocalPointInRectangle (
     canvas.transform as RectTransform,
     Input.mousePosition,
     null,
     out position);
    pickedItem.SetLocalPosition (position);
   }
  }

  private void DiscardPickedItem(){
   // 處理物品丟棄
   // IsPointerOverGameObject(-1)判斷滑鼠左鍵是否有碰到GameObject
   if (pickedItem && Input.GetMouseButtonDown (0) 
    && EventSystem.current.IsPointerOverGameObject(-1) == false) {
    // TODO 跟Slot.cs的PickupAllSlotItem相衝突,若用DestroyImmediate會導致此方法被呼叫
    isPickedItem = false;
    pickedItem.Hide ();
   }
  }

  private Item GetItemByID(int id){
   foreach (Item entity in itemList.ConsumableEntityList) {
    if (entity.ID == id) {
     return entity;
    } 
   }
   foreach (Item entity in itemList.EquipmentEntityList) {
    if (entity.ID == id) {
     return entity;
    } 
   }
   foreach (Item entity in itemList.MaterialEntityList) {
    if (entity.ID == id) {
     return entity;
    } 
   }
   return null;
  }

  private void ParseItemsJson(){
   // 從Resource資料夾中讀取Items.json
   TextAsset json = Resources.Load ("Items");
   // 解析Json格式
   itemList = JsonUtility.FromJson (json.text);
  }

  private void ParseForgeFormulaJson(){
   TextAsset json = Resources.Load ("ForgeFormula");
   forgeFormulaList = JsonUtility.FromJson (json.text);
  }
 }
}

然後來執行遊戲測試看看吧,裝備劍的時候,就會把Orc Sword裝備上去。

裝備弓的時候,Simple Bow就會裝備上去。



留言