MVC背包
需求:
1、背包格子的裝備是可以拖動的
2、裝備欄的裝備也是可以拖動的
3、當背包格子的裝備拖動到裝備欄時,如果是裝備類型和裝備欄類型是一致的能裝上
4、背包的裝備是按照順序放在格子中的,有分頁的功能
5、每件裝備上有物品的屬性加成
6、人物有基本的屬性和裝備加成的屬性,界面顯示就是基本屬性+ 加成屬性(綠色標識)
7、當你拖動背包的裝備時,對應能裝備該物品的裝備欄要提示
8、不光需要拖動才能裝備,鼠標右鍵點擊裝備也能裝備或卸下
9、背包是通過負重來顯示裝備的多少的
10、背包的裝備拖動到非背包區域,可以銷毀
11、鼠標懸停裝備時,顯示裝備的屬性信息
12、一件整理,把相同類型的裝備放在一起(考慮權重)
13、一件出售
MVC結構
Model(數據層)
1、每一件裝備需要一個類來存儲屬性信息
2、人物的基本屬性,和身上的裝備,裝備的加成需要一個類來存儲
3、背包里的裝備和背包的負重需要一個類來存儲
View(視圖層)
1、每個格子是可以拖動裝備的,對於背包的格子和裝備欄的格子拖動的功能是一致的,但是拖動結束時的判斷不一致,我們可以把共有的功能抽象出來一個基類,兩個子類分別繼承基類。
2、對於人物顯示的界面,需要一個類來管理裝備欄的裝備,顯示人物的屬性。
3、需要一個背包的類來管理每個格子,顯示頁數,負重等信息。
4、當鼠標放在裝備上時,顯示裝備的基本信息也需要一個類來做處理。
Control(控制層)
需要一個控制層來做數據的交換,刪除等等
界面搭建
畫布尺寸1920x1080

字體錨點
字體自適應10-20

