注:本實例開發環境為Unity 5.3.4,開發語言為C#
這周的任務是用UGUI實現NGUI的一個效果:
http://www.tasharen.com/ngui/exampleX.html
實現效果:
1. 格子是什么?其實是個按鈕!
項目分級:
頂層:InventoryMenu空對象,下屬InventoryMenuCam - UI攝像機,MenuManager - 管理類,ICanvas - UI畫布
ICanvas下屬:角色3D模型(從商店下載),Window - 控制Equip和Backpack面板
Equip: 下屬三個格子,分別對應頭部,上身和足部三種類別的盔甲
Backpack:下屬九個格子,可以放任意類別的盔甲
Grid:實際上是個按鈕 - OnClick()上掛載UIManager.mouseClickHandler(GameObject),對象上還掛在了一個Grid的MonoBehavior腳本,里面只有一個公共值 - GridNumber
設置Grid Number對應自身的格子編號。
設置OnClick傳遞自身對象為參數。
(對每個格子進行如上操作 - 我覺得會有更好的解決辦法?)
那這時每當按到這個格子的時候就會調用一次UIManager的mouseClickHandler()方法,這是會將自身對象作為一個參數發送給UIManager。
mouseClickHandler()里為不同情況下的點擊處理做了定義:
public void mouseClickHandler(GameObject gridObject) { int gridNumber = gridObject.GetComponent<Grid>().gridNumber;//0 - 2 is the equipment grid number if (gridNumber >= 0 && gridNumber <= 2) { if (im.isEquipmentGridOcupied (gridNumber)) { if (mouseInventType == 0) { int equipmentType = im.getEquipmentInventory (gridNumber); toggleImageFromGridToMouse (gridObject); mouseInventType = equipmentType; } } else { //Grid 0 for head gear, 1 for chest gear, 2 for foot gear if (mouseInventType == 1 && gridNumber == 0) { im.putInventoryonEqupment (gridNumber, mouseInventType); toggleImageFromMouseToGrid (gridObject); mouseInventType = 0; } else if (mouseInventType == 2 && gridNumber == 1) { im.putInventoryonEqupment (gridNumber, mouseInventType); toggleImageFromMouseToGrid (gridObject); mouseInventType = 0; } else if (mouseInventType == 3 && gridNumber == 2) { im.putInventoryonEqupment (gridNumber, mouseInventType); toggleImageFromMouseToGrid (gridObject); mouseInventType = 0; } } } //3 - 11 is the backpack grid number else if (gridNumber >= 3 && gridNumber <= 11) { int backPackGridNumber = gridNumber - 3; Debug.Log (backPackGridNumber + im.isBackpackGridOccupied(backPackGridNumber).ToString() + mouseInventType); if (im.isBackpackGridOccupied (backPackGridNumber)) { if (mouseInventType == 0) { int inventoryType = im.getBackPackInventory (backPackGridNumber); toggleImageFromGridToMouse (gridObject); mouseInventType = inventoryType; } } else { if (mouseInventType != 0) { im.putBackPackInventory (backPackGridNumber, mouseInventType); toggleImageFromMouseToGrid (gridObject); mouseInventType = 0; } } } }
2. 界面邏輯和游戲邏輯的分離:UIManager和InventoryManager
你已經注意到了,界面里面我沒有對Grid細分為EquipmentGrid和BackpackGrid。這是考慮到從界面的角度來看,他們其實是同一種對象。
我另外實現了單實例對象InventoryManager,專門負責物品邏輯的管理:
如果要從外部來啟動這個菜單,應該由UIManager來發起初始化界面,再調用InventoryManager的Init方法初始化邏輯。這里為了簡便在UIManager中把初始化的物品列表設為公共。
一些初期設定:
3. 雜項
界面隨鼠標移動的實現方法,別忘了在人像上也掛載喔!
using UnityEngine; public class TiltWindow : MonoBehaviour { public Vector2 range = new Vector2(5f, 3f); Transform mTrans; Quaternion mStart; Vector2 mRot = Vector2.zero; void Start () { mTrans = transform; mStart = mTrans.localRotation; } void Update () { Vector3 pos = Input.mousePosition; float halfWidth = Screen.width * 0.5f; float halfHeight = Screen.height * 0.5f; float x = Mathf.Clamp((pos.x - halfWidth) / halfWidth, -1f, 1f); float y = Mathf.Clamp((pos.y - halfHeight) / halfHeight, -1f, 1f); mRot = Vector2.Lerp(mRot, new Vector2(x, y), Time.deltaTime * 5f); mTrans.localRotation = mStart * Quaternion.Euler(-mRot.y * range.y, -mRot.x * range.x, 0f); } }
圖像隨鼠標移動的方法:
新建一個對象,ItenOnMouse,里面有個Image組件。讓這個組件無時無刻跟着鼠標走就行了。需要的時候enable,不需要的時候disable。
當然,由於在Canvas里用的是RectTransform,我們也必須用RectTransform,LocalPoint相關的方法來處理:
void Update() { Vector2 mousePosWorld; RectTransformUtility.ScreenPointToLocalPointInRectangle (ICanvas.transform as RectTransform, Input.mousePosition, UICamera.GetComponent<Camera>(), out mousePosWorld); ImageOnMouseObj.transform.position = ICanvas.transform.TransformPoint (mousePosWorld); }
怎么防止ImageOnMouseObj擋着鼠標按按鈕?
將Raycast Target的勾去掉。
按鈕的狀態:
為按鈕加載動畫控制器Button
這些Trigger,對應着控制器中的Trigger名字
控制器設計:
Highlighted 上的動作:
HoverEnter,掛在在Highlighted
HoverExit,掛載在Normal
Loop Time的勾都去掉
Button的Navigation屬性勾去掉
完整代碼:
UIManager:
using UnityEngine; using UnityEngine.UI; using System.Collections; public class UIManager : MonoBehaviour { public int[] equipmentList = {1, 2, 3}; public int[] backPackInvnetoryList = { 1, 2, 3, 0, 0, 0, 0, 0, 0 }; public GameObject UICamera; public GameObject ICanvas; InventoryManager im = InventoryManager.getInstance(); //following Invent Type convension int mouseInventType = 0; public GameObject ImageOnMouseObj; Image ImageOnMouse; public Sprite defaultSprite; void Start() { ImageOnMouse = ImageOnMouseObj.GetComponent<Image>(); ImageOnMouseObj.SetActive (false); im.init (equipmentList, backPackInvnetoryList); } void Update() { Vector2 mousePosWorld; RectTransformUtility.ScreenPointToLocalPointInRectangle (ICanvas.transform as RectTransform, Input.mousePosition, UICamera.GetComponent<Camera>(), out mousePosWorld); ImageOnMouseObj.transform.position = ICanvas.transform.TransformPoint (mousePosWorld); } public void mouseClickHandler(GameObject gridObject) { int gridNumber = gridObject.GetComponent<Grid>().gridNumber; //0 - 2 is the equipment grid number if (gridNumber >= 0 && gridNumber <= 2) { if (im.isEquipmentGridOcupied (gridNumber)) { if (mouseInventType == 0) { int equipmentType = im.getEquipmentInventory (gridNumber); toggleImageFromGridToMouse (gridObject); mouseInventType = equipmentType; } } else { //Grid 0 for head gear, 1 for chest gear, 2 for foot gear if (mouseInventType == 1 && gridNumber == 0) { im.putInventoryonEqupment (gridNumber, mouseInventType); toggleImageFromMouseToGrid (gridObject); mouseInventType = 0; } else if (mouseInventType == 2 && gridNumber == 1) { im.putInventoryonEqupment (gridNumber, mouseInventType); toggleImageFromMouseToGrid (gridObject); mouseInventType = 0; } else if (mouseInventType == 3 && gridNumber == 2) { im.putInventoryonEqupment (gridNumber, mouseInventType); toggleImageFromMouseToGrid (gridObject); mouseInventType = 0; } } } //3 - 11 is the backpack grid number else if (gridNumber >= 3 && gridNumber <= 11) { int backPackGridNumber = gridNumber - 3; Debug.Log (backPackGridNumber + im.isBackpackGridOccupied(backPackGridNumber).ToString() + mouseInventType); if (im.isBackpackGridOccupied (backPackGridNumber)) { if (mouseInventType == 0) { int inventoryType = im.getBackPackInventory (backPackGridNumber); toggleImageFromGridToMouse (gridObject); mouseInventType = inventoryType; } } else { if (mouseInventType != 0) { im.putBackPackInventory (backPackGridNumber, mouseInventType); toggleImageFromMouseToGrid (gridObject); mouseInventType = 0; } } } } void toggleImageFromGridToMouse(GameObject grid){ Debug.Log (grid.name); Image ri_g = grid.GetComponent<Image>(); ImageOnMouse.sprite = ri_g.sprite; ri_g.sprite = defaultSprite; Color color = ri_g.color; color.a = 0.78f; ri_g.color = color; Debug.Log (ri_g.color); ImageOnMouseObj.SetActive (true); } void toggleImageFromMouseToGrid(GameObject grid){ ImageOnMouseObj.SetActive (false); Image ri_g = grid.GetComponent<Image>(); Color color = ri_g.color; color.a = 1f; ri_g.color = color; ri_g.sprite = ImageOnMouse.sprite; ImageOnMouse.sprite = defaultSprite; } }
InventoryManager:
using UnityEngine; using System.Collections; public class InventoryManager : System.Object { private static InventoryManager _instance; public static InventoryManager getInstance() { if (_instance == null) { _instance = new InventoryManager (); } return _instance; } int[] equipment; int[] backpack; public void init(int[] equipped, int[] backPackInventory){ equipment = equipped; backpack = backPackInventory; } public bool isEquipmentGridOcupied (int equipmentGridNumber){ if (equipmentGridNumber <= 2 && equipmentGridNumber >= 0) { if (equipment [equipmentGridNumber] != 0) { return true; } else { return false; } } else { return false; } } public bool isBackpackGridOccupied (int inventoryGridNumber){ //print_item (); if (backpack [inventoryGridNumber] != 0) { return true; } else { return false; } } public int getBackPackInventory(int backPackGridNumber){ if (backPackGridNumber <= 8 && backPackGridNumber >= 0) { int inventoryType = backpack [backPackGridNumber]; backpack [backPackGridNumber] = 0; //print_item (); return inventoryType; } else { return -1; } } public bool putBackPackInventory(int backPackGridNumber, int putInventoryType){ if (backPackGridNumber <= 8 && backPackGridNumber >= 0) { backpack [backPackGridNumber] = putInventoryType; //print_item (); return true; } else { return false; } } public int getEquipmentInventory(int equipmentGridNumber){ if (equipmentGridNumber <= 2 && equipmentGridNumber >= 0) { int equipmentInventoryType = equipment [equipmentGridNumber]; equipment [equipmentGridNumber] = 0; //print_item (); return equipmentInventoryType; } else { return -1; } } public bool putInventoryonEqupment(int equipmentGridNumber, int inventoryType){ //print_item (); if (equipmentGridNumber <= 2 && equipmentGridNumber >= 0) { equipment [equipmentGridNumber] = inventoryType; //print_item (); return true; } else { return false; } } void print_item(){ Debug.Log ("Equipement: "); for (int i = 0; i < 3; i++) { Debug.Log (equipment[i] + " "); } Debug.Log ("Backpack: "); for (int i = 0; i < 9; i++) { Debug.Log (backpack[i] + " "); } } }
Grid:
using UnityEngine; using System.Collections; public class Grid : MonoBehaviour { public int gridNumber = 0; }
TiltWindow:
using UnityEngine; public class TiltWindow : MonoBehaviour { public Vector2 range = new Vector2(5f, 3f); Transform mTrans; Quaternion mStart; Vector2 mRot = Vector2.zero; void Start () { mTrans = transform; mStart = mTrans.localRotation; } void Update () { Vector3 pos = Input.mousePosition; float halfWidth = Screen.width * 0.5f; float halfHeight = Screen.height * 0.5f; float x = Mathf.Clamp((pos.x - halfWidth) / halfWidth, -1f, 1f); float y = Mathf.Clamp((pos.y - halfHeight) / halfHeight, -1f, 1f); mRot = Vector2.Lerp(mRot, new Vector2(x, y), Time.deltaTime * 5f); mTrans.localRotation = mStart * Quaternion.Euler(-mRot.y * range.y, -mRot.x * range.x, 0f); } }