Unity开发弱数据联网游戏(背包系统)

在上一篇文章中我们已经实现了与云端数据库的数据交互了,接下来我们要做的是制作一个背包,让我们的数据交互看起来更加的直观,有大坑,请放心阅读。

https://blog.csdn.net/qq_40442319/article/details/125251058

1.我们先去找一个背包,我就直接找以前的代替了

至于背包的实现代码,我这里就不写了,毕竟没啥难度

2.写一个道具Item的属性的表格,包含id(对应之前数据交互中的道具序号号)、name、icon(图片的名字,主要是为了背包格子显示)、price、instro。

 3.将上面的表格转个json格式上传到云端,按照之前数据交互的方式实现就行了,因为以后可能用到,我这里就直接放本地了。

{
	"data": [{
			"id": 1,
			"name": "多蓝盾牌",
			"icon": "033_Buckler",
			"type": 6,
			"price": 15,
			"intro": "这是一个非常有用的多蓝盾牌"
		},
		{
			"id": 2,
			"name": "红爪手套",
			"icon": "139_Strygwyrs_Reaver",
			"type": 7,
			"price": 30,
			"intro": "这是一个非常有用的红爪手套"
		},
		{
			"id": 3,
			"name": "红药水",
			"icon": "2003_Health_Potion",
			"type": 10,
			"price": 45,
			"intro": "这是一个非常有用的红药水"
		},
		{
			"id": 4,
			"name": "蓝药水",
			"icon": "2004_Mana_Potion",
			"type": 10,
			"price": 60,
			"intro": "这是一个非常有用的蓝药水"
		},
		{
			"id": 5,
			"name": "侦察守卫",
			"icon": "1020_Glowing_Orb",
			"type": 10,
			"price": 75,
			"intro": "这是一个非常有用的侦察守卫"
		},
		{
			"id": 6,
			"name": "贝壳下衣",
			"icon": "161_Elementium_Woven",
			"type": 8,
			"price": 90,
			"intro": "这是一个非常有用的贝壳下衣"
		},
		{
			"id": 7,
			"name": "草鞋",
			"icon": "1001_Boots_of_Speed",
			"type": 4,
			"price": 105,
			"intro": "这是一个非常有用的草鞋"
		},
		{
			"id": 8,
			"name": "绿豆项链",
			"icon": "1006_Rejuvenation",
			"type": 5,
			"price": 120,
			"intro": "这是一个非常有用的绿豆项链"
		},
		{
			"id": 9,
			"name": "破布上衣",
			"icon": "1029_Cloth_Armour",
			"type": 1,
			"price": 135,
			"intro": "这是一个非常有用的破布上衣"
		},
		{
			"id": 10,
			"name": "魔抗下衣",
			"icon": "1033_Elementium_Threaded",
			"type": 8,
			"price": 150,
			"intro": "这是一个非常有用的魔抗下衣"
		},
		{
			"id": 11,
			"name": "暴击拳套",
			"icon": "1051_Brawlers_Gloves",
			"type": 7,
			"price": 165,
			"intro": "这是一个非常有用的暴击拳套"
		},
		{
			"id": 12,
			"name": "三速鞋",
			"icon": "3009_Boots_Teleportation",
			"type": 4,
			"price": 180,
			"intro": "这是一个非常有用的三速鞋"
		},
		{
			"id": 13,
			"name": "法穿鞋",
			"icon": "3020_Flamewalkers",
			"type": 4,
			"price": 195,
			"intro": "这是一个非常有用的法穿鞋"
		},
		{
			"id": 14,
			"name": "复活护甲",
			"icon": "3026_Guardian_Angel",
			"type": 1,
			"price": 210,
			"intro": "这是一个非常有用的复活护甲"
		},
		{
			"id": 15,
			"name": "振奋铠甲",
			"icon": "3065_Spirit_Visage",
			"type": 1,
			"price": 225,
			"intro": "这是一个非常有用的振奋铠甲"
		},
		{
			"id": 16,
			"name": "日炎铠甲",
			"icon": "3068_SunfireCape",
			"type": 1,
			"price": 240,
			"intro": "这是一个非常有用的日炎铠甲"
		},
		{
			"id": 17,
			"name": "狂徒铠甲",
			"icon": "3083_Warmog",
			"type": 1,
			"price": 255,
			"intro": "这是一个非常有用的狂徒铠甲"
		},
		{
			"id": 18,
			"name": "黄色盾牌",
			"icon": "3105_AegisOfTheLegion",
			"type": 6,
			"price": 270,
			"intro": "这是一个非常有用的黄色盾牌"
		},
		{
			"id": 19,
			"name": "急速长剑",
			"icon": "3170_MoonflairSpellblade",
			"type": 3,
			"price": 285,
			"intro": "这是一个非常有用的急速长剑"
		},
		{
			"id": 20,
			"name": "凌风长剑",
			"icon": "3172_Zephyr",
			"type": 3,
			"price": 300,
			"intro": "这是一个非常有用的凌风长剑"
		},
		{
			"id": 21,
			"name": "鸟盾",
			"icon": "IronSolari",
			"type": 6,
			"price": 315,
			"intro": "这是一个非常有用的鸟盾"
		},
		{
			"id": 22,
			"name": "蓝爸爸的头盔",
			"icon": "3206_SoulEaterWraith",
			"type": 2,
			"price": 330,
			"intro": "这是一个非常有用的蓝爸爸的头盔"
		},
		{
			"id": 23,
			"name": "蓝爸爸的拳套",
			"icon": "3207_SoulEaterGolem",
			"type": 7,
			"price": 345,
			"intro": "这是一个非常有用的蓝爸爸的拳套"
		},
		{
			"id": 24,
			"name": "蓝爸爸的斗篷",
			"icon": "3211_SpectresCowl",
			"type": 2,
			"price": 360,
			"intro": "这是一个非常有用的蓝爸爸的斗篷"
		},
		{
			"id": 25,
			"name": "跳钱项链",
			"icon": "3301_BabyPhilo",
			"type": 5,
			"price": 375,
			"intro": "这是一个非常有用的跳钱项链"
		},
		{
			"id": 26,
			"name": "牙齿项链",
			"icon": "3411_Bonetooth",
			"type": 5,
			"price": 390,
			"intro": "这是一个非常有用的牙齿项链"
		},
		{
			"id": 27,
			"name": "多兰剑",
			"icon": "PetAttack",
			"type": 3,
			"price": 405,
			"intro": "这是一个非常有用的多兰剑"
		}
	]
}

