当前位置:   article > 正文

背包系统——基于ScriptableObject的学习总结_前端拖拽背包系统

前端拖拽背包系统

目录

1.什么是ScriptableObject

2.UI的搭建

3.创建物品类和背包类并利用ScriptableObject存储信息

 4.拾取物品在背包中显示

5.物品的拖拽,交换位置以及背包的拖拽

6.数据的本地存储

7.实例演示


1.什么是ScriptableObject

ScriptableObject 是一个可独立于类实例来保存大量数据的数据容器。ScriptableObject 的一个主要用例是通过避免重复值来减少项目的内存使用量。如果项目有一个预制件在附加的 MonoBehaviour 脚本中存储不变的数据,这将非常有用。

每次实例化预制件时,都会产生单独的数据副本。这种情况下可以不使用该方法并且不存储重复数据,而是使用 ScriptableObject 来存储数据,然后通过所有预制件的引用访问数据。这意味着内存中只有一个数据副本。

就像 MonoBehaviour 一样,ScriptableObject 派生自基本 Unity 对象,但与 MonoBehaviour 不同,不能将 ScriptableObject 附加到游戏对象。正确的做法是需要将它们保存为项目中的资源。

如果使用 Editor,可以在编辑时和运行时将数据保存到 ScriptableObject,因为 ScriptableObject 使用 Editor 命名空间和 Editor 脚本。但是,在已部署的构建中,不能使用 ScriptableObject 来保存数据,但可以使用在开发期间设置的 ScriptableObject 资源中保存的数据。

从 Editor 工具作为资源保存到 ScriptableObject 的数据将写入磁盘,因此将在会话之间一直保留。

简单来说就是ScriptableObject可以保存数据(在本地),即实现存档。而且它无需挂载在任何节点上,就可以生效。

2.UI的搭建

 

 做好了以后大概是这个样子,这里有一个组件十分的好用,叫Grid Layout Group

它可以让网格在背包中自适应的排列,并且可以控制间距和大小

3.创建物品类和背包类并利用ScriptableObject存储信息

这里放上代码

物品类

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. [CreateAssetMenu(fileName ="New Item",menuName ="Inventory/New Item")]
  5. public class Item : ScriptableObject
  6. {
  7. public string itemName;
  8. public Sprite itemImage;
  9. public int itemNum;
  10. [TextArea]
  11. public string itemInFo;
  12. public bool isEquip;
  13. }

背包类

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. [CreateAssetMenu(fileName = "New Inventory", menuName = "Inventory/New Inventory")]
  5. public class Inventory : ScriptableObject
  6. {
  7. public List<Item> itemList = new List<Item>();
  8. }

这里也利用了定制特性来对AssetMenu进行拓展可以直接创建物品和背包

效果如下

 4.拾取物品在背包中显示

首先我们要给背包中的每个网格创建一个脚本,便于我们记录每个网格储存的物品的信息

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.UI;
  5. public class Slot : MonoBehaviour
  6. {
  7. public int slotID;
  8. public Item slotItem;
  9. public Image slotImage;
  10. public Text slotNum;
  11. public GameObject itemInSlot;
  12. public string slotinfo;
  13. public void ItemOnClicked()
  14. {
  15. InventourManager.UpdateItemInfo(slotinfo);
  16. }
  17. public void SetuupSlot(Item item)
  18. {
  19. if(item==null)
  20. {
  21. itemInSlot.SetActive(false);
  22. return;
  23. }
  24. slotImage.sprite = item.itemImage;
  25. slotNum.text = item.itemNum.ToString();
  26. slotinfo = item.itemInFo;
  27. }
  28. }

接着创建一个单例InventourManager,便于其他脚本来访问这个脚本中的方法,同时也是用来管理我们的背包

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.UI;
  5. public class InventourManager : MonoBehaviour
  6. {
  7. static InventourManager instance;
  8. public Inventory myBag;
  9. public GameObject slotGrid;
  10. // public Slot slotPrefab;
  11. public GameObject emptySlot;
  12. public Text itemInfo;
  13. public List<GameObject> slots = new List<GameObject>();
  14. private void Awake()
  15. {
  16. if(instance!=null)
  17. {
  18. Destroy(this);
  19. }
  20. instance = this;
  21. }
  22. public void OnEnable()
  23. {
  24. RefreshItem();//刷新背包
  25. instance.itemInfo.text = "";//对文字进行初始化
  26. }
  27. public static void UpdateItemInfo(string Itemdescrip)
  28. {
  29. instance.itemInfo.text = Itemdescrip;//上传物品信息
  30. }
  31. public static void RefreshItem()//刷新背包
  32. {
  33. for(int i=0;i<instance.slotGrid.transform.childCount;i++)
  34. {
  35. if (instance.slotGrid.transform.childCount == 0)
  36. break;
  37. Destroy(instance.slotGrid.transform.GetChild(i).gameObject);
  38. instance.slots.Clear();
  39. }
  40. for(int i=0;i<instance.myBag.itemList.Count;i++)
  41. {
  42. instance.slots.Add(Instantiate(instance.emptySlot));
  43. instance.slots[i].transform.SetParent(instance.slotGrid.transform);
  44. instance.slots[i].GetComponent<Slot>().slotID = i;
  45. instance.slots[i].GetComponent<Slot>().SetuupSlot(instance.myBag.itemList[i]);
  46. }
  47. }
  48. }

这然后就要在地图上添加物体并给他们脚本,实现每次拾取物品都会在背包中显示,并且点击物品会显示他们的信息

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class itemOnWorld : MonoBehaviour
  5. {
  6. public Item thisItem;
  7. public Inventory playerBag;
  8. private void OnTriggerEnter2D(Collider2D other)
  9. {
  10. if(other.gameObject.CompareTag("Player"))
  11. {
  12. Destroy(gameObject);
  13. AddNewItem();
  14. }
  15. }
  16. public void AddNewItem()
  17. {
  18. if(!playerBag.itemList.Contains(thisItem))
  19. {
  20. // playerBag.itemList.Add(thisItem);
  21. for(int i=0;i<playerBag.itemList.Count;i++)
  22. {
  23. if(playerBag.itemList[i]==null)
  24. {
  25. playerBag.itemList[i] = thisItem;
  26. break;
  27. }
  28. }
  29. }
  30. else
  31. {
  32. thisItem.itemNum++;
  33. }
  34. InventourManager.RefreshItem();
  35. }
  36. }

5.物品的拖拽,交换位置以及背包的拖拽

这里注意要在Slot上的Item上添加Canvas Group组件,这可以用来在UI面板上进行射线检测,检测当前鼠标位置下的网格是否有物体

 要使用鼠标的拖拽相关的内容,要引入一个新的库,using UnityEngine.EventSystems;同时使用鼠标拖拽相关的接口,IBeginDragHandler,IDragHandler,IEndDragHandler分别对应开始拖拽,拖拽中,拖拽结束。

要注意开始拖拽以后要将CanvasGroup中的blocksRaycasts关掉,以便于获取格子上的信息,否则就只能获取当前鼠标拖拽物品的信息,物品就会挡住后面的各自的信息,然后在拖拽结束也要将它再次打开以便于再次进行拖拽

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.EventSystems;
  5. public class itemOnDrag : MonoBehaviour,IBeginDragHandler,IDragHandler,IEndDragHandler
  6. {
  7. public Transform originalParent;
  8. public Inventory myBag;
  9. int currentID;
  10. public void OnBeginDrag(PointerEventData eventData)
  11. {
  12. originalParent = transform.parent;
  13. currentID = originalParent.GetComponent<Slot>().slotID;
  14. transform.SetParent(transform.parent.parent);
  15. transform.position = eventData.position;
  16. GetComponent<CanvasGroup>().blocksRaycasts = false;
  17. }
  18. public void OnDrag(PointerEventData eventData)
  19. {
  20. transform.position = eventData.position;
  21. // Debug.Log(eventData.pointerCurrentRaycast.gameObject.name);
  22. }
  23. public void OnEndDrag(PointerEventData eventData)
  24. {
  25. if(eventData.pointerCurrentRaycast.gameObject!=null)
  26. {
  27. if (eventData.pointerCurrentRaycast.gameObject.name == "Item Image")
  28. {
  29. transform.SetParent(eventData.pointerCurrentRaycast.gameObject.transform.parent.parent);
  30. transform.position = eventData.pointerCurrentRaycast.gameObject.transform.parent.parent.position;
  31. var temp = myBag.itemList[currentID];
  32. myBag.itemList[currentID] = myBag.itemList[eventData.pointerCurrentRaycast.gameObject.GetComponentInParent<Slot>().slotID];
  33. myBag.itemList[eventData.pointerCurrentRaycast.gameObject.GetComponentInParent<Slot>().slotID] = temp;
  34. eventData.pointerCurrentRaycast.gameObject.transform.parent.position = originalParent.position;
  35. eventData.pointerCurrentRaycast.gameObject.transform.parent.SetParent(originalParent);
  36. GetComponent<CanvasGroup>().blocksRaycasts = true;
  37. return;
  38. }
  39. if (eventData.pointerCurrentRaycast.gameObject.name == "slot(Clone)")
  40. {
  41. transform.SetParent(eventData.pointerCurrentRaycast.gameObject.transform);
  42. transform.position = eventData.pointerCurrentRaycast.gameObject.transform.position;
  43. myBag.itemList[eventData.pointerCurrentRaycast.gameObject.GetComponentInParent<Slot>().slotID] = myBag.itemList[currentID];
  44. if (eventData.pointerCurrentRaycast.gameObject.GetComponent<Slot>().slotID != currentID)
  45. myBag.itemList[currentID] = null;
  46. GetComponent<CanvasGroup>().blocksRaycasts = true;
  47. return;
  48. }
  49. }
  50. transform.SetParent(originalParent);
  51. transform.position = originalParent.position;
  52. GetComponent<CanvasGroup>().blocksRaycasts = true;
  53. }
  54. }

这个代码实现了物品交换位置以及在非法的位置下自动回到原来的位置。

下面是背包的拖拽

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.EventSystems;
  5. public class MoveBag : MonoBehaviour, IDragHandler
  6. {
  7. RectTransform currentRect;
  8. private void Awake()
  9. {
  10. currentRect = GetComponent<RectTransform>();
  11. }
  12. public void OnDrag(PointerEventData eventData)
  13. {
  14. currentRect.anchoredPosition += eventData.delta;
  15. }
  16. }

6.数据的本地存储

当我们的游戏生成以后,ScriptableObject 是不能被记录更改的,就要利用文件相关的知识来存储背包的数据。建立一个GameSaveManager脚用来保存和加载数据。这里要引入两个新的库

using System.IO;文件相关的
using System.Runtime.Serialization.Formatters.Binary;序列化的二进制的格式

同时ScriptableObject中的信息是不能直接转化为二进制形式存储到文件中,因此要医用JsonUtility的ToJson和FromJsonOverwrite将信息转化为json格式存储并可以通过FromJsonOverwrite重新写回到ScriptableObject中

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using System.IO;
  5. using System.Runtime.Serialization.Formatters.Binary;
  6. public class GameSaveManager : MonoBehaviour
  7. {
  8. public Inventory myInventory;
  9. public void SaveGame()
  10. {
  11. Debug.Log(Application.persistentDataPath);//获取游戏数据的文件夹
  12. if (!Directory.Exists(Application.persistentDataPath + "/game_SaveData"))
  13. {
  14. Directory.CreateDirectory(Application.persistentDataPath + "/game_SaveData");
  15. }
  16. BinaryFormatter formatter = new BinaryFormatter();//二进制转化
  17. FileStream file = File.Create(Application.persistentDataPath + "/game_SaveData/inventory.txt");//创建文件
  18. var json = JsonUtility.ToJson(myInventory);//将信息转化为json格式存储并可以通过FromJsonOverwrite重新写回到ScriptableObject
  19. formatter.Serialize(file, json);//以二进制的方法写到文件当中
  20. file.Close();
  21. }
  22. public void LoadGame()
  23. {
  24. BinaryFormatter bf = new BinaryFormatter();
  25. if(File.Exists(Application.persistentDataPath + "/game_SaveData/inventory.txt"))
  26. {
  27. FileStream file = File.Open(Application.persistentDataPath + "/game_SaveData/inventory.txt",FileMode.Open);
  28. JsonUtility.FromJsonOverwrite((string)bf.Deserialize(file), myInventory);
  29. file.Close();
  30. }
  31. }
  32. }

7.实例演示


声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/112496
推荐阅读
相关标签
  

闽ICP备14008679号