Unity 简单背包系统(ScriptableObject)

  • 实例演示:
  • 说明
  • 工作步骤开始
    • 地图场景搭建
    • 背包UI
    • 物品预制体
  • 脚本
    • 1.创建物品类,背包类
    • 2. 玩家控制代码
    • 3.物品信息脚本
    • 4.物品管理脚本
    • 5.碰撞物品脚本
    • 6.背包物品的移动
    • 7.背包的移动
  • 总结

实例演示:

说明

本项目是基于ScriptableObject做的背包系统,内容还算是简单,适合背包入门的人来学习。主要是来认知背包做的流程和大概功能。

ScriptableObject不了解的话就没必要看了,先去学习一下
该项目资源包供大家下载学习:点我下载❤。

  • 功能1:数据存储不丢失
  • 功能2:碰撞到物品可以添加到背包系统里。
  • 功能3:物品可以拖动换位置

缺点: 因为此项目只是简单的学习了解背包的功能,所以性能等方面很粗糙,相当的消耗性能,不建议大家使用,或者大家可以改良一番。

代码逻辑: 代码逻辑的话挺粗糙的。。。所以我建议大家看这篇文章把代码的注释看一下。强烈建议把资源包下载使用观看

工作步骤开始

地图场景搭建

这里我就不赘述了。。。。

背包UI

这里我就不说怎么创建该UI了,希望大家去下载我的资源包。

这里我说明一下Crid,他有个Grid Layout Group组件,让物品自适应排列

物品预制体


Slot的Image图片是物品栏的图片

Item是一个Button组件,删除Image组件,加上Canvas Group和Layout Element组件,并且将Layout Element的Lgnore Layout勾选上

ItemImage作为物品的显示
Number是Item原有的Text,并调整他的位置,在左下角显示数量

脚本

1.创建物品类,背包类

