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