无题
Unity背包系统
个人笔记,如误人子弟,概不负责
准备工作
中间踩过的坑:
1,monobehaviour会重新定义创建物体的方式,如果要创建单例模式,不能随意使用new关键字,不然会出现一些意想不到的错误。
2,没有挂载monobehaviour的脚本挂载到场景中的物体上的时候,Unity会发生报错
3,Resource.Load读取的是相对路径,可以不用Resource/folder1/folder2,直接folder1/folder2即可
4,注意预制体是否已经apply,如果不小心删除了预制体可以在回收站当中找到
5,json有可能会读取它父对象的数据,比如monobehaviour
在游戏内部布置一个ui界面
我们需要实现的功能
1,建立ui的大概样式
2,通过[CreateAssetMenu(menuName = “Panel/PackageTable” , fileName = “PackageTable”)]类似这样的属性来在asset栏目通过右键来创建文件,这个文件用来存入静态数据
3,通过jason将背包物体信息转换成字符串保存在PlayerPref当中来对背包中物体进行存档,并且在下一次打开游戏的时候来读取信息,这是动态数据
如图,我们可以发现动态数据的相比静态数据的成员少了很多
因为动态数据一般是保存游戏中用于交互的信息的,比如物体的数量,物体是否是新的等等,这些信息随时都可能发生变化。动态数据往往挂载在已经实例化的对象上
而静态数据则是用来保存物体的描述,名字,图标等等,这些信息适合在图鉴这种地方进行调用,占用的内存最少,一般不会进行实例化
不在Awake当中调用的单例模式:
保存功能
调用了ToJson方法将这个对象保存成为字符串信息,主要是将保存了动态信息的items列表进行序列化。
保存的信息被保存到了PlayerPrefs当中,这样可以在重新开始游戏的时候重新调用
读取功能
这个函数用来调取所有的动态数据信息,注意PlayerPrefabs.HasKey方法可以检测存档当中是否有这个文件
如果没有这个文件,那么重新进行序列化
如果这个数据集是空(items == null || items.Count == 0) && !(hasKey) 的那么,那么重新创建一个items。
4,通过菜单栏来创建测试数据,包括静态数据和动态数据集
这个方法用来加载静态数据,之后我们可以在unity编辑器的菜单栏中调用,直接读取了PackageTable当中的公用列表
这两个方法用来加载动态数据,与读取静态数据不同的是,我们使用了PackageLocal里面的成员方法来加载创建保存信息
5,建立BasePanel脚本来作为所有Panel UI的父类,里面包括了closePanel方法和OpenPanel方法
注意场景当中的ui物体也要挂载这个脚本,这个脚本挂载了monobehaviour
6,通过UIManager单例模式脚本来控制所有Panel UI系统
1设置单例模式
2定义UIConst,这个类将所有的ui panel作为静态成员变量形式进行保存,注意之后每增加一个UIPanel都要在这里进行添加定义
3,定义三个字典
注意,我们如果要正常使用这个脚本来管理UI Panel,就需要将UI Panel保存成预制体,将其放在Resource文件夹的下面。
这三个字典:其中pathDict记录了你所有的panel在Resource文件夹下面的相对路径
其中prefabDict记录了所有在Resource目录的预制体
其中panelDict记录了你所有以及打开的ui panel
4,三个字典的初始化
将路径作为字符串索引保存到pathDict当中,注意当你想要添加UI panel的时候你需要在这里进行设置
5设置挂载的父物体
具体来说就是在场景的所有物体当中寻找到canvas组件,将这个canvas作为所有panel ui的父物体
我们会先后创建
basePanel代表将要打开(生成)的ui panel
创建了path作为这个panel的预制体路径
创建了空物体panelPrefab作为这个预制体的容器
在这第一个if语句当中,通过name(这个name一般就是UIconst里面的字符串)和out操作符来获取panelDict字典当中保存的panel,如果发生了重名现象,就说明已经打开这个panel了。
同理,之后的都是判断是否有路径、预制体,一般默认情况下都是有的
如果没有路径,就直接进行报错
如果没有预制体,脚本将会尝试根据路径来获取prefab
之后调用Instantiate来在场景canvas画布下创建这个“panelPrefab”预制体对象,一般来说到这一步这个预制体都已经有了
之后获取上面的basePanel脚本,将其保存在panel变量当中
之后在panelDict中添加这个panel basePanel,表示场景中已经存在一个这个panel了
之后返回panel(实际就是basePanel)
关闭面板
这个脚本会尝试从panelDict当中通过传入的UIconst来判断是否已经存在了这个panel
如果没有,那就退出
如果有,那么就执行这个panel(basepanel)的closePanel语法
之后的脚本存在大量的互相引用以及对静态数据和动态数据的应用
7,在背包Panel挂载脚本PackagePanel,里面控制了背包的开关(要引用到basePanel中的panel方法),并且记录了所有可以进行互动的ui组件,记录了它们的相对路径。并且创建两种状态,分别对应普通状态和删除状态。
0界面
1我们创建了这个挂载了PackagePanel脚本的物体下所有的子物件的引用,为什么是Transform,因为Transform组件比较特殊,可以从Transform中向上获取到gameObject父物体
2在初始化的过程当中,我们会调用上面的两个函数,其中第一个函数负责给所有的Transform赋值所有子物件的相对路径
3之后我们初始化子物件当中所有的button,并且给它们建立函数
目前这个脚本中真正使用到的button只有这些
4实现界面的关闭
我们直接从UIManager当中进行关闭
5进行界面的刷新
我们会从uipanel中间的scrollview当中获取里面的scrollrect的内容。
先将里面的物体全部清空
之后遍历GameManager当中保存的所有localdata(实际上可以在里面定义排序的函数)
之后再scrollContent里面生成我们的cell物体的坐标
我们从这个cell物体上面获取它的PackageCell脚本,并且调用里面的refresh方法,这个fresh需要在每层循环当中一个一个地传入(排序过后的)localData,以及这个panel本身
所以这个fresh实际上是先fresh了整体的布局,之后再调用每一个生成的cell物体下的fresh,是两层fresh
6进行页面的状态控制
7进行物品的丢弃(删除)和选中
在默认的情况下,我们不会进入删除模式
但是如果我们点击了删除按钮,那么就会切换进入删除模式
(我们在点击删除按钮之后还会激活调用删除面板ui)
前置工作
删除模式下默认是多选模式,这个列表保存了在删除模式下你所有选中的对象的uid
这个函数处理了删除选中的逻辑,在cell的鼠标点击回调函数中,如果if判断delete模式的逻辑通过,那么就会调用这个函数
给在删除模式下选中的物体上特效
在cell上面的鼠标点击回调函数中进行检测,如果是删除模式,那么就将其加入到删除列表当中
如果是在游戏画面的话,确认删除和取消删除的按钮是长这样的
最终的删除在gameManager当中解决
8选中的时候刷新PackageDetail
前置工作
与删除模式不同的是,普通模式下选择是单选,只保存当前选中对象的uid,每次uid重新赋值之后,都会调用refreshdetail函数
8,在背包物品当中挂载脚本PackageCell,里面控制了PackageCell的逻辑,记录了所有子类下可互动物体的相对路径,加入了鼠标点击的回调接口,回调接口必须要包含实现它的函数
大多数逻辑之前已经讲过了,剩下的都是初始化
9,在物品详情界面挂载脚本PackageDetail,当玩家鼠标选择了某一个物体,就会自动调用这个物体cell上面的鼠标点击回调函数,这个回调函数包含了更新packageDetail信息的方法,将PackageCell当中存储的PackageLocal动态信息中的key值id来获取PackageTable下的静态信息然后传入PackageDetail,物体详情页面下同样有许多其它物体的路径引用
测试代码
初始化
其余的代码(上面已经黏贴过了)
10,GameManager用来管理基础的操作逻辑
将GameManager创建成单例模式,并且放置在游戏场景当中
之后在Start函数当中,调用UIManager的OpenPanel来打开PackagePanel
在gamemanager当中对背包物品进行排序
gamemanager当中获取id和uid的方法
gamemanager当中获取全部PackageLocalData动态数据的方法,一个是排序,一个是直接
gamemanager当中获取静态数据的方法