Related Posts Plugin for WordPress, Blogger...

5-9 Complete Items Tooltip

今天要來完善Tooltip的功能,之前的章節中我們僅完成滑鼠移動到物品上,會顯示該物品的名稱,現在則是要加入其他詳細資訊。首先,先提供新的JSON格式:

Items.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"
    },
    {
        "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"
    },
    {
        "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"
    },
    {
        "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"
    },
    {
        "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"
    },
    {
        "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"
    },
    {
        "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"
    },
    {
        "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"
    },
    {
        "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"
    },
    {
        "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"
    },
    {
        "ID":13,
        "Name":"斧子",
        "ItemTypeString":"Weapon",
        "ItemQualityString":"Rare",
        "Description":"+50 STR",
        "Capacity":1,
        "BuyPrice":20,
        "SellPrice":10,
        "Sprite":"Sprites/Items/axe",
        "Damage":50,
        "WeaponTypeString":"MainHand"
    },
    {
        "ID":14,
        "Name":"劍",
        "ItemTypeString":"Weapon",
        "ItemQualityString":"Rare",
        "Description":"+60 STR",
        "Capacity":1,
        "BuyPrice":20,
        "SellPrice":10,
        "Sprite":"Sprites/Items/sword",
        "Damage":60,
        "WeaponTypeString":"OffHand"
    }
],
"MaterialEntityList":[
    {
        "ID":15,
        "Name":"鍛造秘笈",
        "ItemTypeString":"Material",
        "ItemQualityString":"Artifact",
        "Description":"合成一把斧子的鍛造秘笈",
        "Capacity":10,
        "BuyPrice":100,
        "SellPrice":10,
        "Sprite":"Sprites/Items/book"
    },
    {
        "ID":16,
        "Name":"鍛造秘笈",
        "ItemTypeString":"Material",
        "ItemQualityString":"Artifact",
        "Description":"合成一個頭盔的鍛造秘笈",
        "Capacity":10,
        "BuyPrice":100,
        "SellPrice":10,
        "Sprite":"Sprites/Items/scroll"
    },
    {
        "ID":17,
        "Name":"鐵塊",
        "ItemTypeString":"Material",
        "ItemQualityString":"Rare",
        "Description":"合成裝備使用的鐵塊",
        "Capacity":100,
        "BuyPrice":20,
        "SellPrice":10,
        "Sprite":"Sprites/Items/ingots"
    }
]}

大家應該會發現我有修改了下JSON的規格,新增了三種不同的List分為ConsumableEntityList、EquipmentEntityList、MaterialEntityList。然後在Item.cs中修改了部分程式碼,解析這三種新的List。

在InventorySystem.cs中的GetItemByID方法也略為麻煩,必須要修改成三個foreach迴圈,用來尋訪上述新增的三個新的List。

另外,我在GetToolTipText方法中,使用<color>標籤來標示不同功能的文字,這是Text一個很方便的功能,大家也可以從Unity官方教學了解一下Text有哪些可使用的標籤。
https://docs.unity3d.com/Manual/StyledText.html

接下來,提供本次修改後的完整程式碼給大家。另外,我在GetToolTipText方法中使用StringBuilder來串接動態字串,效率會比較高,大家可以參考這篇文章:

[C#]字串組合的時間效能大比拚(+=、Str.Format、StringBuilder.Append、SB.AppendFormat)
https://dotblogs.com.tw/doraemon0527/2011/01/23/20965

InventorySystem.cs:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.UI;

namespace RPG.Inventory{
 public class InventorySystem : MonoBehaviour {

  [SerializeField] ToolTip toolTip;

  ItemList itemList;
  Slot[] slotList;

  void Start () {
   ParseItemsJson ();

   slotList = GetComponentsInChildren ();
   // 測試程式碼,手動生成物品
   StoreItem (1);
   StoreItem (2);
   StoreItem (1);
   StoreItem (3);
   StoreItem (4);
   StoreItem (5);
   StoreItem (6);
   StoreItem (7);
   StoreItem (8);
   StoreItem (9);
   StoreItem (10);
   StoreItem (11);
   StoreItem (12);
   StoreItem (7);
   StoreItem (8);
   StoreItem (9);
   StoreItem (13);
   StoreItem (14);
   StoreItem (15);
   StoreItem (16);
   StoreItem (17);
  }

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

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

  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) {
    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) {
    if (slot.transform.childCount == 0) {
     // 將Item存進該Slot
     slot.StoreItem (item);
     return true;
    }
   }
   Debug.LogError ("No Empty Slot.");
   return false;
  }

  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);
   // 使用foreach迴圈讀取List中的Item資料
   // TODO 讀取Json有Bug
  }
 }
}