4.将json中的数据提取出来并保存,代码如下:

using System.Collections.Generic;
using UnityEngine;

public class MyItemsData : Singleton<MytemsData>
{
    public Dictionary<int, Item> ItemDic;
    ItemData itemData;
    public MytemsData()
    {
        Init();
    }

    public void Init()
    {
        ItemDic = new Dictionary<int, Item>();
        string data = Resources.Load<TextAsset>("Json/Itemdata").text;
        itemData = JsonUtility.FromJson<ItemData>(data);

        for (int i = 0; i < itemData.data.Count; i++)
        {
            ItemDic.Add(itemData.data[i].id, itemData.data[i]);
        }
    }
    /// <summary>
    /// 通过Id获取Item的Msg
    /// </summary>
    public Item GetItemDatas(int id)
    {
        return ItemDic.ContainsKey(id) ? ItemDic[id] : null;
    }
    /// <summary>
    ///  获取所有基础道具信息
    /// </summary>
    public ItemData GetItemsMsg()
    {
        return itemData;
    }
}

public class ItemData
{
    public List<Item> data;
}

[System.Serializable]
public class Item
{
    public int id;
    public string name;
    public string icon;
    public int type;
    public int price;
    public string intro;

}

5.将从云端获取的数据信息更新至背包,这需要将与云端数据交互的脚本稍微修改以下,不过千万千万要记住,一定不能在ContinueWith里面尝试更新至背包,改成async+await中也不行,这是由于Unity的UI线程不允许其他线程访问,但是logger线程是独立的,所以如果我们之前能成功的输出了日志,但是这UI实在是不行,这个我也琢磨了很久,暂时没发现特别好的解决办法,就只能放Update了里面等通知了。

using LC.Newtonsoft.Json.Linq;
using LeanCloud;
using LeanCloud.Storage;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

public class Login : MonoSingleton<Login>
{
    /// <summary>
    /// 是否登录成功
    /// </summary>
    public static bool isLogin;
    /// <summary>
    /// 登录的账号
    /// </summary>
    public static LCUser _User;
    /// <summary>
    /// 玩家信息
    /// </summary>
    public static Player player;
    /// <summary>
    /// 玩家数据id
    /// </summary>
    public static string playerobjectid;



