Unity背包系统

在前面的文章中我介绍了背包系统的Json生成与解析、UI的设计

在本文介绍整个背包制作流程,由于背包系统制作涉及挺多内容,大家如果想了解透,建议下载本文工程源码点击打开链接

【开发物品类及其子类】

物品类(KnapsackGood)是一切物品的基类

其中物品分为消耗物品(Consumable)、装备物品(Equitment)、武器物品(Weapon)、材料物品(Material)都继承自物品类(KnapsackGood)

KnapsackGood.cs

using UnityEngine;
using System.Collections;

/// 
/// 物品共有属性类
/// 
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;
    }
}

/// 
/// 物品基类
/// 
public class KnapsackGood 
{
    public BaseProperty goodProperty { get; set; }

    public KnapsackGood()
    { 
    }

    public KnapsackGood(BaseProperty goodProperty)
    {
        this.goodProperty = goodProperty;
    }

    /// 
    /// 物品类型
    /// 
    public enum GoodType
    {
        Consumable, //消耗品
        Equipment,  //装备
        Weapon,     //武器
        Meterial    //材料
    }

    /// 
    /// 物品品质
    /// 
    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("{1}\n购买价格:{2} 出售价格:{3}\n{4}", 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();
			}

			return _instance;
		}
	}

	private bool isShowToolTilePanel = false;

	public GameObject pickGood;
	public GoodUI GetPickGoodUI
	{
		get
		{
			return pickGood.GetComponent();
		}
	}

	public Vector3 GetPickGoodLocalPos
	{
		get
		{
			return pickGood.transform.localPosition;
		}
	}
	
	public bool isPickGood = false;

	/// 
	/// 存储多个物品对象(从json解析过来)
	/// 
	private List 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().transform as RectTransform, Input.mousePosition, null, out point);
			pickGood.GetComponent().SetLocalPosition(point);
		}
		if (isShowToolTilePanel)
		{
			Vector2 point=Vector2.zero;
			//该函数是将屏幕坐标转化以第一个参数对象的子节点坐标
			//参数一:需要转换的坐标以该对象作为父节点
			//参数二:鼠标点
			//参数三:参数一对象以哪个摄像机渲染(由于该参数一画布没有相机渲染,故为null)
			//参数四:返回一个需要转换的目标点
			RectTransformUtility.ScreenPointToLocalPointInRectangle(canvase.GetComponent().transform as RectTransform,Input.mousePosition,null, out point);
			toolTilePanel.GetComponent().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();

        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 goods = JsonMapper.ToObject>(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(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().ShowPanel(str);
	}

	public void HideToolTilePanel()
	{
		isShowToolTilePanel = false;
		toolTilePanel.GetComponent().HidePanel();
	}

}


【创建面板类Inventory】

面板类管理面板中各个物品槽,当存储物品是,寻找合适的物品槽并且管理将物品存储到该物品槽内,并且控制面板的显示和隐藏

Inventory.cs

using UnityEngine;
using System.Collections;

public class Inventory : MonoBehaviour
{
	public Slot[] slotList;

    /// 
    /// 控制面板的透明度从而控制显示或隐藏
    /// 
    private float targetAlpha=1;
    private CanvasGroup cg;
    private float lerpSpeed=2.0f;

	protected virtual void Start()
	{
		slotList = this.GetComponentsInChildren();
        cg = this.GetComponent();
	}

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

	/// 
	/// 根据ID寻找存储了该物品的物品槽
	/// 
	/// 
	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();
			}

			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();
		return gu.good.goodProperty.ID;
	}

	public bool IsFill()
	{
		GoodUI gu = this.transform.GetChild(0).GetComponent();
		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().SetGood(good,amount);
		}
		else
		{
			if (this.transform.GetChild(0).gameObject.activeInHierarchy == false)
			{
				this.transform.GetChild(0).GetComponent().Show();
				this.transform.GetChild(0).GetComponent().SetGood(good,amount);
			}
			else
			{
				this.transform.GetChild(0).GetComponent().AddGood();
			}
			
		}
	}

	public  void OnPointerEnter(PointerEventData eventData)
	{
		Debug.Log("鼠标进入物品槽");
		if (this.transform.childCount > 0)
		{
			//获得描述文本
			string text = this.transform.GetChild(0).GetComponent().good.GetDescribe();
		
			KnapsackManager.GetInstance.ShowToolTilePanel(text);
		}
	}

	public void OnPointerExit(PointerEventData eventData)
	{
		if (this.transform.childCount > 0)
		{
			KnapsackManager.GetInstance.HideToolTilePanel();
		}
	}

	/// 
	/// 鼠标抬下
	/// 
	/// **************************方法说明*************************************
	/// *//物品槽里没有物品													  *
	///	*	//1.IsPickGood=true		将PickGood里的物品放置到该物品槽内		  *
	///	*	//2.IsPickGood=false	不做任何处理							  *
	///	*//物品槽里存在物品													  *					
	///	*	//1.IsPickGood=true		将该物品槽的物品替换PickGood里的物品		  *	
	///	*	//2.IsPickGood=false	将该物品槽的物品放置在PickGood里		  *
	///	***********************************************************************
	/// 
	/// 
	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().good, this.transform.GetChild(0).GetComponent().amount);
				
				//this.transform.GetChild(0).GetComponent().Exchange(Slot.GetInstance.transform.GetChild(0).GetComponent());
				//StoreGood(KnapsackManager.GetInstance.GetPickGoodUI.good, KnapsackManager.GetInstance.GetPickGoodUI.amount);
				this.transform.GetChild(0).GetComponent().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());

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

	private Text GoodUIText
	{
		get
		{
			return this.transform.GetComponentInChildren();
		}
	}

	public void SetGood(KnapsackGood good, int amount = 1)
	{
		this.good = good;
		this.amount = amount;


		GoodUIImage.sprite = Resources.Load(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键,发现随机生成了物品并显示在物品槽中

Unity背包系统_第1张图片


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

Unity背包系统_第2张图片

好了,背包系统介绍到这里,有什么问题可以和我联系,就这样吧!!!


总结:

在这里我着重介绍两个知识点:

一.RectTransformUtility.ScreenPointToLocalPointInRectangle函数的使用

//该函数是将屏幕坐标转化以第一个参数对象的子节点坐标
			//参数一:需要转换的坐标以该对象作为父节点
			//参数二:鼠标点
			//参数三:参数一对象以哪个摄像机渲染(由于该参数一画布没有相机渲染,故为null)
			//参数四:返回一个需要转换的目标点
			RectTransformUtility.ScreenPointToLocalPointInRectangle(canvase.GetComponent().transform as RectTransform,Input.mousePosition,null, out point);

二.IPointerEnterHandler,IPointerExitHandler,IPointerDownHandler接口的使用

以上接口的实现函数分别在鼠标移入该对象、移除该对象、点击该对象时触发调用

其中会传进一个参数UnityEngine.EventSystems.PointerEventData eventData

可以根据这个参数,解析你需要的事件

比如:

if (eventData.button != PointerEventData.InputButton.Left)
        {
            return;
        }


本人也在寻找一份游戏开发实习工作,如果大佬们需要开发人员,请把我带走

这是我的简历:resume.liujunliang.com.cn/resume.pdf

作品的话可以私聊我哦!


你可能感兴趣的:(Unity背包系统)