限制列數,中心對齊
Model
ItemData,物品信息類
using System.Collections; using System.Collections.Generic; using UnityEngine; /// <summary> /// 該類用於存儲裝備的基本屬性 /// </summary> [System.Serializable] public class ItemData { #region 私有變量 [SerializeField] private int id;//作為物品的唯一標識 [SerializeField] private string itemName;//物品的名字 [SerializeField] private string iconName;//物品顯示圖片的名字 [SerializeField] private float atk;//攻擊力加成 [SerializeField] private float def;//防御力加成 [SerializeField] private float thump;//暴擊率加成 [SerializeField] private float hp;//血量加成 [SerializeField] private float mp;//加成 [SerializeField] private float anger;//怒氣加成 [SerializeField] private float weight;//重量 [SerializeField] private ItemType type;//物品的類型 #endregion #region 屬性 public int Id { get { return id; } } public string ItemName { get { return itemName; } } public string IconName { get { return iconName; } } public float Atk { get { return atk; } } public float Def { get { return def; } } public float Thump { get { return thump; } } public float Hp { get { return hp; } } public float Mp { get { return mp; } } public float Anger { get { return anger; } } public float Weight { get { return weight; } } public ItemType Type { get { return type; } } #endregion #region 構造函數 public ItemData() { } public ItemData(int id, string itemName, string iconName, float atk, float def, float thump, float hp, float mp, float anger, float weight, ItemType type) { this.id = id; this.itemName = itemName; this.iconName = iconName; this.atk = atk; this.def = def; this.thump = thump; this.hp = hp; this.mp = mp; this.anger = anger; this.weight = weight; this.type = type; } #endregion }
ItemType,物品類型類
/// <summary> /// 物品的類型, 裝備欄能裝備的類型 /// </summary> public enum ItemType { Weapon,//武器 Cap,//頭盔 Armour,//鎧甲 Ring,//戒指 Belt,//腰帶 Necklace,//項鏈 Shoe,//鞋子 Headwear,//頭飾 }
PlayerData,角色裝備類
using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerData { #region 單例 private static PlayerData instance;//這個數據類的單例,從文件中反序列化來的 public static PlayerData Instance { get { return instance; } } private PlayerData() { } /// <summary> /// 對單例進行賦值 /// </summary> /// <param name="data"></param> public static void SetInstance(PlayerData data) { if (null == data) { instance = new PlayerData(); } else { instance = data; } } #endregion #region 私有變量 [SerializeField] private List<ItemData> items;//人物身上穿戴的裝備 [SerializeField] private float atk;//基礎攻擊力 [SerializeField] private float def;//基礎防御力 [SerializeField] private float thump;//基礎暴擊率 [SerializeField] private float hp;//基礎血量 [SerializeField] private float mp;//基礎魔法 [SerializeField] private float anger;//基礎怒氣 //裝備的屬性加成都是裝備的裝備計算出來的 private float addAtk;//裝備攻擊力加成 private float addDef;// private float addThump;// private float addHp;// private float addMp;// private float addAnger;// #endregion #region 屬性 public float Atk { get { return atk; } } public float Def { get { return def; } } public float Thump { get { return thump; } } public float Hp { get { return hp; } } public float Mp { get { return mp; } } public float Anger { get { return anger; } } public float AddAtk { get { return addAtk; } } public float AddDef { get { return addDef; } } public float AddThump { get { return addThump; } } public float AddHp { get { return addHp; } } public float AddMp { get { return addMp; } } public float AddAnger { get { return addAnger; } } public List<ItemData> Items { get { return items; } } #endregion #region 提供一些訪問或刪除裝備的方法 /// <summary> /// 通過裝備的ID來訪問裝備 /// </summary> /// <param name="id"></param> /// <returns></returns> public ItemData GetItem(int id) { return null; } /// <summary> /// 通過裝備的類型來訪問裝備 /// </summary> /// <param name="type"></param> /// <returns></returns> public ItemData GetItem(ItemType type) { return null; } /// <summary> /// 添加裝備 /// </summary> /// <param name="data"></param> public void AddItem(ItemData data) { } /// <summary> /// 刪除裝備 /// </summary> /// <param name="data"></param> public void RemoveItem(ItemData data) { } /// <summary> /// 通過ID刪除裝備 /// </summary> /// <param name="id"></param> public void RemoveItem(int id) { } /// <summary> /// 通過裝備類型刪除裝備 /// </summary> /// <param name="type"></param> public void RemoveItem(ItemType type) { } #endregion public void Init() { UpdateAdditionData(); } /// <summary> /// 計算裝備的加成數據, 每次當數據發生改變的時候調用 /// </summary> void UpdateAdditionData() { this.addAtk = 0; this.addDef = 0; this.addThump = 0; this.addHp = 0; this.addMp = 0; this.addAnger = 0; //把每一件裝備的加成數據加給實際的數據 for (int i = 0; i < items.Count; i++) { this.addAtk += items[i].Atk; this.addDef += items[i].Def; this.addThump += items[i].Thump; this.addHp += items[i].Hp; this.addMp += items[i].Mp; this.addAnger += items[i].Anger; } } }
BagData,背包物品類
using System.Collections; using System.Collections.Generic; using UnityEngine; public class BagData { #region 單例 private static BagData instance;//從文件中反序列化來的 public static BagData Instance { get { return instance; } } private BagData() { } public static void SetInstance(BagData data) { if (data == null) { data = new BagData(); data.items = new List<ItemData>(); } else { instance = data; } } #endregion #region 私有變量 [SerializeField] private List<ItemData> items;//當前的所有的裝備 [SerializeField] private float maxCapacity;//最大容量, 從文件中讀取進來的 private float currentCapacity;//當前容量, 根據當前背包的裝備計算出來的 #endregion #region 屬性 public float MaxCapacity { get { return maxCapacity; } } public float CurrentCapacity { get { return currentCapacity; } } public List<ItemData> Items { get { return items; } } #endregion #region 提供一些操作背包裝備的方法 /// <summary> /// 使用ID訪問背包中的裝備 /// </summary> /// <param name="id"></param> /// <returns></returns> public ItemData GetItem(int id) { return null; } /// <summary> /// 添加裝備 /// </summary> /// <param name="id"></param> public void AddItem(ItemData data) { } /// <summary> /// 刪除裝備 /// </summary> /// <param name="data"></param> public void RemoveItem(ItemData data) { } /// <summary> /// 刪除指定ID的裝備 /// </summary> /// <param name="id"></param> public void RemoveItem(int id) { } #endregion public void UpdateCurrentCapacity() { currentCapacity = 0; //把每一件裝備的負重累加在一起,就是當前的負重 for (int i = 0; i < items.Count; i++) { currentCapacity += items[i].Weight; } } }
View:人物界面UI:屬性,裝備
GridBase,格子基類
PlayerGridUI,格子子類-玩家
BagGridUI,格子子類-背包
PlayerUI,玩家界面
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class PlayerUI : MonoBehaviour { private GridBase[] grids;//所有的裝備欄 //所有顯示屬性的Text private Text atkText; private Text defText; private Text thumpText; private Text hpText; private Text mpText; private Text angerText; void Awake() { //在子物體里獲取所有的GridBase這個組件,返回的是一個數組 grids = gameObject.GetComponentsInChildren<GridBase>(); atkText = transform.Find("MessageUI/Message/AtkText").GetComponent<Text>(); defText = transform.Find("MessageUI/Message/DefText").GetComponent<Text>(); thumpText = transform.Find("MessageUI/Message/ThumpText").GetComponent<Text>(); hpText = transform.Find("MessageUI/Message/HPText").GetComponent<Text>(); mpText = transform.Find("MessageUI/Message/MPText").GetComponent<Text>(); angerText = transform.Find("MessageUI/Message/AngerText").GetComponent<Text>(); //新建Json的小技巧 //ItemData data = new ItemData(); //Debug.Log(JsonUtility.ToJson(data, true)); } private void Start() { UpdatePanel(); } /// <summary> /// 更新界面的方法 /// </summary> void UpdatePanel() { //把人物身上裝備的物品顯示, 所有屬性顯示 atkText.text = "攻擊力:" + PlayerData.Instance.Atk + "<color=\"green\"> + " + PlayerData.Instance.AddAtk + "</color>"; defText.text = "防御力:" + PlayerData.Instance.Def + "<color=\"green\"> + " + PlayerData.Instance.AddDef + "</color>"; thumpText.text = "暴擊率:" + PlayerData.Instance.Thump + "<color=\"green\"> + " + PlayerData.Instance.AddThump + "</color>"; hpText.text = "生命值:" + PlayerData.Instance.Hp + "<color=\"green\"> + " + PlayerData.Instance.AddHp + "</color>"; mpText.text = "魔法值:" + PlayerData.Instance.Mp + "<color=\"green\"> + " + PlayerData.Instance.AddMp + "</color>"; angerText.text = "怒氣值:" + PlayerData.Instance.Anger + "<color=\"green\"> + " + PlayerData.Instance.AddAnger + "</color>"; } }
BagUI,背包UI
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class BagUI : MonoBehaviour { private GridBase[] grids; private Text weightText; private Text numText; private Button rightButton; private Button leftButton; private Button clearUpButton; private int currentNum; private int maxNum;//根據背包里物品數量與當前有多少個格子比較 void Awake() { //獲取所有的格子 grids = gameObject.GetComponentsInChildren<GridBase>(); weightText = transform.Find("Capacity/Text").GetComponent<Text>(); numText = transform.Find("Page/PageNum/Text").GetComponent<Text>(); rightButton = transform.Find("Page/RightButton").GetComponent<Button>(); leftButton = transform.Find("Page/LeftButton").GetComponent<Button>(); clearUpButton = transform.Find("ClearUpButton").GetComponent<Button>(); //按鈕注冊事件 rightButton.onClick.AddListener(RightClick); leftButton.onClick.AddListener(LeftClick); clearUpButton.onClick.AddListener(ClearUpClick); } private void Start() { //maxNum = (int)Mathf.Ceil(BagData.Instance.Items.Count / (float)grids.Length); currentNum = 1;//界面一開始,當前頁是1 //UpdatePanel(); } /// <summary> /// 更新界面 /// </summary> void UpdatePanel() { //更新當前頁數的物品, 更新當前頁數, 更新當前的負重 //計算當前的最大頁數 maxNum = (int)Mathf.Ceil(BagData.Instance.Items.Count / (float)grids.Length); weightText.text = "負重:" + BagData.Instance.CurrentCapacity + "/" + BagData.Instance.MaxCapacity; numText.text = currentNum + "/" + maxNum; //顯示當前的頁數的物品 //根據當前頁數,確定第一個位置應該排背包數據的里的第幾個索引(起始索引) //模擬 格子: 20 當第一頁是起始索引為0, 當第二頁時起始索引為20 int startIndex = (currentNum - 1) * grids.Length;//(當前頁數 - 1) * 格子數量 //把從起始索引開始,依次的把物品放在對應的格子上 for (int i = 0; i < grids.Length; i++) { //當i= 0時,證明是第一個格子,對應的物品索引 startIndex = startIndex + i //當i= 1時,證明是第二各格子,對應的物品索引 startIndex + 1 = startIndex + i //.... //當i = grids.Length - 1時, 最后一個格子, 對應的物品索引 startIndex + grids.Length - 1 = startIndex + i //如果startIndex + i 超出了物品的數量, 證明這個格子沒有物品 //如果startIndex + i 沒有超出物品的數量, 這個這個格子有物品 if (startIndex + i >= BagData.Instance.Items.Count) { //超出了物品的數量,該格子沒有物品 grids[i].UpdateItem(-1, ""); } else { grids[i].UpdateItem(BagData.Instance.Items[startIndex + i].Id, BagData.Instance.Items[startIndex + i].IconName); } } } /// <summary> /// 翻頁的右按鈕 /// </summary> void RightClick() { Debug.Log("RightClick"); } /// <summary> /// 翻頁的左按鈕 /// </summary> void LeftClick() { Debug.Log("LeftClick"); } /// <summary> /// 整理按鈕 /// </summary> void ClearUpClick() { Debug.Log("ClearUpClick"); } }
GridBase
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using UnityEngine.EventSystems; public class GridBase : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerClickHandler, IBeginDragHandler, IDragHandler, IEndDragHandler { public int itemID = -1;// 規定-1 那么這個格子空的 public Transform tempParent;//拖動時臨時的父物體 private Image image;//顯示裝備的圖片 private Vector2 outPos;//轉換之后的坐標 private Vector2 offset;//鼠標偏移量 private RectTransform parentRT; protected void Init() { image = transform.Find("Item").GetComponent<Image>(); image.raycastTarget = false; parentRT = tempParent as RectTransform; } /// <summary> /// 更新自己本身的物品 /// </summary> /// <param name="itemID"></param> /// <param name="iconName"></param> public void UpdateItem(int itemID, string iconName) { if (this.itemID == itemID) { return; } this.itemID = itemID; if (itemID < 0)//沒有物品 { image.enabled = false; } else { image.enabled = true; if (image.sprite.name != iconName) { Sprite sp = Resources.Load<Sprite>("Texture/Icon/" + iconName); image.sprite = sp; } } } #region 接口的虛方法 //開始拖動的虛方法 protected virtual void BeginDrag(PointerEventData eventData) { //Debug.Log("父類:BeginDrag"); //image.transform.parent = tempParent; image.transform.SetParent(tempParent); if (RectTransformUtility.ScreenPointToLocalPointInRectangle(parentRT, eventData.position, eventData.enterEventCamera, out outPos)) { offset = outPos - new Vector2(image.transform.localPosition.x, image.transform.localPosition.y); } } //拖動的虛方法 protected virtual void Drag(PointerEventData eventData) { //Debug.Log("父類:Drag"); if (RectTransformUtility.ScreenPointToLocalPointInRectangle(parentRT, eventData.position, eventData.enterEventCamera, out outPos)) { image.transform.localPosition = outPos - offset; } } //拖動結束時的虛方法 protected virtual void EndDrag(PointerEventData eventData) { //Debug.Log("父類:EndDrag"); //image.transform.parent = transform; image.transform.SetParent(transform); image.transform.localPosition = Vector3.zero; } //點擊的虛方法 protected virtual void Click(PointerEventData eventData) { //Debug.Log("父類:Click"); } //進入的虛方法 protected virtual void Enter(PointerEventData eventData) { //Debug.Log("父類:Enter"); //Debug.Log("顯示信息"); } //出去的虛方法 protected virtual void Exit(PointerEventData eventData) { //Debug.Log("父類:Exit"); //Debug.Log("隱藏信息"); } #endregion #region 實現的接口 public void OnBeginDrag(PointerEventData eventData) { BeginDrag(eventData); } public void OnDrag(PointerEventData eventData) { Drag(eventData); } public void OnEndDrag(PointerEventData eventData) { EndDrag(eventData); } public void OnPointerClick(PointerEventData eventData) { Click(eventData); } public void OnPointerEnter(PointerEventData eventData) { Enter(eventData); } public void OnPointerExit(PointerEventData eventData) { Exit(eventData); } #endregion }
PlayerGridUI
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using UnityEngine.EventSystems; public class PlayerGridUI : GridBase { public ItemType gridType; private Text text; // Use this for initialization void Awake() { base.Init(); text = transform.Find("Text").GetComponent<Text>(); gameObject.tag = "PlayerGrid"; } protected override void Click(PointerEventData eventData) { if (eventData.button == PointerEventData.InputButton.Right) { Debug.Log("卸下: " + itemID); } } protected override void EndDrag(PointerEventData eventData) { base.EndDrag(eventData); if (eventData.pointerCurrentRaycast.gameObject != null && eventData.pointerCurrentRaycast.gameObject.CompareTag("BagGrid")) { Debug.Log("卸下裝備"); } } }
BagGridUI
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.EventSystems; public class BagGridUI : GridBase { void Awake() { base.Init(); gameObject.tag = "BagGrid"; } protected override void Click(PointerEventData eventData) { if (eventData.button == PointerEventData.InputButton.Right) { Debug.Log("裝備: " + itemID); } } protected override void EndDrag(PointerEventData eventData) { base.EndDrag(eventData); if (eventData.pointerCurrentRaycast.gameObject == null)//當拖出背包區域時 { Debug.Log("賣出物品"); } else if (eventData.pointerCurrentRaycast.gameObject.CompareTag("PlayerGrid")) { Debug.Log("裝備物品"); } } }
Json:StreamingAssets -> Json -> PlayerData -> UTF-8
SaveManeger
using System.Collections; using System.Collections.Generic; using UnityEngine; public class SaveManager { #region 單例 private static SaveManager instance; public static SaveManager Instance { get { if (null == instance) { instance = new SaveManager(); } return instance; } } private SaveManager() { } #endregion /// <summary> /// 初始化數據 /// </summary> public void InitData() { //初始化人物的數據 string playerJson = FileTools.ReadJson(Application.streamingAssetsPath + "/Json/PlayerData.txt"); if (playerJson == "") { PlayerData.SetInstance(null); } else { PlayerData.SetInstance(JsonUtility.FromJson<PlayerData>(playerJson)); } PlayerData.Instance.Init(); ////初始化背包數據 //string bagJson = FileTools.ReadJson(Application.streamingAssetsPath + "/Json/Bag.txt"); //if (bagJson == "") //{ // BagData.SetInstance(null); //} //else //{ // BagData.SetInstance(JsonUtility.FromJson<BagData>(bagJson)); //} //BagData.Instance.UpdateCurrentCapacity();//更新一次負重 } /// <summary> /// 保存數據 /// </summary> public void SaveData() { } }
GameManeger
using System.Collections; using System.Collections.Generic; using UnityEngine; public class GameManager : MonoBehaviour { // Use this for initialization void Awake() { DontDestroyOnLoad(gameObject); SaveManager.Instance.InitData(); } // Update is called once per frame void OnDestroy() { SaveManager.Instance.SaveData(); } }
FileTools
using System.Collections; using System.Collections.Generic; using UnityEngine; using System.IO; using System.Text; public static class FileTools { /// <summary> /// 讀取指定路徑的json文件 /// </summary> /// <param name="path"></param> /// <returns></returns> public static string ReadJson(string path) { if (!File.Exists(path)) { return ""; } string json = ""; StreamReader sr = new StreamReader(path, Encoding.UTF8); try { json = sr.ReadToEnd(); } catch (System.Exception e) { Debug.Log(e.ToString()); } sr.Close(); return json; } /// <summary> /// 把json寫入指定的文件a /// </summary> /// <param name="path"></param> /// <param name="json"></param> public static void WriteJson(string path, string json) { if (!File.Exists(path)) { FileStream fs = File.Create(path); fs.Close(); } StreamWriter sw = new StreamWriter(path, false, Encoding.UTF8); try { sw.Write(json); } catch (System.Exception e) { Debug.Log(e.ToString()); } sw.Close(); } }