Related Posts Plugin for WordPress, Blogger...

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的顯示與消失。

接著在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 ();
   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;
 }
}



然後修改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 ();
  }
 }
}

最後,我們來執行遊戲試看看吧!滑鼠移動到血量瓶,會跳出提示框顯示訊息了!

移動到能量瓶,也會顯示能量瓶的資訊。

留言