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程式碼:
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 | 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<text> (); contentText = GameObject.Find( "Content" ).GetComponent<text> (); canvasGroup = GetComponent<canvasgroup> (); } 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<canvas> (); RectTransformUtility.ScreenPointToLocalPointInRectangle ( canvas.transform as RectTransform, Input.mousePosition, null , out position); SetLocalPosition (position + tooltipOffset); } } public void Show( string text){ toolTipText.text = text; contentText.text = text; tooltipAlpha = 1; isTooltipShow = true ; } public void Hide(){ tooltipAlpha = 0; isTooltipShow = false ; } private void SetLocalPosition(Vector3 localPosition){ transform.localPosition = localPosition; } } </canvas></canvasgroup></text></text> |
然後修改InventorySystem.cs,在InvetorySystem中提供方法讓ToolTip顯示跟隱藏,當Slot偵測到滑鼠事件時,告訴InventorySystem讓ToolTip顯示,相當於Mediator Pattern的概念。如果不了解Mediator Pattern,大家可以先看這篇文章了解一下:
C#设计模式(18)——中介者模式(Mediator Pattern)
http://blog.jobbole.com/78124/
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 | 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); } 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<textasset> ( "Items" ); // 解析Json格式 itemList = JsonUtility.FromJson <itemlist>(json.text); // 使用foreach迴圈讀取List中的Item資料 foreach (Item entity in itemList.ItemEntityList) { // TODO 讀取Json有Bug } } } } </itemlist></textasset></slot> |
接著,我們要使用IPointerEnterHandler以及IPointerExitHandler,來判斷滑鼠的進入進出。
Slot.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 | 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<itemui> ().SetItem (item); } else { // 已存在該Item,所以增加數量即可 transform.GetComponentInChildren<itemui> ().AddAmount (); } } public int GetItemID(){ return transform.GetComponentInChildren<itemui> ().Item.ID; } public bool IsFilled(){ ItemUI itemUI = transform.GetComponentInChildren<itemui> (); return itemUI.Amount >= itemUI.Item.Capacity; } public void OnPointerEnter(PointerEventData eventData){ if (transform.childCount > 0) { Item item = GetComponentInChildren<itemui> ().Item; GetComponentInParent<inventorysystem> ().ShowToolTip (item.GetToolTipText ()); } } public void OnPointerExit(PointerEventData eventData){ GetComponentInParent<inventorysystem> ().HideToolTip (); } } } </inventorysystem></inventorysystem></itemui></itemui></itemui></itemui></itemui> |
最後,我們來執行遊戲試看看吧!滑鼠移動到血量瓶,會跳出提示框顯示訊息了!
移動到能量瓶,也會顯示能量瓶的資訊。
留言
張貼留言