Unity3D 学习手记 - UGUI
注:本实例开发环境为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); } }