在上一篇文章中我们已经实现了与云端数据库的数据交互了,接下来我们要做的是制作一个背包,让我们的数据交互看起来更加的直观,有大坑,请放心阅读。
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键随机增加两个道具的功能。
运行结果如下:
至此,这次的文章就到这了,后续功能实现将在下篇文章中简述。