    public InputField user;//账号
    public InputField pass;//密码
    public Button sigin;//注册/登录
    public Button tologin;//前往登录
    private bool isLogining;

    private bool GetDataSuccess;

    private void Awake()
    {
        LCApplication.Initialize("Nv3l7cT1F6BJeHYuhI5pLYOK-gzGzoHsz", "WxO6PGdlcv0pAqTmcpIHAtgM", "https://nv3l7ct1.lc-cn-n1-shared.com");
        sigin.onClick.AddListener(() =>
        {

            Debug.Log(MytemsData.Instance.GetItemDatas(1).name);
            if (isLogining)
            {
                LeanCloundLogin(user.text, pass.text);
                Debug.Log("开始登录");
            }
            else
            {
                LeanCloundSign(user.text, pass.text);
                Debug.Log("开始注册");
            }
        });
        tologin.onClick.AddListener(() =>
        {
            isLogining = true;
            sigin.GetComponentInChildren<Text>().text = "登录";
            tologin.gameObject.SetActive(false);
        });
    }


    /// <summary>
    /// 注册账号
    /// </summary>
    /// <param name="username"></param>
    /// <param name="password"></param>
    private void LeanCloundSign(string username, string password)
    {
        if (username == string.Empty || password == string.Empty)
        {
            return;
        }
        else
        {
            LCUser user = new LCUser();
            user["username"] = username;
            user["password"] = password;
            user.SignUp().ContinueWith(t =>
            {
                if (t.Exception != null)
                {
                    Debug.Log("用户名已存在");///
                }
                else
                {
                    Debug.Log("注册成功");
                    //添加玩家初始数据至数据库
                    AddPlayerData(username);
                }
            });
        }
    }
    /// <summary>
    /// 登录账号
    /// </summary>
    /// <param name="username"></param>
    /// <param name="password"></param>
    private void LeanCloundLogin(string username, string password)
    {
        var res = LCUser.Login(username, password);
        res.ContinueWith(e =>
        {
            if (e.Exception != null)
            {
                Debug.Log("登录失败");
            }
            else
            {
                isLogin = true;
                _User = e.Result;
                Debug.Log("登录成功");
                Debug.Log("获取玩家数据");
                GetPalyerData(e.Result.Username);
            }
        });
    }
    /// <summary>
    /// 添加玩家初始数据至云数据库
    /// </summary>
    /// <param name="username"></param>
    private void AddPlayerData(string username)
    {
        Player player = new Player();
        string json = JsonUtility.ToJson(player);
        LCObject lc = new LCObject("Gamedate");
        lc["username"] = username;
        lc["diamond"] = 10;
        lc["playeritemsmsg"] = json;
        lc.Save().ContinueWith(e =>
        {
            if (e.Exception != null)
            {
                Debug.Log(e.Exception);
            }
            else
            {
                Debug.Log("上传初始装备数据成功");
            }
        });
    }
    /// <summary>
    /// 获取玩家装备信息
    /// </summary>
    /// <param name="username"></param>
    private void  GetPalyerData(string username)
    {
        LCQuery<LCObject> query = new LCQuery<LCObject>("Gamedate");
        query.WhereEqualTo("username", username);
         query.First().ContinueWith(e=> {
             if (e.Exception!=null)
             {
                 Debug.Log("获取玩家信息失败");
             }
             else
             {
                 JObject json = JObject.Parse(e.Result.ToString());
                 player = JsonUtility.FromJson<Player>(json["playeritemsmsg"].ToString());
                 playerobjectid = json["objectId"].ToString();
                 GetDataSuccess = true;
                 Debug.Log("获取玩家信息成功");
             }
         });
       
    }


    /// <summary>
    /// 更新本地和服务器数据
    /// </summary>
    /// <param name="itemid"></param>
    /// <param name="count"></param>
    public void UpdatePlayerItemsMsg(int itemid, int count)
    {
        var Inventory = WindowsMgr.Instance.GetPanel<BagController>("Inventory");
        //更新本地玩家数据
        var item = player.ItemMsg.Where(it => it.id == itemid).FirstOrDefault();
        if (item != null)
        {
            if (count < 0 && item.num < -count)//不允许透支
            {
                return;
            }
            item.num += count;
            if (item.num == 0)
            {
                player.ItemMsg.Remove(item);
            }
        }
        else
        {
            if (count > 0)
            {
                player.ItemMsg.Add(new PlayItemMsg { id = itemid, num = count });
            }
        }
 //将数据更新至背包
        Inventory.Task = new KeyValuePair<Item, int>(MytemsData.Instance.GetItemDatas(itemid), count);


        //上传至云数据库
        LCObject lc = LCObject.CreateWithoutData("Gamedate", playerobjectid);
        lc["playeritemsmsg"] = player;
        lc.Save();
    }
    private void Update()
    {
        if (GetDataSuccess)
        {
            GetDataSuccess = false;
            var Inventory = WindowsMgr.Instance.GetPanel<BagController>("Inventory");
            foreach (var it in player.ItemMsg)
            {
                Debug.Log("道具序号:" + it.id + "  拥有数量:" + it.num);
                //将数据更新至背包
                Inventory.Task=new KeyValuePair<Item, int>(MytemsData.Instance.GetItemDatas(it.id), it.num);
            }
        }
        if (Input.GetKeyDown(KeyCode.Alpha1))
        {
            int a = Random.Range(1, 8);
            UpdatePlayerItemsMsg(a, 2);
        }
    }
}
[System.Serializable]
public class Player
{
    public string name;
    public List<PlayItemMsg> ItemMsg;
    public Player()//初始信息
    {
        name = "法外狂徒";
        ItemMsg = new List<PlayItemMsg>() {
            new PlayItemMsg() { id = 1, num = 1 },
            new PlayItemMsg() { id = 2, num = 1 },
            new PlayItemMsg() { id = 27, num = 1 },
            new PlayItemMsg() { id = 3, num = 10 },
            new PlayItemMsg() { id = 4, num = 20 }
        };
    }
}
//玩家拥有装备信息
[System.Serializable]
public class PlayItemMsg
{
    public int id;
    public int num;
}

6.背包和道具格子的代码我也放这吧,不懂的可以参考一下下

背包:

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

public class BagController : BasePanel
{
    public Dictionary<ItemSlot, Item> itemSlotDic = new Dictionary<ItemSlot, Item>();//格子
    private Transform InventoryItems;//格子父节点

    #region---------------------属性访问控制------------------------
    public KeyValuePair<Item, int> Task//任务
    {
        set
        {
            SetItemToSlot(value.Key, value.Value);
        }
    }

    #endregion------------------------------------------------------

    protected override void Register()
    {
        base.Register();
        InventoryItems = transform.Find("Scroll/Viewport/InventoryItems").transform;
       // SpawnSlot();
    }


    /// <summary>
    /// 生成空格子
    /// </summary>
    private void SpawnSlot(int count = 4)
    {
        //确保新生成的Slot中定义图片和数量的引用
        bool close=false;
        if (!Active)
        {
            Active = true;
            close = true;
        }
        for (int i = 0; i < count; i++)
        {
            GameObject item = Resources.Load<GameObject>("UI/Inventory/InventorySlot");
            GameObject solt = Instantiate(item, InventoryItems);
            itemSlotDic.Add(solt.AddComponent<ItemSlot>(), null);
        }
        if (close) Active = false;
    
    }
    /// <summary>
    /// 给控格子赋值
    /// </summary>
    private  void SetItemToSlot(Item item, int count = 1)
    {

        if (item.type == 10)
        {
            var keyValue = itemSlotDic.Where(it => it.Value == item);
            if (keyValue.Count() != 0)
            {
                keyValue.FirstOrDefault().Key.ItemCount += count;
                return;
            }
        }

        int par = item.type == 10 ? 1 : count;
        for (int i = 0; i < par; i++)
        {
            var keyValues = itemSlotDic.Where(it => it.Value == null);
            if (keyValues.Count() == 0)
            {
                SpawnSlot();
                SetItemToSlot(item);
            }
            else
            {
                var keyValue = keyValues.FirstOrDefault();
                itemSlotDic[keyValue.Key] = item;
                var solt = keyValue.Key;
                solt.Itemer = item;
            }
        }
        if (par == 1 && count > 1)
        {
            SetItemToSlot(item, count - 1);
        }
    }

