1、思路:
(1)信息數據:需要展示屬性信息
(2)信息的展示:負責顯示UI屬性信息
(3)UI的跟隨:負責實現UI對人物的跟隨
(4)UI的管理:負責對UI進行創建於回收,游戲中需要用到UI的地方都是與該腳本的交互。
2、將需要用到的UI資源打到一個圖集中,以免出現大量人物的時候圖集穿插造成不良的影響。
3、創建一個頭頂UI預設,當道對應的目錄下面:
為了測試的方便,在Resources下面也放一個做好的UI預設:
需要注意的是,做的這個預設是不帶Panel的,因為希望可以把游戲展示的所有人物頭頂UI信息放到同一個Panel下管理。
4、創建一個Panel,用來管理頭頂的UI信息:
5、UI跟隨腳本:
using UnityEngine; /// <summary> /// 2DUI跟隨3D物體 /// </summary> public class UIFollow3DPosition : MonoBehaviour { public Transform targetTrans; //2DUI需要跟隨的3D目標 public Camera targetCa; //照射的3D目標的攝像機 public Vector3 offsetPos; //2DUI需要在3D目標點的基礎上做出的偏移量 public float dis; //3D目標點與攝像機之間的距離,為了實現:3D目標點攝像機機發生變化的時候2DUI也跟隨發生變化 public float ratio; //2DUI的縮放比例 [HideInInspector] public bool isAutoScale = false; //控制2DUI是否發生縮放 public const float FarDistance = 40f; //3D目標點與攝像機的最大距離 public const float NearDistance = 6.5f; //3D目標點與攝像機的最小距離 public static Vector3 MaxScale = Vector3.one; public static Vector3 MinScale = new Vector3(0.5f, 0.5f, 0.5f); private Camera uiCa; //NGUI攝像機 Transform selfTrans; //2DUI,因為該腳本是掛在2DUI上的 GameObject selfObj; //2DUI Vector3 screenPos; //2DUI的屏幕坐標 Vector3 curPos; //2DUI的世界坐標 Vector3 backPos = new Vector3(3000, 0, 0); public Transform cachedTransform { get { if (trans == null) trans = transform; return trans; } } private Transform trans; void Awake() { selfTrans = transform; selfObj = gameObject; uiCa = UICamera.first.cachedCamera; } public void QuickUpdate() { LateUpdate(); } void LateUpdate() { if (targetTrans != null && targetCa != null) { screenPos = targetCa.WorldToScreenPoint(targetTrans.position + offsetPos); if(screenPos.z < 0) { selfTrans.localPosition = backPos; } else { curPos = uiCa.ScreenToWorldPoint(screenPos); curPos.z = 0; selfTrans.position = curPos; } if(isAutoScale) { dis = Vector3.Distance(targetTrans.position, targetCa.transform.position); ratio = Mathf.Clamp01((dis - NearDistance) / (FarDistance - NearDistance)); cachedTransform.localScale = Vector3.Lerp(MaxScale, MinScale, ratio); } } } }
6、數據及展示的實現:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using UnityEngine; namespace Module.UI { //頭頂顯示需要用到的是數據 public class CharacterTopParam { public int level; public string name; public CharacterTopParam(int level, string name) { this.level = level; this.name = name; } } public class UI_Character_TopUI : MonoBehaviour { [SerializeField] UILabel lblInfo; GameObject obj = null; public GameObject cachedGameObjec { get { if (null == obj) obj = gameObject; return obj; } } UIFollow3DPosition followPos = null; public UIFollow3DPosition FollowPos { get { if (null == followPos) { followPos = cachedGameObjec.GetComponent<UIFollow3DPosition>(); followPos.isAutoScale = true; } return followPos; } } public void Show(CharacterTopParam param) { if (null != param) { lblInfo.text = string.Format("LV.{0} Name.{1}", param.level, param.name); cachedGameObjec.SetActive(true); } } public void Hide() { cachedGameObjec.SetActive(false); } } }
7、將以上兩個腳本掛載到做好的UI預設上,並進行相應的設置:
8、UI管理腳本的實現:
using System.Collections; using System.Collections.Generic; using UnityEngine; namespace Module.UI { public class BillboardUIManager { static BillboardUIManager billboardMgr = null; public static BillboardUIManager BillboardMgr { get { if (null == billboardMgr) billboardMgr = new BillboardUIManager(); return billboardMgr; } } Transform childParent; public BillboardUIManager() { GameObject go = GameObject.Find("UI Root/BillboardPanel"); childParent = NGUITools.AddChild(go).transform; } public void Destroy() { GameObject.Destroy(childParent.gameObject); } public void SetParentRootActive(bool active) { childParent.gameObject.SetActive(active); } Queue<UI_Character_TopUI> queueCharacterTopUI = new Queue<UI_Character_TopUI>(); public UI_Character_TopUI GetCharacterTopUI(Transform target, Camera cam) { UI_Character_TopUI topUI = null; if (queueCharacterTopUI.Count <= 0) { Object go = Resources.Load("ui_character_top"); if (go) { GameObject prefab = GameObject.Instantiate(go) as GameObject; prefab.transform.parent = childParent; prefab.transform.localPosition = Vector3.zero; prefab.transform.localRotation = Quaternion.identity; prefab.transform.localScale = Vector3.one; NGUITools.SetLayer(prefab, childParent.gameObject.layer); topUI = prefab.GetComponent<UI_Character_TopUI>(); } } else { topUI = queueCharacterTopUI.Dequeue(); } topUI.FollowPos.targetTrans = target; topUI.FollowPos.targetCa = cam; topUI.FollowPos.offsetPos = new Vector3(0, 1, 0); return topUI; } void RecycleCharacterUI(UI_Character_TopUI topUI) { if (null != topUI) { topUI.Hide(); queueCharacterTopUI.Enqueue(topUI); } } } }
9,以上就是全部的核心邏輯,下面是調用邏輯:
using System.Collections; using System.Collections.Generic; using UnityEngine; using Module.UI; public class CubeCtrl : MonoBehaviour { [SerializeField] Camera targetCam; float speed = 20.0f; Transform trans = null; public Transform Trans { get { if (null == trans) trans = transform; return trans; } } //Show UI Info UI_Character_TopUI characterTopUI = null; public UI_Character_TopUI CharacterTopUI { get { if (null == characterTopUI) characterTopUI = BillboardUIManager.BillboardMgr.GetCharacterTopUI(Trans, targetCam); return characterTopUI; } } void Start() { Trans.transform.rotation = Quaternion.identity; CharacterTopParam param = new CharacterTopParam(12, "我是方的"); CharacterTopUI.Show(param); } void Update() { if (Input.GetKey(KeyCode.A)) { Trans.Translate(-Time.deltaTime * speed, 0, 0); } else if (Input.GetKey(KeyCode.D)) { Trans.Translate(Time.deltaTime * speed, 0, 0); } else if (Input.GetKey(KeyCode.W)) { Trans.Translate(0, 0, Time.deltaTime * speed); } else if (Input.GetKey(KeyCode.S)) { Trans.Translate(0, 0, -Time.deltaTime * speed); } } }
10、項目運行前后的效果對比圖:
11、項目運行時候的結構: