超级详细的游戏常见系统之背包系统制作以及优化

(一)基本知识点
拼面板:ugui
面板样子如图
在这里插入图片描述

数据来源:json配置表 ,file读取文件
(二)需求分析
1.背包中显示玩家拥有的物品信息
2.需要显示信息就要读取数据,数据来源(配置表:json,玩家信息:文件读取,json)
3.获取数据更新到面板上,处理面板逻辑:ugui,逻辑处理
(三)前期准备
(1).拼面板:注意ui命名最好唯一,主要是scrollow view和content下加grid layout组件+content size fitter:grid layout用来拍格子,content size fitter:用来动态控制content大小,让其随格子的多少而变化大小。注意,这里的道具,装备,宝石,是用toggle做的,
让显示多少内容是和viewpoint的mask有关:
在这里插入图片描述
去掉mask会变成这样
在这里插入图片描述

(2).数据准备:道具信息数据设计和读取,玩家数据信息设计存储和读取。具体数据准备步骤
*************************************准备道具信息
1.道具装备信息,用表格如图
在这里插入图片描述
2.excel转成json如图
在这里插入图片描述
可以利用在线转换工具:json在线转换工具
然后再放到在线工具中格式化校验一下,如图
在这里插入图片描述
3.让json可以在程序中使用,就要存到一个list中,而且把一些字符串格式的改成int型如id,type,price,如图
在这里插入图片描述
4.把格式化校验后的json,放到resources文件夹下的文本文件中,把itemsinfo.txt的格式改成utf-8的形式,不改txt格式,读取会出问题。
5.使用json数据,创建一个脚本gamedatamgr.cs,管理器是单例模式
代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameDataMgr
{
	
    private static GameDataMgr _instance;
    public static GameDataMgr Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new GameDataMgr();

            }
            return _instance;
        }
    }

    /// <summary>
    /// 加载资源里的json文件
    /// </summary>
    public void Init()
    {
        string info = Resources.Load<TextAsset>("Json/itemInfo").text;
        Debug.Log(info);
        //根据json文件,解析成对应的数据结构,并存储
        Items items = JsonUtility.FromJson<Items>(info);
        Debug.Log(items.info.Count);
    }
    
}

/// <summary>
/// 首先要有对应的数据结构去装这个json里的数据,这个是单个item的数据结构
/// </summary>
[System.Serializable]//序列化之后才能被unity自带的json解析出来
public class Item
{
    //注意这里的命名要和json里对应的键一摸一样
    public int id;
    public string name;
    public string icon;
    public int type;
    public int price;
    public string tips;
}


/// <summary>
/// 用来装items的数据结构
/// </summary>
public class Items
{
    //这里的info取名和json中的一样
    public List<Item> info;
}

在项目入口处调试,能打印正确的json字符串和正确的道具信息个数,说明道具数据准备信息完成了。

6.再做一步优化,如果数据在list中,每次查找都要遍历,不方便,所以要把list里的数据放到字典中<int,item>这样可以通过id查找。然后再写一个通过id,从字典获取具体道具的方法,代码如下

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameDataMgr
{
    //最终用字典存储items的信息
    private Dictionary<int, Item> itemInfos = new Dictionary<int, Item>();
    private static GameDataMgr _instance;
    public static GameDataMgr Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new GameDataMgr();

            }
            return _instance;
        }
    }

    /// <summary>
    /// 加载资源里的json文件
    /// </summary>
    public void Init()
    {
        string info = Resources.Load<TextAsset>("Json/itemInfo").text;
        Debug.Log(info);
        //根据json文件,解析成对应的数据结构,并存储
        Items items = JsonUtility.FromJson<Items>(info);
        Debug.Log(items.info.Count);
        //将解析好的list添加进字典中
        for(int i = 0; i < items.info.Count; i++)
        {
            itemInfos.Add(items.info[i].id, items.info[i]);
        }
    }

    /// <summary>
    /// 通过字典里的id获取具体道具信息
    /// </summary>
    /// <param name="id">道具id</param>
    /// <returns></returns>
    public Item GetItemInfo(int id)
    {
        if (itemInfos.ContainsKey(id))
        {
            return itemInfos[id];
        }
        return null;
        
    }


}

/// <summary>
/// 首先要有对应的数据结构去装这个json里的数据,这个是单个item的数据结构
/// </summary>
[System.Serializable]//序列化之后才能被unity自带的json解析出来
public class Item
{
    //注意这里的命名要和json里对应的键一摸一样
    public int id;
    public string name;
    public string icon;
    public int type;
    public int price;
    public string tips;
}


/// <summary>
/// 用来装items的数据结构
/// </summary>
public class Items
{
    //这里的info取名和json中的一样
    public List<Item> info;
}

准备数据的核心思想:数据和程序逻辑分离开,只要换json文件就行了。而且这样玩家那里的数据,不用存储具体的道具信息,只需存道具的id,和个数就可以了。这样玩家存储的数据就会减少。
*************************************准备玩家信息(暂时都写在GameDataMgr文件中)
1.初步设定玩家的数据结构包含哪些,简单的方法,根据面板上的显示,决定玩家数据需要准备什么,初步设定玩家的数据结构player是,名字,等级,金币,宝石,能量,道具,装备,碎片(宝石)如代码所示:

/// <summary>
/// 玩家信息数据结构
/// </summary>
public class player
{
    public string name;
    public int lev;
    public int money;
    public int gem;
    public int pro;
    public List<ItemInfo> items;
    public List<ItemInfo> equips;
    public List<ItemInfo> gems;
    
}

2.设计这些玩家数据的数据结构,玩家的道具只需包含id和个数即可,所以玩家的道具信息数据结构以及后面的装备,碎片的数据结构是,如下代码:

/// <summary>
/// 玩家持有的道具信息的数据结构
/// </summary>
[System.Serializable]//为的是unity自带json读取时能够识别,要序列化的是json里的数据,而不是容器list
public class ItemInfo
{
    int id;
    int num;
}

3.用本地文件,玩家信息的保存和读取
具体步骤:
初始化时:
(1)如果有这个文件,就从里面读取玩家信息
1.当有这个文件了,就读取这个文件的所有字节数组
2.转成json字符串
3.通过fromjson还原player对象
(2)如果没有这个文件,就创建这个文件,将初始化的玩家信息写入该文件中
1.player类的构造函数先赋值一些基本信息和道具信息
2.init()中然后把player对象的信息转成json字符串
3.然后json字符串转成字节数组,存入文件中
(3)把保存玩家信息的代码提取成一个方法SavePlayerInfo(),方便更新玩家数据
整段代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;

public class GameDataMgr
{
    //用来存储玩家信息文件的路径
    private static string playerInfo_url = Application.persistentDataPath + "/PlayerInfo.txt";
    //玩家对象
    public static player playerInfo;
 
    //最终用字典存储items的信息
    private Dictionary<int, Item> itemInfos = new Dictionary<int, Item>();
    private static GameDataMgr _instance;
    public static GameDataMgr Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new GameDataMgr();

            }
            return _instance;
        }
    }

    /// <summary>
    /// 初始化道具信息和玩家信息
    /// </summary>
    public void Init()
    {
        string info = Resources.Load<TextAsset>("Json/itemInfo").text;
        Debug.Log(info);
        //根据json文件,解析成对应的数据结构,并存储
        Items items = JsonUtility.FromJson<Items>(info);
        Debug.Log(items.info.Count);
        //将解析好的list添加进字典中
        for(int i = 0; i < items.info.Count; i++)
        {
            itemInfos.Add(items.info[i].id, items.info[i]);
        }

        //从本地文件读取玩家信息,如果没有就创建
        if (File.Exists(playerInfo_url))
        {
            //读取文件的所有字节数组,转换成字符串json,然后还原成player对象
            byte[] bytes = File.ReadAllBytes(playerInfo_url);
            string json = System.Text.Encoding.UTF8.GetString(bytes);
            playerInfo = JsonUtility.FromJson<player>(json);
            //测试有没有读对,打印下玩家名字
            Debug.Log(playerInfo.name);
            
        }
        else
        {
            //没有这个文件,就先创建player对象(对象的构造函数会给玩家信息赋值一个初始值,然后转成json字符串,通过字节数组存入文件中
            playerInfo = new player();
            SavePlayerInfo();
            //测试打印下这个路径,查看下文件有没有写入成功
            Debug.Log(playerInfo_url);

        }
        
    }

    /// <summary>
    /// 可以把存储json文件的部分提取成一个公共方法,方便更新玩家信息
    /// </summary>
    public static void SavePlayerInfo()
    {
        string json = JsonUtility.ToJson(playerInfo);
        File.WriteAllBytes(playerInfo_url, System.Text.Encoding.UTF8.GetBytes(json));
    }

    /// <summary>
    /// 通过字典里的id获取具体道具信息
    /// </summary>
    /// <param name="id">道具id</param>
    /// <returns></returns>
    public Item GetItemInfo(int id)
    {
        if (itemInfos.ContainsKey(id))
        {
            return itemInfos[id];
        }
        return null;
        
    }

    

}
/// <summary>
/// 玩家信息数据结构
/// </summary>
public class player
{
    public string name;
    public int lev;
    public int money;
    public int gem;
    public int pro;
    public List<ItemInfo> items;
    public List<ItemInfo> equips;
    public List<ItemInfo> gems;

    //给玩家构造函数初始化一个玩家信息
    public player(){
        name = "root";
        lev = 99;
        money = 999;
        gem = 0;
        pro = 99;
        items = new List<ItemInfo>() { new ItemInfo() {id= 2,num=1} };
        equips = new List<ItemInfo>() { new ItemInfo() { id = 1, num = 3 } };
        gems = new List<ItemInfo>() {  };

    }


   
}



/// <summary>
/// 玩家持有的道具信息的数据结构
/// </summary>
[System.Serializable]//为的是unity自带json读取时能够识别,要序列化的是json里的数据,而不是容器list
public class ItemInfo
{
    public int id;
    public int num;
}


/// <summary>
/// 首先要有对应的数据结构去装这个json里的数据,这个是单个item的数据结构
/// </summary>
[System.Serializable]//序列化之后才能被unity自带的json解析出来
public class Item
{
    //注意这里的命名要和json里对应的键一摸一样
    public int id;
    public string name;
    public string icon;
    public int type;
    public int price;
    public string tips;
}


/// <summary>
/// 用来装items的数据结构
/// </summary>
public class Items
{
    //这里的info取名和json中的一样
    public List<Item> info;
}

(四)基本逻辑实现
实现效果:
在这里插入图片描述
代码;
格子脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// 格子脚本
/// </summary>
public class ItemCell :BasePanel
{
    private ItemInfo itemInfo;

    public void InitItem(ItemInfo info)
    {
        this.itemInfo = info;
        Item itemData = GameDataMgr.GetInstance().GetItemInfo(info.id);//通过id找到该物品的详细信息
        Sprite icon= ResMgr.GetInstance().Load<Sprite>("Icon/" + itemData.icon);//找到该物品对应的图片
        GetUI<Image>("imgIcon").sprite = icon;//替换当前的图片
        GetUI<Text>("txtNum").text = info.num.ToString();//给物品数量赋值
    }

    
}

背包脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public enum E_Bag_Type
{
    Item,
    equip,
    Gem,
}

/// <summary>
/// 背包脚本
/// </summary>
public class BagPanel : BasePanel
{
    public Transform content;
    List<ItemCell> list = new List<ItemCell>();

    // Start is called before the first frame update
    void Start()
    {
        GetUI<Button>("Close_Img").onClick.AddListener(() =>{
            UIManager.GetInstance().ClosePanel("BagPanel");
        });

        GetUI<Toggle>("dg_img").onValueChanged.AddListener(ToggleValueChange);
        GetUI<Toggle>("zb_img").onValueChanged.AddListener(ToggleValueChange);
        GetUI<Toggle>("bs_img").onValueChanged.AddListener(ToggleValueChange);


    }

    public override void Show()
    {
        base.Show();
        ChangeType(E_Bag_Type.Item);
    }
    /// <summary>
    /// 根据点击不同的toggle,且换不同的数据
    /// </summary>
    /// <param name="value"></param>
    public  void ToggleValueChange(bool value)
    {
        if (GetUI<Toggle>("dg_img").isOn)
        {
            ChangeType(E_Bag_Type.Item);

        }else if (GetUI<Toggle>("zb_img").isOn)
        {
            ChangeType(E_Bag_Type.equip);
        }
        else if (GetUI<Toggle>("bs_img"))
        {
            ChangeType(E_Bag_Type.Gem);
        }
    }

    private void ChangeType(E_Bag_Type type)
    {
        List<ItemInfo> tempInfo = GameDataMgr.playerInfo.items;
        switch (type)
        {
            case E_Bag_Type.equip:
                tempInfo = GameDataMgr.playerInfo.equips;
                break;
            case E_Bag_Type.Gem:
                tempInfo = GameDataMgr.playerInfo.gems;
                break;

        }

        //先销毁,再清空
        for (int i = 0; i < list.Count; i++)
        {
            Destroy(list[i].gameObject);
        }

        list.Clear();
        for (int i = 0; i < tempInfo.Count; i++)
        {
            GameObject itemCellObj = ResMgr.GetInstance().Load<GameObject>("UI/ItemCell");//获取实例化好的格子物体
        
            ItemCell cell = itemCellObj.GetComponent<ItemCell>();//获取格子上的脚本
       
            cell.transform.SetParent(content);//设置父对象
            cell.transform.localScale = new Vector3(1, 1, 1);//设置大小
     
            cell.InitItem(tempInfo[i]);//给格子数据初始化
            list.Add(cell);//添加进list,方便下次显示
        }
    }
    


    
}