物品类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;[CreateAssetMenu(fileName = "New Item", menuName = "Inventory/New Item")]
public class Item : ScriptableObject
{public string itemName;//物品名字public Sprite ItemSprite;//物品的照片public int itemHeld=1;//物品的数量,默认是一个,因为拾取第一个后直接为1,再拾取就直接+1即可[TextArea]//使text可以富文本进行多行书写public string itemInfo;//物品的介绍描述}
//该脚本是一个物品的基础脚本

背包类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;[CreateAssetMenu(fileName = "New InventoryBag", menuName = "Inventory/New IventoryBag")]
public class InventoryBag : ScriptableObject
{/// <summary>/// 玩家背包,因为储存多个物品,所以是一个集合/// </summary>public List<Item> bagList=new List<Item>();
}
//基于这样的情况我们还可以进行添加商店背包等等.......

在该文件夹下我创建了两个物品,一个玩家背包

其中一个物品信息

玩家背包信息

2. 玩家控制代码

按下B键来打开关闭背包界面

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class PlayerMovement : MonoBehaviour
{Rigidbody2D rb;Collider2D coll;Animator anim;public GameObject Bag;//玩家的背包public bool isOpen;public float speed;Vector2 movement;private void Awake(){rb = GetComponent<Rigidbody2D>();coll = GetComponent<Collider2D>();anim = GetComponent<Animator>();}private void Update(){Movement();SwitchAnim();BagSet();}void Movement()//移动{movement.x = Input.GetAxisRaw("Horizontal");movement.y = Input.GetAxisRaw("Vertical");rb.MovePosition(rb.position + movement * speed * Time.deltaTime);}void SwitchAnim()//切换动画{if (movement != Vector2.zero)//保证Horizontal归0时,保留movment的值来切换idle动画的blend tree{anim.SetFloat("horizontal", movement.x);anim.SetFloat("vertical", movement.y);}anim.SetFloat("speed", movement.magnitude);//magnitude 也可以用 sqrMagnitude 具体可以参考Api 默认返回值永远>=0}//点击B键进行背包的打开void BagSet(){if (Input.GetKeyDown(KeyCode.B)){isOpen = !Bag.activeSelf;Bag.SetActive(isOpen);}}
}

3.物品信息脚本

该脚本挂在到预制体身上,并且赋值如下图

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;public class Slot : MonoBehaviour
{/// <summary>/// 装备栏的序号/// </summary>public int slotId;public Item slotItem;public Image slotIamge;//物品的图片public Text Num;//数量Textpublic string slotInfo;//物品信息public GameObject itemInSlot;//拖动的Itempublic void SlotOnClick()//注册物品点击事件{InventoryMannage.UpItemInfomation(slotInfo);//更新背包的描述信息栏的信息}//物品的信息初始化设置public void SetupSlot(Item item){//这里就是没有物品的话就禁用if (item == null){itemInSlot.SetActive(false);return;}slotIamge.sprite = item.ItemSprite;Num.text = item.itemHeld.ToString();slotInfo = item.itemInfo;}
}

4.物品管理脚本

该脚本挂在Canvas上面

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;public class InventoryMannage : MonoBehaviour
{private static InventoryMannage instance;//设置成单例模式public InventoryBag playerBag;//玩家背包public GameObject slotGrid;//装备栏//public Slot slotPrefab;public GameObject emptslot;//物品预制体public Text itemInfomation;//物品描述的textpublic List<GameObject> slots=new List<GameObject>();//因为直接在装备栏生成18个装备,用一个集合来存储,并标记序号private void Awake(){if(instance!=null)Destroy(this);instance = this;itemInfomation.text = "";}private void OnEnable(){RestItem();}//这里的代码是直接增加在装备栏里一个装备,但是无法进行拖拽。。。。。这里直接跳过不用看,因为用该方法的话要变的东西很多这算是一个别的思路吧/*public static void CreatNewItem(Item item){Slot newItem = Instantiate(instance.slotPrefab, instance.slotGrid.transform.position, Quaternion.identity);newItem.gameObject.transform.SetParent(instance.slotGrid.transform);newItem.slotItem = item;newItem.slotIamge.sprite = item.ItemSprite;newItem.Num.text = item.itemHeld.ToString();}*//// <summary>/// 每一次刷新背包里面的内容都要先删除原先所有的物品,然后再重新生成添加/// 因为这样的操作所以是性能大大的降低....../// 根据玩家背包的数量来进行遍历操作/// </summary>public static void RestItem(){//删除所有的for (int i = 0; i < instance.slotGrid.transform.childCount; i++){Destroy(instance.slotGrid.transform.GetChild(i).gameObject);instance.slots.Clear();}//添加for (int i = 0; i <instance.playerBag.bagList.Count; i++){//CreatNewItem(instance.playerBag.bagList[i]);instance.slots.Add(Instantiate(instance.emptslot));//物品集合添加物品instance.slots[i].transform.SetParent(instance.slotGrid.transform);//让物品成为物品栏得到子集instance.slots[i].GetComponent<Slot>().slotId = i;//物品栏的物品序号初始化instance.slots[i].GetComponent<Slot>().SetupSlot(instance.playerBag.bagList[i]);//物品信息的初始化}}/// <summary>/// 进行物品描述信息的赋值/// </summary>/// <param name="info"></param>public static void UpItemInfomation(string info){instance.itemInfomation.text = info;}
}


PlayerBag就是我们刚开始创建的背包。

5.碰撞物品脚本

这是挂在到游戏场景中物品的脚本

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;//该脚本绑定到游戏场景中的物品下
public class AddInventory : MonoBehaviour
{public Item thisItem;//给物品注册该物品是哪一类的物品public InventoryBag thisBag;//玩家背包private void OnTriggerEnter2D(Collider2D other){if (other.gameObject.CompareTag("Player")){AddItem();Destroy(gameObject);}}void AddItem(){if (!thisBag.bagList.Contains(thisItem)){//thisBag.bagList.Add(thisItem);//遍历背包,如果为null的话就设置成该物品,因为我们直接给装备栏添加了18个物品,只是初始化都是nullfor (int i = 0; i < thisBag.bagList.Count; i++){if (thisBag.bagList[i] == null){thisBag.bagList[i] = thisItem;break;}}}else{thisItem.itemHeld += 1;//如果存在该物品,就将数量+1;}InventoryMannage.RestItem();//更新背包的数据}
}


这里的ThisItem需要自己做出你游戏场景中对应的Item。并将碰撞器改为触发器
到这里我们就已经实现了游戏背包的核心功能,可以添加到背包里面了并存储

6.背包物品的移动

该脚本挂在到Item身上,并将Item的Button组件注册事件,上面的Slot脚本里面有

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UIElements;/// <summary>
/// 为了实现点击物品跟随鼠标移动,所以它添加上IBeginDragHandler,IDragHandler,IEndDragHandler三个事件
/// 注意添加这三个接口时添加头文件:using UnityEngine.EventSystems;
/// </summary>
public class ItemOnDrag : MonoBehaviour,IBeginDragHandler,IDragHandler,IEndDragHandler
{/// <summary>/// 记录原来的父级位置,因为可能进行物品交换位置,所以记录原先的父级/// 因为我们的层级关系是有两层的,该代码是在Item身上。/// Item层级关系:Slot(Clone)->Item,我们改变Item的位置所以找到Slot(Clone)/// </summary>public Transform startParent;public InventoryBag PlayerBag;//玩家的背包private int startID;//当前初始的序号,点击物品的刚开始序号,因为交换挪位置,所以也要记录下来public void OnBeginDrag(PointerEventData eventData){startParent = transform.parent;startID = startParent.GetComponent<Slot>().slotId;transform.SetParent(transform.parent.parent);//我们点击物品后我们要脱离原来的父物体,这样物品就不会被装备栏覆盖掉不显示了transform.position = eventData.position;//物品跟随鼠标的位置GetComponent<CanvasGroup>().blocksRaycasts = false;//防止拖拽的物体挡住鼠标的射线检测}public void OnDrag(PointerEventData eventData){transform.position = eventData.position;//Debug.Log(eventData.pointerCurrentRaycast.gameObject.name);}public void OnEndDrag(PointerEventData eventData){if (eventData.pointerCurrentRaycast.gameObject != null)//这里判断鼠标拖动物体是否到场景中,如果在场景的话就直接返回原来的位置,不进行判断的话会报错{if (eventData.pointerCurrentRaycast.gameObject.name == "ItemImage")//判断下面物体的名字是:ItemImage 那么互换位置{transform.SetParent(eventData.pointerCurrentRaycast.gameObject.transform.parent.parent);//这里两个parent是因为第一时间检测到的是ItemImage,我们要找的是Slot,所以是两个parenttransform.position = eventData.pointerCurrentRaycast.gameObject.transform.parent.parent.position;//bagList的物品存储位置改变,我们要改变背包里面的顺序,这样才不会挪动了位置不会保存,下面这里两个Item的进行调换var temp = PlayerBag.bagList[startID];PlayerBag.bagList[startID] =PlayerBag.bagList[eventData.pointerCurrentRaycast.gameObject.GetComponentInParent<Slot>().slotId];PlayerBag.bagList[eventData.pointerCurrentRaycast.gameObject.GetComponentInParent<Slot>().slotId] = temp;//进行交换位置,改变父子级别eventData.pointerCurrentRaycast.gameObject.transform.parent.position = startParent.position;eventData.pointerCurrentRaycast.gameObject.transform.parent.SetParent(startParent);GetComponent<CanvasGroup>().blocksRaycasts = true;//当操作进行完后要把他设置成true,可以进行以后的射线检测return;}//下面的逻辑和上面的大致相同//这里是判断移动的位置是空位置的话就简单地移动位置即可,不用交换的操作if (eventData.pointerCurrentRaycast.gameObject.name == "Slot(Clone)"){transform.SetParent(eventData.pointerCurrentRaycast.gameObject.transform);transform.position = eventData.pointerCurrentRaycast.gameObject.transform.position;PlayerBag.bagList[eventData.pointerCurrentRaycast.gameObject.GetComponentInParent<Slot>().slotId] = PlayerBag.bagList[startID];//这里的判断是当你移动该物品,最后又移动到原来的位置,如果移动两者前后序号一样就不用操作,如果不一样就设置原来的为空if(eventData.pointerCurrentRaycast.gameObject.GetComponent<Slot>().slotId!=startID)PlayerBag.bagList[startID] = null;GetComponent<CanvasGroup>().blocksRaycasts = true;return;}}//其他任何位置都设置成原来的位置transform.SetParent(startParent);transform.position = startParent.position;GetComponent<CanvasGroup>().blocksRaycasts = true;}
}

7.背包的移动

这里挂载到Bag上面

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;/// <summary>
/// 因为实现背包的移动,所以增加IDragHandler事件
/// 移动背包移动的是他的中心点,这样看起来平滑并且不会一下子移动很远
/// </summary>
public class MoveBag : MonoBehaviour,IDragHandler
{private RectTransform startRect;private void Awake(){startRect = GetComponent<RectTransform>();}public void OnDrag(PointerEventData eventData){startRect.anchoredPosition += eventData.delta;//移动中心点}
}

总结

到现在我们已经把背包系统做好了,核心功能还有拖拽工作已经做好了。
但是性能的消耗大家应该也很清楚了就希望大家了解后创造出更优秀的背包系统

Unity 简单背包系统(ScriptableObject)相关推荐