Item.cs:

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

namespace RPG.Inventory{
 [Serializable]
 public class ItemList{
  public List ConsumableEntityList;
  public List EquipmentEntityList;
  public List MaterialEntityList;
 }

 [Serializable]
 public class Item : ISerializationCallbackReceiver{
  public int ID;
  public string Name;
  public string ItemTypeString;
  public string ItemQualityString;
  // 目前Unity提供的JsonUtility不支援enum型別的轉換,所以此處指定為NonSerialized代表不解析
  [NonSerialized] public ItemType TheItemType;
  [NonSerialized] public ItemQuality TheItemQuality;
  public string Description;
  public int Capacity;
  public int BuyPrice;
  public int SellPrice;
  public string Sprite;

  public void OnAfterDeserialize(){
   // JSON格式解析後,將ItemTypeString,ItemQualityString轉換成enum型別
   TheItemType = (ItemType)System.Enum.Parse (typeof(ItemType), ItemTypeString);
   TheItemQuality = (ItemQuality)System.Enum.Parse (typeof(ItemQuality), ItemQualityString);
  }

  public void OnBeforeSerialize(){}

  public virtual string GetToolTipText(){
   string color = String.Empty;
   // TODO 重構這段冗長的Switch
   switch (TheItemQuality) {
   case ItemQuality.Common:
    color = "white";
    break;
   case ItemQuality.Uncommon:
    color = "lime";
    break;
   case ItemQuality.Rare:
    color = "navy";
    break;
   case ItemQuality.Epic:
    color = "magenta";
    break;
   case ItemQuality.Legendary:
    color = "orange";
    break;
   case ItemQuality.Artifact:
    color = "red";
    break;
   }

   StringBuilder text = new StringBuilder ();
   text.AppendFormat ("{1}\n", color, Name);
   text.AppendFormat ("購買價格:{0}\n", BuyPrice);
   text.AppendFormat ("銷售價格:{0}\n", SellPrice);
   text.Append (Description);
   return text.ToString();
  }
 }

 public enum ItemType{
  Consumable,
  Equipment,
  Weapon,
  Material
 }

 public enum ItemQuality{
  Common,
  Uncommon,
  Rare,
  Epic,
  Legendary,
  Artifact
 }
}

Consumable.cs:

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

namespace RPG.Inventory{
 [Serializable]
 public class Consumable : Item{
  public int HP;
  public int MP;

  public override string GetToolTipText (){
   string text = base.GetToolTipText ();
   StringBuilder newText = new StringBuilder (text);
   newText.AppendFormat ("\n\n加血:{0}\n", HP);
   newText.AppendFormat ("加魔:{0}", MP);

   return newText.ToString();
  }
 }
}

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;
  [NonSerialized] public EquipmentType TheEquipmentType;

  public void OnAfterDeserialize(){
   TheEquipmentType = (EquipmentType)System.Enum.Parse (typeof(EquipmentType), EquipmentTypeString);
  }

  public void OnBeforeSerialize(){}

  public override string GetToolTipText (){
   string text = base.GetToolTipText ();
   StringBuilder newText = new StringBuilder (text);
   // TODO 重構這段冗長的Switch
   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
 }
}

Materials.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

namespace RPG.Inventory{
 [Serializable]
 public class Materials : Item {

 }
}


最後,來執行遊戲看看結果吧!把滑鼠移動到裝備上後會顯示裝備資訊,根據不同的等級的武器名稱的顏色也不同,數字部分則是用黃色來標記。

這是合成材料的標記。

這是消耗品的標記,這樣就完成了完整的Tooltip功能嘍!


留言