Related Posts Plugin for WordPress, Blogger...

5-9 Complete Items Tooltip

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

Items.json:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
{"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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
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<slot> ();
   // 測試程式碼,手動生成物品
   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<textasset> ("Items");
   // 解析Json格式
   itemList = JsonUtility.FromJson <itemlist>(json.text);
   // 使用foreach迴圈讀取List中的Item資料
   // TODO 讀取Json有Bug
  }
 }
}
</itemlist></textasset></slot>

Item.cs:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Text;
 
namespace RPG.Inventory{
 [Serializable]
 public class ItemList{
  public List<consumable> ConsumableEntityList;
  public List<equipment> EquipmentEntityList;
  public List<materials> 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 ("<color>{1}</color>\n", color, Name);
   text.AppendFormat ("購買價格:<color yellow="">{0}</color>\n", BuyPrice);
   text.AppendFormat ("銷售價格:<color yellow="">{0}</color>\n", SellPrice);
   text.Append (Description);
   return text.ToString();
  }
 }
 
 public enum ItemType{
  Consumable,
  Equipment,
  Weapon,
  Material
 }
 
 public enum ItemQuality{
  Common,
  Uncommon,
  Rare,
  Epic,
  Legendary,
  Artifact
 }
}
</materials></equipment></consumable>

Consumable.cs:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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加血:<color yellow="">{0}</color>\n", HP);
   newText.AppendFormat ("加魔:<color yellow="">{0}</color>", MP);
 
   return newText.ToString();
  }
 }
}

Equipment.cs:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
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裝備類型:<color yellow="">{0}</color>\n",equipmentTypeText);
   newText.AppendFormat ("力量:<color yellow="">{0}</color>\n", Strength);
   newText.AppendFormat ("智力:<color yellow="">{0}</color>\n", Intellect);
   newText.AppendFormat ("敏捷:<color yellow="">{0}</color>\n", Agility);
   newText.AppendFormat ("體力:<color yellow="">{0}</color>\n", Stamina);
 
   return newText.ToString();
  }
 }
 
 public enum EquipmentType{
  Head,
  Neck,
  Armor,
  Ring,
  Leg,
  Bracer,
  Boots,
  Shoulder,
  Belt,
  OffHand
 }
}

Materials.cs:
1
2
3
4
5
6
7
8
9
10
11
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
 
namespace RPG.Inventory{
 [Serializable]
 public class Materials : Item {
 
 }
}


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

這是合成材料的標記。

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


留言