(五)进一步优化,当放到物品格子时,显示物品的详细信息
实现效果:
在这里插入图片描述

拼面板:

在这里插入图片描述
ps:为了让提示面板的其它部分不响应鼠标的点击事件,提示面板的所有射线检测关闭,不然鼠标放上去时,会有面板抖动现象
代码:
提示面板代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// 用来显示格子信息的提示面板
/// </summary>
public class TipsPanel : BasePanel

{
    public void InitTipsInfo(ItemInfo info)
    {
        Item itemData = GameDataMgr.GetInstance().GetItemInfo(info.id);//通过id找到该物品的详细信息
        Sprite icon = ResMgr.GetInstance().Load<Sprite>("Icon/" + itemData.icon);//找到该物品对应的图片
        GetUI<Image>("imgIcon").sprite = icon;//替换当前的图片
        GetUI<Text>("txtNum").text = "数量:"+info.num.ToString();//给物品数量赋值
        //得到物品的详细描述
        GetUI<Text>("txtTips").text = "描述:" + itemData.tips;
        //得到物品名字
        GetUI<Text>("txtName").text = "名字:" + itemData.name;
    }
}

格子的代码:主要是添加格子里的图片的eventtrigger,然后设置好鼠标进入,和移出事件,摆放提示面板位置为图片中心位置,然后初始化提示面板数据

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

/// <summary>
/// 格子脚本
/// </summary>
public class ItemCell :BasePanel
{
    private ItemInfo itemInfo;

    private void Start()
    {
        //给图片添加事件监听
        EventTrigger trigger=GetUI<Image>("imgIcon").gameObject.AddComponent<EventTrigger>();
        //声明一个鼠标进入事件,鼠标移出事件
        EventTrigger.Entry entry = new EventTrigger.Entry();
        entry.eventID = EventTriggerType.PointerEnter;
        entry.callback.AddListener(EnterItemCell);
        trigger.triggers.Add(entry);//还要添加进事件

        EventTrigger.Entry exit = new EventTrigger.Entry();
        exit.eventID = EventTriggerType.PointerExit;
        exit.callback.AddListener(ExitItemCell);
        trigger.triggers.Add(exit);//还要添加进事件
        


    }

    //给鼠标点击和移除事件编写对应逻辑(显示面板,将面板的位置移到图片的中心点,然后设置提示面板的数据)
    private void EnterItemCell(BaseEventData data)
    {
       
        UIManager.GetInstance().ShowPanel<TipsPanel>("TipsPanel", UI_Layer.top, (panel) =>
        {
            panel.transform.position = GetUI<Image>("imgIcon").gameObject.transform.position;
            panel.InitTipsInfo(itemInfo);
        });
    }

    private void ExitItemCell(BaseEventData data)
    {
       
        UIManager.GetInstance().ClosePanel("TipsPanel");
    }

    public void InitItem(ItemInfo info)
    {
        this.itemInfo = info;
        Item itemData = GameDataMgr.GetInstance().GetItemInfo(info.id);//通过id找到该物品的详细信息
        Sprite icon= ResMgr.GetInstance().Load<Sprite>("Icon/" + itemData.icon);//找到该物品对应的图片
        GetUI<Image>("imgIcon").sprite = icon;//替换当前的图片
        GetUI<Text>("txtNum").text = info.num.ToString();//给物品数量赋值

    }

    
}

(六)如果背包要装非常多的物体,如何优化?

  • 11
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Unity背包系统是一个游戏中常用的组件,用于管理玩家在游戏中所获得的道具。一个完整的背包系统通常由数据、逻辑和UI三部分组成。在设计过程中,我们首先进行UI设计,包括背包的标题、关闭键和背包内的格子容器。 背包系统的数据部分主要负责管理物品的信息,例如物品的名称、图片、数量等。逻辑部分负责实现将物品放置进背包、对背包内物品进行管理以及使用背包内物品等功能。实际上,当我们获取或移动物品时,我们会直接修改背包数据库中的物品信息。修改完成后,我们通过背包中的UI元素来获取背包数据库中相应索引的物品信息,并将物品栏中的UI信息更新为数据库中对应序号的物品的图片和数量。 通过将背包系统分解为数据、逻辑和UI三部分,我们可以有效地管理背包系统的复杂逻辑关系,并使代码更加清晰和易于维护。这种设计模式有助于提高游戏开发的效率和代码质量。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Unity游戏开发背包系统的实现](https://blog.csdn.net/float_freedom/article/details/126243888)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [Unity3D RPG实现 2 —— 背包系统](https://blog.csdn.net/weixin_43757333/article/details/123187025)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值