5-8 Show Items Tooltip - IPointerEnterHandler
今天要來製作道具的提示訊息,當滑鼠移動到道具上以後,旁邊會顯示一個小提示框標明道具的使用訊息。雖然從第5章開始的教學文章主要是想將遊戲改成移動平台,但如果有相關功能是用滑鼠操控的,我還是會跟大家分享。後面的文章會再將Tooltip改成彈框的形式。
首先,我們要來製作ToolTip的介面,在Canvas上新增UI/Text。
接著在剛剛新增的Text底下新增UI/Image。
將Image設定一個背景圖,請大家自己Google一下ToolTip的背景圖唷。然後先將該Image的Alpha值設低一些,以免看不清楚文字。
接著,在Text上新增Content Size Fitter組件,我們可以使用此組件讓Text的大小依照文字內容自動縮放,不過還需要進行一個設定。
請將Horizontal Fit跟Vertical Fit都設定為Preferred Size,如果對Content Size Fitter不熟悉,請大家先看看以下文章,對Auto Layout佈局的教學非常深刻。
Unity UGUI 原理篇(五):Auto Layout 自動佈局http://k79k06k02k.com/blog/542/unity/unity-ugui-%E5%8E%9F%E7%90%86%E7%AF%87%E4%BA%94%EF%BC%9Aauto-layout-%E8%87%AA%E5%8B%95%E4%BD%88%E5%B1%80
接著將Image的Anchor Presets設定為stretch stretch,這樣當Text縮放時,Image的底圖也會跟著縮放。
如下圖,修改Text的文字內容後,Text本身的大小會縮放,Image的底圖也會跟著縮放。
修改文字後,Image又改變了。
接著點選Text,選擇Duplicate複製一份。
複製的新的Text放在原本的Text底下,並將新的Text底下的Image刪除。
最後階層會如下圖,ToolTip是Text,ToolTip底下有Image跟Content,Content也是Text。我知道階層設計看起來滿奇怪的,ToolTip有用來自動縮放Image底圖的作用,Content文字則是真正可以蓋過Image底圖的文字。
完成後的預覽如下圖,可以看到Content文字會蓋過Image底圖,但是為了自動縮放,必須要在最上層也用相同的Text。如果有更好的做法,希望大家可以提供給我。
接著,在ToolTip上新增一個Canvas Group,這是為了使用Alpha屬性控制ToolTip的顯示與消失。
然後修改InventorySystem.cs,在InvetorySystem中提供方法讓ToolTip顯示跟隱藏,當Slot偵測到滑鼠事件時,告訴InventorySystem讓ToolTip顯示,相當於Mediator Pattern的概念。如果不了解Mediator Pattern,大家可以先看這篇文章了解一下:
C#设计模式(18)——中介者模式(Mediator Pattern)
http://blog.jobbole.com/78124/
InventorySystem.cs:
接著,我們要使用IPointerEnterHandler以及IPointerExitHandler,來判斷滑鼠的進入進出。
Slot.cs:
最後,我們來執行遊戲試看看吧!滑鼠移動到血量瓶,會跳出提示框顯示訊息了!
移動到能量瓶,也會顯示能量瓶的資訊。
首先,我們要來製作ToolTip的介面,在Canvas上新增UI/Text。
接著在剛剛新增的Text底下新增UI/Image。
將Image設定一個背景圖,請大家自己Google一下ToolTip的背景圖唷。然後先將該Image的Alpha值設低一些,以免看不清楚文字。
接著,在Text上新增Content Size Fitter組件,我們可以使用此組件讓Text的大小依照文字內容自動縮放,不過還需要進行一個設定。
請將Horizontal Fit跟Vertical Fit都設定為Preferred Size,如果對Content Size Fitter不熟悉,請大家先看看以下文章,對Auto Layout佈局的教學非常深刻。
Unity UGUI 原理篇(五):Auto Layout 自動佈局http://k79k06k02k.com/blog/542/unity/unity-ugui-%E5%8E%9F%E7%90%86%E7%AF%87%E4%BA%94%EF%BC%9Aauto-layout-%E8%87%AA%E5%8B%95%E4%BD%88%E5%B1%80
接著將Image的Anchor Presets設定為stretch stretch,這樣當Text縮放時,Image的底圖也會跟著縮放。
如下圖,修改Text的文字內容後,Text本身的大小會縮放,Image的底圖也會跟著縮放。
修改文字後,Image又改變了。
接著點選Text,選擇Duplicate複製一份。
複製的新的Text放在原本的Text底下,並將新的Text底下的Image刪除。
最後階層會如下圖,ToolTip是Text,ToolTip底下有Image跟Content,Content也是Text。我知道階層設計看起來滿奇怪的,ToolTip有用來自動縮放Image底圖的作用,Content文字則是真正可以蓋過Image底圖的文字。
完成後的預覽如下圖,可以看到Content文字會蓋過Image底圖,但是為了自動縮放,必須要在最上層也用相同的Text。如果有更好的做法,希望大家可以提供給我。
接著,在ToolTip上新增一個Canvas Group,這是為了使用Alpha屬性控制ToolTip的顯示與消失。
接著在Project視窗內新增一個Script名為ToolTip.cs。
撰寫ToolTip.cs程式碼:
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class ToolTip : MonoBehaviour { [SerializeField] float smoothAlphaMultiplier = 5f; [SerializeField] Vector2 tooltipOffset = new Vector2(100, -100); Text toolTipText; Text contentText; CanvasGroup canvasGroup; float tooltipAlpha = 0; bool isTooltipShow = true; void Start(){ toolTipText = GetComponent(); contentText = GameObject.Find("Content").GetComponent (); canvasGroup = GetComponent (); } void Update(){ if (canvasGroup.alpha != tooltipAlpha) { // 用Lerp函數控制Alpha的漸變 canvasGroup.alpha = Mathf.Lerp (canvasGroup.alpha, tooltipAlpha, Time.deltaTime * smoothAlphaMultiplier); if (Mathf.Abs (canvasGroup.alpha - tooltipAlpha) < 0.01f) { canvasGroup.alpha = tooltipAlpha; } } if (isTooltipShow) { // 將滑鼠座標轉換成Canvas上的座標 Vector2 position; Canvas canvas = GetComponentInParent
然後修改InventorySystem.cs,在InvetorySystem中提供方法讓ToolTip顯示跟隱藏,當Slot偵測到滑鼠事件時,告訴InventorySystem讓ToolTip顯示,相當於Mediator Pattern的概念。如果不了解Mediator Pattern,大家可以先看這篇文章了解一下:
C#设计模式(18)——中介者模式(Mediator Pattern)
http://blog.jobbole.com/78124/
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); } 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.ItemEntityList) { 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資料 foreach (Item entity in itemList.ItemEntityList) { // TODO 讀取Json有Bug } } } }
接著,我們要使用IPointerEnterHandler以及IPointerExitHandler,來判斷滑鼠的進入進出。
Slot.cs:
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.EventSystems; namespace RPG.Inventory{ public class Slot : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler { [SerializeField] GameObject itemPrefab; public void StoreItem(Item item){ // 若Slot底下沒有Item,則Instantiate一個 if (transform.childCount == 0) { GameObject itemObject = Instantiate (itemPrefab, transform); itemObject.GetComponentInChildren().SetItem (item); } else { // 已存在該Item,所以增加數量即可 transform.GetComponentInChildren ().AddAmount (); } } public int GetItemID(){ return transform.GetComponentInChildren ().Item.ID; } public bool IsFilled(){ ItemUI itemUI = transform.GetComponentInChildren (); return itemUI.Amount >= itemUI.Item.Capacity; } public void OnPointerEnter(PointerEventData eventData){ if (transform.childCount > 0) { Item item = GetComponentInChildren ().Item; GetComponentInParent ().ShowToolTip (item.GetToolTipText ()); } } public void OnPointerExit(PointerEventData eventData){ GetComponentInParent ().HideToolTip (); } } }
最後,我們來執行遊戲試看看吧!滑鼠移動到血量瓶,會跳出提示框顯示訊息了!
移動到能量瓶,也會顯示能量瓶的資訊。
留言
張貼留言