  1. unity简单的背包系统笔记(有视频讲解)

    视频参考:Unity教程:背包系统:04:显示在背包里(C# code) InventoryManager_哔哩哔哩_bilibili 素材下载: https://pan.baidu.com/s/1o ...

  2. Unity 简单示例代码和向导/Unity Aplication Block

    Unity 简单示例代码和向导 关于Unity 的说明和下载地址,请访问[微软控制反转和依赖注入容器Unity 1.0发布] http://forum.entlib.com/Default.aspx? ...

  3. unity简单技能系统

    unity简单技能系统 类类型概览 CharacterSkillManager      角色技能管理器 挂载在角色 持有SkillData与释放器 通过释放器进行技能释放 SkillDeployer ...

  4. unity简单计时器_简单且悦目的Pomodoro计时器

    unity简单计时器 拟态 (pomotroid) Pomotroid is a simple and configurable Pomodoro timer. It aims to provide ...

  5. Unity简单2D游戏开发

    Unity简单2D游戏开发 前言: 近日比较无聊,在b站找了一个up主,跟着他的教程来做游戏--开发一个简单的2D游戏 用 Tilemap 绘制场景 新建一个2D项目,在Unity Asset Sto ...

  6. unity简单复刻无敌破坏王

    使用unity简单复刻无敌破坏王总结 国庆突发奇想想简单复刻一下无敌破坏王,这里写一些总结,希望也能对大家有帮助 游戏截图: 基本思路: 时间有限,也只是简单复刻,所以只实现破坏方块的功能,画面算是广 ...

  7. 【贪玩巴斯】Unity3D初学圣经(一)——学习要求 Unity简单介绍 游戏引擎介绍 课程体系介绍 「1-1 到 1-4 」—— 2021年12月9日

    Unity3D初学圣经 一 --学习要求 & Unity简单介绍 & 游戏引擎介绍 & 课程体系介绍 本文对应视频P1 1-1 到P2 1-4 1.学习要求 2.Unity简单 ...

  8. Unity 简单实现子弹射击

    Unity 简单实现子弹射击 一.具体步骤: 1.  创建预制体:Assets >> Create >> Prefab 并命名,添加碰撞(Box Collider 等)并勾选 ...

  9. Unity简单商城系统,用SQLite数据库保存/加载数据

    Unity简单商城系统案例 流程 最后效果展示 1. 创建项目并导入SQLite需要的dll文件 2. 创建数据库表(玩家表和商店表) 3. Singleton 单例脚本 4. 封装SQLite数据库 ...

最新文章

  1. VC++读取txt文件指针的变化
  2. 可视化神经网络的网络结构并保存
  3. XIII Open Grodno SU Championship
  4. kdj值应用口诀_KDJ买卖绝学!记住这些操作 精准判断quot;顶部和底部quot; 让你远离亏损...
  5. VHDL硬件描述语言
  6. .net core 集成 autofac.
  7. 斯坦福华人教授:声波、光波,其实都是RNN!机器学习模型对应
  8. jvm类加载机制是怎么样的
  9. 一步步学习SPD2010--第九章节--使用可重用工作流和工作流表单(1)--创建和使用可重用工作流...
  10. 三大应用需求:5G信道编码技术取得突破
  11. 单独使用ckfinder选择图片
  12. 学python能赚钱吗-自学Python三个月能赚钱吗?
  13. 第四篇 NumPy基础:数组和⽮量计算
  14. Android TextView drawableLeft 在代码中实现
  15. xampp错误: mysql 非正常关闭._mysql数据库DBA实用技巧--为你的数据库开启Innodb监控...
  16. mysql vs0215_0215 docker环境
  17. 热血江湖游戏中断开服务器,为什么最近老是一进去游戏就提示与服务器断开 – 手机爱问...
  18. 拆解CRM头牌“销售易” | 如何做好客户关系管理?
  19. Photoshop渐变工具+羽化使用
  20. python界面编程实例_Python GUI编程 文本弹窗的实例

热门文章

  1. wordpress安装PHP Warning: Use of undefined constant DB_USER - assumed 'DB_USER' 问题解决
  2. 在使用线程池等会缓存线程的组件情况下传递ThreadLocal
  3. DiagramDesigner 画流程图软件使用心得
  4. springboot获取文件路径
  5. 怎样让你的客户服务更高效?
  6. 狐狸与兔子 java,兔子夫妇和狐狸夫妇的爱情
  7. 《西河大鼓——霸桥挑袍》(唱词文本)
  8. Spring中使用RestTemplate发送Http请求
  9. C++17 filesystem
  10. qDebug输出QString