    private void Update()
    {
        if (Input.GetMouseButtonDown(1))
        {
            Onzhenglibag();
        }
    }
    /// <summary>
    /// 一键整理背包
    /// </summary>
    public void Onzhenglibag()
    {
        var Items = itemSlotDic.Where(it => it.Value == null).ToList();
        foreach (var it in Items)
        {
            it.Key.transform.SetAsLastSibling();
        }
    }

}

道具格子:


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

public class ItemSlot : BasePanel, IPointerClickHandler, IBeginDragHandler, IDragHandler, IEndDragHandler
{
    private Image itemImage;//道具图片
    private Text ItemCountTex;//道具数量
    private ItemSlotTipPanel itemSlotTip;//拖拽是显示的图片的脚本

    #region ---------------属性访问控制-----------------

    public bool IsDraging
    {
        set
        {
            itemImage.enabled = !value;
            ItemCountTex.enabled = !value;
        }
    }
    private int itemCount = 0;
    public int ItemCount
    {
        get
        {
            return itemCount;
        }
        set
        {
            itemCount = value;
            if (value <= 0)
            {
                Itemer = null;
            }

            if (value > 1)
            {
                ItemCountTex.gameObject.SetActive(true);
                ItemCountTex.text = itemCount.ToString();
            }
            else
            {
                ItemCountTex.gameObject.SetActive(false);

            }

        }
    }

    private Item itemer;
    public Item Itemer
    {
        get
        {
            return itemer;
        }
        set
        {
            if (itemer != value)
            {
                itemer = value;
                if (value != null)
                {
                    itemCount = 1;
                    string icon = value.icon;
                    //Sprite sprite = Resources.Load<Sprite>(string.Format("ItemIcon/" + icon));
                    //改成AB包加载
                    Sprite sprite = LoadAssetsbundle.Instance.LoadRes<Sprite>(icon);
                    itemImage.sprite = sprite;
                    itemImage.gameObject.SetActive(true);
                }
                else
                {
                    var Inventory = WindowsMgr.Instance.GetPanel<BagController>("Inventory");
                    Inventory.itemSlotDic[this] = null;
                    itemImage.gameObject.SetActive(false);
                }
            }
        }
    }

    #endregion -----------------------------------------


    #region -----------------接口实现-------------------

    /// <summary>
    /// 双击使用道具
    /// </summary>
    public void OnPointerClick(PointerEventData eventData)
    {
        if (eventData.clickCount == 2)
        {
            if (itemer != null)
            {
                //PlayerDate.Instance.ItemChange = new KeyValuePair<Item, int>(itemer, -1);
                Login.Instance.UpdatePlayerItemsMsg(itemer.id, -1);
            }
            if (itemer != null && itemer.type != 10)
            {
                ItemCount -= 1;
            }
        }
    }

    public void OnBeginDrag(PointerEventData eventData)
    {
        if (itemer != null)
        {
            itemSlotTip._Sprite = itemImage.sprite;
            IsDraging = true;
        }
    }

    public void OnDrag(PointerEventData eventData)
    {
        itemSlotTip.transform.position = eventData.position;
    }

    public void OnEndDrag(PointerEventData eventData)
    {
        itemSlotTip.Active = false;
        IsDraging = false;

        var slot = eventData.hovered.Where(it => it.CompareTag("Slot")).FirstOrDefault();
        ItemSlot slotcs;
        if (slot != null && slot.TryGetComponent(out slotcs))
        {
            ChangeSibling(slot);
        }
    }

    #endregion---------------------------------------------


    protected override void Awake()//不让添加到窗体控制字典和隐藏
    {
        base.Register();
        itemImage = GetControl<Image>("InventoryItem");
        ItemCountTex = GetControl<Text>("ItemCount");
        itemSlotTip = WindowsMgr.Instance.GetPanel<ItemSlotTipPanel>("ItemSlotTip");

    }
    /// <summary>
    /// 互换位置
    /// </summary>
    private void ChangeSibling(GameObject par)
    {
        if (itemer != null && transform.parent == par.transform.parent)
        {
            int index1 = transform.GetSiblingIndex();
            int index2 = par.transform.GetSiblingIndex();
            transform.SetSiblingIndex(index2);
            par.transform.SetSiblingIndex(index1);
        }
    }

}

道具格子里面有双击消耗该道具的功能,Login中有按下1键随机增加两个道具的功能。

运行结果如下:

至此,这次的文章就到这了,后续功能实现将在下篇文章中简述。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值