Unity背包系统
发表于2018-03-20
在前面的文章中给大家介绍了背包系统的Json生成与解析与背包UI设计。在本文中将和大家介绍整个背包制作流程,由于背包系统制作涉及挺多内容,大家如果想了解透,可以下载本文工程源码。
【开发物品类及其子类】
物品类(KnapsackGood)是一切物品的基类
其中物品分为消耗物品(Consumable)、装备物品(Equitment)、武器物品(Weapon)、材料物品(Material)都继承自物品类(KnapsackGood)
KnapsackGood.cs
using UnityEngine; using System.Collections; /// <summary> /// 物品共有属性类 /// </summary> public class BaseProperty { public int ID { get; set; } public string name { get; set; } public string description { get; set; } public int capacity { get; set; } public int buyPrice { get; set; } public int sellPrice { get; set; } public string sprite { get; set; } public KnapsackGood.GoodType goodType { get; set; } public KnapsackGood.GoodQuality goodQuality { get; set; } public BaseProperty() {} public BaseProperty(int ID, string name, string description, int capacity, int buyPrice, int sellPrice, string sprite, KnapsackGood.GoodType goodType, KnapsackGood.GoodQuality goodQuality) { this.ID = ID; this.name = name; this.description = description; this.capacity=capacity; this.buyPrice = buyPrice; this.sellPrice = sellPrice; this.sprite = sprite; this.goodType = goodType; this.goodQuality = goodQuality; } } /// <summary> /// 物品基类 /// </summary> public class KnapsackGood { public BaseProperty goodProperty { get; set; } public KnapsackGood() { } public KnapsackGood(BaseProperty goodProperty) { this.goodProperty = goodProperty; } /// <summary> /// 物品类型 /// </summary> public enum GoodType { Consumable, //消耗品 Equipment, //装备 Weapon, //武器 Meterial //材料 } /// <summary> /// 物品品质 /// </summary> public enum GoodQuality { Common, //普通 Uncommon, //不普通 Rare, //稀有 Epic, //史诗 Legendary //传奇 } public virtual string GetDescribe() { string color = ""; switch (goodProperty.goodQuality) { case KnapsackGood.GoodQuality.Common: color = "white"; break; case KnapsackGood.GoodQuality.Epic: color = "lime"; break; case KnapsackGood.GoodQuality.Legendary: color = "navy"; break; case KnapsackGood.GoodQuality.Rare: color = "magenta"; break; case KnapsackGood.GoodQuality.Uncommon: color = "orange"; break; default: break; } string describe = string.Format("<color={0}>{1}</color>\n<size=10><color=green>购买价格:{2} 出售价格:{3}</color></size>\n<color=yellow><size=10>{4}</size></color>", color, goodProperty.name, goodProperty.buyPrice, goodProperty.sellPrice, goodProperty.description); return describe; } }
其他子类是在父类的基础上增加特点属性,因为篇幅在这里我就不贴出其子类的代码,大家可下载源码自行学习,代码不难理解
【创建KnapsackManager(面板与物品之间的媒介)】
KnapsackManager这个类管理面板背包面板与物品,相当于面板与物品之间的媒介,方便管理
using UnityEngine; using System.Collections; using UnityEditor; using LitJson; using System.Collections.Generic; using System.IO; using System.Text; using System; public class KnapsackManager : MonoBehaviour { private static KnapsackManager _instance; public static KnapsackManager GetInstance { get { if (_instance == null) { _instance = GameObject.Find("Camera").GetComponent<KnapsackManager>(); } return _instance; } } private bool isShowToolTilePanel = false; public GameObject pickGood; public GoodUI GetPickGoodUI { get { return pickGood.GetComponent<GoodUI>(); } } public Vector3 GetPickGoodLocalPos { get { return pickGood.transform.localPosition; } } public bool isPickGood = false; /// <summary> /// 存储多个物品对象(从json解析过来) /// </summary> private List<KnapsackGood> goodList; void Awake() { ParseGoodsJson(); } private GameObject toolTilePanel; private GameObject canvase; // Use this for initialization void Start () { toolTilePanel = GameObject.Find("ToolTilePanel"); canvase = GameObject.Find("Canvas"); pickGood = GameObject.Find("PickGoodUI"); } // Update is called once per frame void Update () { if (isPickGood) { Vector2 point = Vector2.zero; //该函数是将屏幕坐标转化以第一个参数对象的子节点坐标 //参数一:需要转换的坐标以该对象作为父节点 //参数二:鼠标点 //参数三:参数一对象以哪个摄像机渲染(由于该参数一画布没有相机渲染,故为null) //参数四:返回一个需要转换的目标点 RectTransformUtility.ScreenPointToLocalPointInRectangle(canvase.GetComponent<Canvas>().transform as RectTransform, Input.mousePosition, null, out point); pickGood.GetComponent<GoodUI>().SetLocalPosition(point); } if (isShowToolTilePanel) { Vector2 point=Vector2.zero; //该函数是将屏幕坐标转化以第一个参数对象的子节点坐标 //参数一:需要转换的坐标以该对象作为父节点 //参数二:鼠标点 //参数三:参数一对象以哪个摄像机渲染(由于该参数一画布没有相机渲染,故为null) //参数四:返回一个需要转换的目标点 RectTransformUtility.ScreenPointToLocalPointInRectangle(canvase.GetComponent<Canvas>().transform as RectTransform,Input.mousePosition,null, out point); toolTilePanel.GetComponent<ToolTilePanel>().SetLocalPosition(point + new Vector2(5,-15)); } // 画布的EventSystem事件上 判断点击鼠标左键位置上是否有UI对象 // | | //UnityEngine.EventSystems.EventSystem.current.IsPointerOverGameObject(-1)) if(isPickGood&&Input.GetMouseButtonDown(0)&&UnityEngine.EventSystems.EventSystem.current.IsPointerOverGameObject(-1)==false) { isPickGood = false; GetPickGoodUI.SetLocalPosition(new Vector3(-420, 2, 0)); HideToolTilePanel(); } } void ParseGoodsJson() { goodList = new List<KnapsackGood>(); string path = Application.streamingAssetsPath + "/GoodJson.json"; #region 文件流方式读文件 /* byte[] bt=new byte[1024]; char[] data=new char[1024]; FileStream fileStream = File.Open(path,FileMode.Open); fileStream.Seek(0,SeekOrigin.Begin); fileStream.Read(bt,0,1024); System.Text.Decoder coder = System.Text.Encoding.UTF8.GetDecoder(); coder.GetChars(bt,0,bt.Length,data,0); Debug.Log(new string(data)); fileStream.Flush(); fileStream.Close(); List<Consumable> goods = JsonMapper.ToObject<List<Consumable>>(new string(data)); foreach (Consumable good in goods) { Debug.Log(good.ToString()); } */ #endregion StreamReader sr =new StreamReader(path); string json=sr.ReadToEnd(); sr.Close(); JsonData data = JsonMapper.ToObject(json); int i = 0; for (i = 0; i <= data.Count - 1; i++) { BaseProperty gp = JsonMapper.ToObject<BaseProperty>(data[i]["goodProperty"].ToJson()); KnapsackGood kg = null; switch (gp.goodType) { case KnapsackGood.GoodType.Consumable: int HP = int.Parse(data[i]["HP"].ToString()); int MP = int.Parse(data[i]["MP"].ToString()); kg = new Consumable(HP,MP,gp); break; case KnapsackGood.GoodType.Equipment: int strength = int.Parse(data[i]["strength"].ToString()); int intellect = int.Parse(data[i]["intellect"].ToString()); int agility = int.Parse(data[i]["agility"].ToString()); int stamina = int.Parse(data[i]["stamina"].ToString()); Equipment.EquitmentType equitmentType = (Equipment.EquitmentType)System.Enum.Parse(typeof(Equipment.EquitmentType), data[i]["equitmentType"].ToString()); kg = new Equipment(strength,intellect,agility,stamina,equitmentType,gp); break; case KnapsackGood.GoodType.Meterial: break; case KnapsackGood.GoodType.Weapon: int damage = int.Parse(data[i]["damage"].ToString()); Weapon.WeaponType weaponType = (Weapon.WeaponType)System.Enum.Parse(typeof(Weapon.WeaponType), data[i]["weaponType"].ToString()); kg = new Weapon(damage, weaponType, gp); break; default: break; } goodList.Add(kg); } } public KnapsackGood GetGoodWithID(int ID) { foreach (KnapsackGood good in goodList) { if (ID == good.goodProperty.ID) { return good; } } return null; } public void ShowToolTilePanel(string str=" ") { isShowToolTilePanel = true; toolTilePanel.GetComponent<ToolTilePanel>().ShowPanel(str); } public void HideToolTilePanel() { isShowToolTilePanel = false; toolTilePanel.GetComponent<ToolTilePanel>().HidePanel(); } }
【创建面板类Inventory】
面板类管理面板中各个物品槽,当存储物品是,寻找合适的物品槽并且管理将物品存储到该物品槽内,并且控制面板的显示和隐藏
Inventory.cs
using UnityEngine; using System.Collections; public class Inventory : MonoBehaviour { public Slot[] slotList; /// <summary> /// 控制面板的透明度从而控制显示或隐藏 /// </summary> private float targetAlpha=1; private CanvasGroup cg; private float lerpSpeed=2.0f; protected virtual void Start() { slotList = this.GetComponentsInChildren<Slot>(); cg = this.GetComponent<CanvasGroup>(); } void Update() { if (targetAlpha != this.cg.alpha) { this.cg.alpha = Mathf.Lerp(this.cg.alpha, targetAlpha, lerpSpeed * Time.deltaTime); if (Mathf.Abs(this.cg.alpha - targetAlpha) <= 0.01f) { this.cg.alpha = targetAlpha; } } } public bool StoreGood(int ID) { KnapsackGood good = KnapsackManager.GetInstance.GetGoodWithID(ID); return StoreGood(good); } public bool StoreGood(KnapsackGood good) { if (good == null) { Debug.Log("需要存储的物品为空"); return false; } if (good.goodProperty.capacity == 1)//如果该物品在物品槽中只能存放一个 {//直接找到一个还没有使用的物品槽 Slot slot = FindEmptySlot(); if (slot != null)//如果找到了空的物品槽,那么将该物品发进去 { slot.StoreGood(good); } else//如果没有找到,则将说明背包以满 { Debug.Log("没有找到空的物品槽,可能是背包以满"); return false; } } else {//先寻找是否已经有存放了该物品的物品槽 Slot slot = FindFreeSlot(good.goodProperty.ID); if (slot != null) { slot.StoreGood(good); } else { Slot s = FindEmptySlot(); if (s != null) { s.StoreGood(good); } else { Debug.Log("没有找到空的物品槽,可能是背包以满"); return false; } } } return true; ; } /// <summary> /// 根据ID寻找存储了该物品的物品槽 /// </summary> /// <returns></returns> private Slot FindFreeSlot(int ID) { foreach (Slot s in slotList) { if (s.transform.childCount > 0 && s.GetGoodID() == ID&&s.IsFill()==false) { return s; } } return null; } private Slot FindEmptySlot() { foreach (Slot s in slotList) { //还没有存放物品的物品槽中也没有添加GoodUI,根据该物品槽中是否有子节点这个线索来判断是否存放物品 if (s.transform.childCount == 0||s.transform.gameObject.activeInHierarchy==false) { return s; } } //没有找到空的物品槽,说明背包以满 return null; } public void ShowPanel() { targetAlpha = 1; this.cg.blocksRaycasts = true; } public void HidePanel() { targetAlpha = 0; this.cg.blocksRaycasts = false; } }
创建一个KnapsackPanel.cs挂载到面板上,继承自Inventory
using UnityEngine; using System.Collections; public class KnapsackPanel : Inventory { private static KnapsackPanel _instance; public static KnapsackPanel GetInstance { get { if (_instance == null) { _instance = GameObject.Find("KnapsackPanel").GetComponent<KnapsackPanel>(); } return _instance; } } }
【创建物品槽类】
物品槽类控制显示物品UI,提示面板的显示和隐藏
该类是挂载到物品槽上
using UnityEngine; using System.Collections; using UnityEngine.EventSystems; using System; public class Slot : MonoBehaviour ,IPointerEnterHandler,IPointerExitHandler,IPointerDownHandler { // Use this for initialization void Start () { } // Update is called once per frame void Update () { } public int GetGoodID() { if (this.transform.childCount == 0) { return -1; } GoodUI gu = this.transform.GetChild(0).GetComponent<GoodUI>(); return gu.good.goodProperty.ID; } public bool IsFill() { GoodUI gu = this.transform.GetChild(0).GetComponent<GoodUI>(); return gu.good.goodProperty.capacity <= gu.amount;//物品的容量小于物品当前的数量 } public GameObject goodPrefab; public void StoreGood(KnapsackGood good,int amount=1) { if (this.transform.childCount == 0)//如果该物品槽为空,则在这物品槽里新建一个GoodUI { GameObject obj = GameObject.Instantiate(goodPrefab); obj.transform.SetParent(this.transform); obj.transform.localPosition = Vector3.zero; obj.transform.localScale = Vector3.one; obj.GetComponent<GoodUI>().SetGood(good,amount); } else { if (this.transform.GetChild(0).gameObject.activeInHierarchy == false) { this.transform.GetChild(0).GetComponent<GoodUI>().Show(); this.transform.GetChild(0).GetComponent<GoodUI>().SetGood(good,amount); } else { this.transform.GetChild(0).GetComponent<GoodUI>().AddGood(); } } } public void OnPointerEnter(PointerEventData eventData) { Debug.Log("鼠标进入物品槽"); if (this.transform.childCount > 0) { //获得描述文本 string text = this.transform.GetChild(0).GetComponent<GoodUI>().good.GetDescribe(); KnapsackManager.GetInstance.ShowToolTilePanel(text); } } public void OnPointerExit(PointerEventData eventData) { if (this.transform.childCount > 0) { KnapsackManager.GetInstance.HideToolTilePanel(); } } /// <summary> /// 鼠标抬下 /// /// **************************方法说明************************************* /// *//物品槽里没有物品 * /// * //1.IsPickGood=true 将PickGood里的物品放置到该物品槽内 * /// * //2.IsPickGood=false 不做任何处理 * /// *//物品槽里存在物品 * /// * //1.IsPickGood=true 将该物品槽的物品替换PickGood里的物品 * /// * //2.IsPickGood=false 将该物品槽的物品放置在PickGood里 * /// *********************************************************************** /// </summary> /// <param name="eventData"></param> public virtual void OnPointerDown(PointerEventData eventData) { if (this.transform.childCount == 0 || this.transform.GetChild(0).gameObject.activeInHierarchy == false) { if (KnapsackManager.GetInstance.isPickGood) { Debug.Log(KnapsackManager.GetInstance.GetPickGoodUI.amount); StoreGood(KnapsackManager.GetInstance.GetPickGoodUI.good, KnapsackManager.GetInstance.GetPickGoodUI.amount); KnapsackManager.GetInstance.GetPickGoodUI.SetLocalPosition(new Vector3(-420,2,0));//将pickGoodUI位置归零 KnapsackManager.GetInstance.isPickGood = false; } else { //无需做处理 } } else { if (KnapsackManager.GetInstance.isPickGood) { StorageSlot.slot.StoreGood(this.transform.GetChild(0).GetComponent<GoodUI>().good, this.transform.GetChild(0).GetComponent<GoodUI>().amount); //this.transform.GetChild(0).GetComponent<GoodUI>().Exchange(Slot.GetInstance.transform.GetChild(0).GetComponent<GoodUI>()); //StoreGood(KnapsackManager.GetInstance.GetPickGoodUI.good, KnapsackManager.GetInstance.GetPickGoodUI.amount); this.transform.GetChild(0).GetComponent<GoodUI>().SetGood(KnapsackManager.GetInstance.GetPickGoodUI.good, KnapsackManager.GetInstance.GetPickGoodUI.amount); KnapsackManager.GetInstance.GetPickGoodUI.SetLocalPosition(new Vector3(-420, 2, 0));//将pickGoodUI位置归零 KnapsackManager.GetInstance.isPickGood = false; } else { StorageSlot.slot = this; KnapsackManager.GetInstance.GetPickGoodUI.ReplaceGoodUI(transform.GetChild(0).GetComponent<GoodUI>()); KnapsackManager.GetInstance.isPickGood = true; //将自身物品槽的UI销毁 Destroy(this.transform.GetChild(0).gameObject); } } } }
【创建GoodUI】
该类挂载在物品槽的子对象上
控制物品现在在物品槽内
using UnityEngine; using System.Collections; using UnityEngine.UI; public class GoodUI : MonoBehaviour { public KnapsackGood good { get; private set; }//物品槽中存放的物品 public int amount { get; private set; }//物品槽存放物品的数量 private Image GoodUIImage { get { return this.transform.GetComponent<Image>(); } } private Text GoodUIText { get { return this.transform.GetComponentInChildren<Text>(); } } public void SetGood(KnapsackGood good, int amount = 1) { this.good = good; this.amount = amount; GoodUIImage.sprite = Resources.Load<Sprite>(good.goodProperty.sprite); if (good.goodProperty.capacity > 1) { GoodUIText.text = this.amount.ToString(); } else { GoodUIText.text = ""; } } public void AddGood(int amount = 1) { this.amount += amount; if (good.goodProperty.capacity > 1) { GoodUIText.text = this.amount.ToString(); } else { GoodUIText.text = ""; } } public void ReplaceGoodUI(GoodUI gu) { this.good = gu.good; this.amount = gu.amount; Debug.Log(gu.amount); SetGood(this.good, this.amount); } public void Exchange(GoodUI gu) { KnapsackGood tempGood = gu.good; int tempAmount = gu.amount; gu.SetGood(good, amount); this.SetGood(tempGood, tempAmount); } public void SetLocalPosition(Vector3 point) { this.transform.localPosition = point; } public void Show() { this.gameObject.SetActive(true); } public void Hide() { this.gameObject.SetActive(false); } }
【测试】
创建一个UserPlaying.cs来测试代码
using UnityEngine; using System.Collections; public class UserPlaying : MonoBehaviour { // Update is called once per frame void Update () { if (Input.GetKeyUp(KeyCode.G)) { int rand = Random.Range(1,8); KnapsackPanel.GetInstance.StoreGood(rand); } } }
按下G键,发现随机生成了物品并显示在物品槽中

建议大家下载我的源码学习,这样有助于理解,源码中也写了角色背包,角色面板的角色物品槽处理和背包不尽相同,大体差不多

好了,背包系统介绍到这里。
总结:
在这里我着重介绍两个知识点:
一、RectTransformUtility.ScreenPointToLocalPointInRectangle函数的使用
//该函数是将屏幕坐标转化以第一个参数对象的子节点坐标 //参数一:需要转换的坐标以该对象作为父节点 //参数二:鼠标点 //参数三:参数一对象以哪个摄像机渲染(由于该参数一画布没有相机渲染,故为null) //参数四:返回一个需要转换的目标点 RectTransformUtility.ScreenPointToLocalPointInRectangle(canvase.GetComponent<Canvas>().transform as RectTransform,Input.mousePosition,null, out point);
二、IPointerEnterHandler,IPointerExitHandler,IPointerDownHandler接口的使用
以上接口的实现函数分别在鼠标移入该对象、移除该对象、点击该对象时触发调用
其中会传进一个参数UnityEngine.EventSystems.PointerEventData eventData
可以根据这个参数,解析你需要的事件
比如:
if (eventData.button != PointerEventData.InputButton.Left) { return; }
背包系统源码:https://download.csdn.net/download/qq_33747722/9819495
来自:http://blog.csdn.net/qq_33747722/article/details/70244303