7月份自己做了一个模拟经营类的游戏Demo,在此总结UI、库存系统、交易系统、游戏循环等相关内容的代码和实现。
实现效果预览
目录
UI
本项目的UI通过Unity自家的UGUI实现,所有面板的父对象皆为Canvas,各面板为一个实例化的单例对象,其数据由自己进行存储和更新。
面板基础逻辑
IPanel接口:
interface IPanel
{
public void ShowPanel();
public void HidePanel();
}
以商店面板为例:
(通过给面板添加CanvasGroup组件,并更改其参数实现面板的显隐)
public class ShopPanel : MonoBehaviour, IPanel
{
//单例
private static ShopPanel instance;
public static ShopPanel Instance
{
get
{
return instance;
}
}
//获取商店面板信息
public TMP_Text txtTime;
public TMP_Text txtDays;
public TMP_Text txtShopMoney;
public KnapsackPanel knapsack1Panel;
public KnapsackPanel knapsack2Panel;
public KnapsackPanel knapsack3Panel;
public RolePanel role1Panel;
public RolePanel role2Panel;
public RolePanel role3Panel;
private CanvasGroup canvasGroup;
private int money = 100;
public int Money
{
get { return money; }
set
{
money = value;
txtShopMoney.text = money.ToString();
}
}
public Button btnStorage;
public Button btnCreate;
public static bool storageVisible = false;
public static bool createVisible = false;
private void Awake()
{
//实例化单例
instance = this;
}
// Start is called before the first frame update
void Start()
{
canvasGroup = gameObject.GetComponent<CanvasGroup>();
HidePanel();
btnStorage.onClick.AddListener(() =>
{
//提示面板清空
NoticePanel.Instance.HideNotice();
//仓库按钮按下的逻辑
if (!storageVisible)
{
storageVisible = true;
StoragePanel.Instance.ShowPanel();
createVisible = false;
CreatePanel.Instance.HidePanel();
}
else
{
storageVisible = false;
StoragePanel.Instance.HidePanel();
}
});
btnCreate.onClick.AddListener(() =>
{
//提示面板清空
NoticePanel.Instance.HideNotice();
//制造按钮按下的逻辑
if (!createVisible)
{
createVisible = true;
CreatePanel.Instance.ShowPanel();
storageVisible = false;
StoragePanel.Instance.HidePanel();
}
else
{
createVisible = false;
CreatePanel.Instance.HidePanel();
}
});
txtShopMoney.text = money.ToString();
}
//实现接口IPanel
public void ShowPanel()
{
canvasGroup.alpha = 1;
canvasGroup.interactable = true;
canvasGroup.blocksRaycasts = true;
}
public void HidePanel()
{
canvasGroup.alpha = 0;
canvasGroup.interactable = false;
canvasGroup.blocksRaycasts = false;
}
/// <summary>
/// 花费金钱
/// </summary>
public bool ConsumeMoney(int amount)
{
if (money < amount)
{
NoticePanel.Instance.ShowNotice("金钱不足!");
return false;
}
money -= amount;
txtShopMoney.text = money.ToString();
NoticePanel.Instance.ShowNotice("交易成功!");
return true;
}
/// <summary>
/// 赚取金钱
/// </summary>
public void EarnMoney(int amount)
{
money += amount;
txtShopMoney.text = money.ToString();
}
}
库存系统
库存系统由Inventory、InventoryManager、Slot、ItemUI等类实现。
Inventory类:
using System.Collections;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
public class Inventory : MonoBehaviour
{
protected Slot[] slots;
public Slot[] Slots
{
get
{
return slots;
}
set
{
slots = value;
}
}
// Start is called before the first frame update
public virtual void Start()
{
slots = GetComponentsInChildren<Slot>();
}
/// <summary>
/// 通过ID存储物品
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public bool StoreItem(int id)
{
Item item = InventoryManager.Instance.GetItemID(id);
return StoreItem(item);
}
/// <summary>
/// 是否存储成功
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public bool StoreItem(Item item)
{
if (item == null)
return false;
if (item.capacity == 1)
{
Slot slot = FindEmptySlot();
if (slot == null)
{
NoticePanel.Instance.ShowNotice("没空的物品槽!");
return false;
}
else
{
slot.CreateItem(item);
}
}
else
{
Slot slot = FindSameIDSlot(item);
if (slot != null)
{
slot.CreateItem(item);
}
else //找新的空物品槽
{
Slot slot2 = FindEmptySlot();
if (slot2 != null)
slot2.CreateItem(item);
else
{
NoticePanel.Instance.ShowNotice("没空的物品槽!");
return false;
}
}
}
return true;
}
/// <summary>
/// 减少物品数量
/// </summary>
/// <param name="targetItem"></param>
/// <param name="total"></param>
/// <param name="amount"></param>
/// <returns></returns>
public bool ReduceItem(Item targetItem, int total, int amount = 1)
{
if (amount > total)
return false;
foreach (Slot slot in slots)
{
if (slot.transform.childCount != 0)
{
GameObject items = slot.transform.GetChild(0).GetComponent<ItemUI>().gameObject;
if (items.GetComponent<ItemUI>().Item.id == targetItem.id)
{
int currentAmount = int.Parse(items.transform.GetChild(0).gameObject.GetComponent<TMP_Text>().text);
if (amount >= currentAmount)
{
amount -= currentAmount;
total -= currentAmount;
items.transform.GetChild(0).gameObject.GetComponent<TMP_Text>().text = 0.ToString();
DestroyImmediate(items);
items = null;
if (total == 0)
{
BuyPanel.CancelBuy();
return true;
}
}
else
{
currentAmount -= amount;
total -= amount;
items.GetComponent<ItemUI>().Amount = currentAmount;
items.transform.GetChild(0).gameObject.GetComponent<TMP_Text>().text = currentAmount.ToString();
amount = 0;
}
}
if (amount == 0)
{
return true;
}
}
}
return false;
}
/// <summary>
/// 找空的物品槽
/// </summary>
/// <returns></returns>
private Slot FindEmptySlot()
{
foreach (Slot slot in slots)
{
if (slot.transform.childCount == 0)
return slot;
}
return null;
}
/// <summary>
/// 找相同ID的物品槽
/// </summary>
/// <returns></returns>
private Slot FindSameIDSlot(Item item)
{
foreach (Slot slot in slots)
{
if (slot.transform.childCount >= 1 && slot.GetItemID() == item.id && slot.IsFull() == false)
return slot;
}
return null;
}
/// <summary>
/// 统计相同ID物品的总数量
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public int CountSameIDSlot(Item item)
{
int count = 0;
foreach (Slot slot in slots)
{
if (slot.transform.childCount >= 1 && slot.GetItemID() == item.id)
count += int.Parse(slot.transform.GetChild(0).transform.GetChild(0).GetComponent<TMP_Text>().text);
}
return count;
}
/// <summary>
/// 背包整理(空格收纳、物品排序、材料收纳)
/// </summary>
public void InventoryClear()
{
EmptyClear();
Sort();
Combine();
}
/// <summary>
/// 空格收纳
/// </summary>
public void EmptyClear()
{
int itemCount = 0;
foreach(Slot slot in slots)
{
if (slot.transform.childCount != 0)
itemCount++;
}
for (int i = 0; i < slots.Length; i++)
{
if (slots[i].transform.childCount == 0)
{
OneEmptyClear(i);
i = -1;
}
if (i + 1 == itemCount)
return;
}
}
/// <summary>
/// 单个空格收纳
/// </summary>
/// <param name="index"></param>
public void OneEmptyClear(int index)
{
for (int i = index + 1; i < slots.Length; i++)
{
if (slots[i].transform.childCount != 0)
{
GameObject go1 = slots[i].transform.Find("Items(Clone)").gameObject;
go1.transform.SetParent(slots[i - 1].transform);
go1.transform.localPosition = Vector3.zero;
}
}
}
/// <summary>
/// 物品排序
/// </summary>
public void Sort()
{
//整体排序
int i, j;
for (i = 0; i < slots.Length - 1; i++)
{
if (slots[i].transform.childCount == 0)
break;
for (j = i + 1; j < slots.Length; j++)
{
if (slots[j].transform.childCount == 0)
break;
if (slots[i].transform.GetChild(0).GetComponent<ItemUI>().Item.id < slots[j].transform.GetChild(0).GetComponent<ItemUI>().Item.id)
{
Swap(i, j);
}
}
}
int l = 0;
int k = l;
//装备耐久度排序(优先低耐久度)
while (slots[l].transform.childCount != 0 && slots[l].transform.GetChild(0).GetComponent<ItemUI>().Item.type == Item.ItemType.Equipment)
{
while (slots[k].transform.childCount != 0 && slots[k].transform.GetChild(0).GetComponent<ItemUI>().Item.id == slots[l].transform.GetChild(0).GetComponent<ItemUI>().Item.id)
k++;
for (i = l; i < k - 1; i++)
{
for (j = i + 1; j < k; j++)
{
if (slots[i].transform.GetChild(0).GetComponent<ItemUI>().durability > slots[j].transform.GetChild(0).GetComponent<ItemUI>().durability)
{
Swap(i, j);
}
}
}
l = k;
}
}
/// <summary>
/// 交换物品位置
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
public void Swap(int a, int b)
{
GameObject go1 = slots[a].transform.Find("Items(Clone)").gameObject;
GameObject go2 = slots[b].transform.Find("Items(Clone)").gameObject;
go1.transform.SetParent(slots[b].transform);
go1.transform.localPosition = Vector3.zero;
go2.transform.SetParent(slots[a].transform);
go2.transform.localPosition = Vector3.zero;
}
/// <summary>
/// 合并材料
/// </summary>
public void Combine()
{
int i = 0;
//结束范围
while (slots[i].transform.childCount != 0 && i < slots.Length)
{
//开始范围
if (slots[i].transform.GetChild(0).GetComponent<ItemUI>().Item.type == Item.ItemType.EquipMaterial)
{
int j = i, k = i;
//设置此种材料的下界
while (slots[k].transform.childCount != 0 && slots[k].transform.GetChild(0).GetComponent<ItemUI>().Item.id == slots[i].transform.GetChild(0).GetComponent<ItemUI>().Item.id)
k++;
int sum = 0;
for (; j < k; j++)
{
//局部计算当前材料的总数量
sum += int.Parse(slots[j].transform.GetChild(0).transform.GetChild(0).GetComponent<TMP_Text>().text);
}
j = i;
while (sum != 0)
{
//满足条件则是此种材料最后一格
if (sum <= slots[i].transform.GetChild(0).GetComponent<ItemUI>().Item.capacity)
{
slots[j].transform.GetChild(0).transform.GetChild(0).GetComponent<TMP_Text>().text = sum.ToString();
sum = 0;
if (slots[j + 1].transform.childCount != 0 && slots[j + 1].transform.GetChild(0).GetComponent<ItemUI>().Item.id == slots[i].transform.GetChild(0).GetComponent<ItemUI>().Item.id)
{
DestroyImmediate(slots[j + 1].transform.GetChild(0).gameObject);
EmptyClear();
}
}
else
{
slots[j].transform.GetChild(0).transform.GetChild(0).GetComponent<TMP_Text>().text = slots[i].transform.GetChild(0).GetComponent<ItemUI>().Item.capacity.ToString();
sum -= slots[i].transform.GetChild(0).GetComponent<ItemUI>().Item.capacity;
}
j++;
}
i = k - 1;
}
i++;
}
}
}
需要实现库存功能的面板继承Inventory类,以KnapsackPanel为例:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
public class KnapsackPanel : Inventory, IPanel
{
public Button btnKnapsackClose;
public RolePanel rolePanel;
private CanvasGroup canvasGroup;
// Start is called before the first frame update
public override void Start()
{
base.Start();
canvasGroup = gameObject.GetComponent<CanvasGroup>();
HidePanel();
btnKnapsackClose.onClick.AddListener(() =>
{
//提示面板清空
NoticePanel.Instance.HideNotice();
//关闭逻辑
RolePanel.knapsackVisible = false;
HidePanel();
});
}
// Update is called once per frame
void Update()
{
}
public void ShowPanel()
{
//整理库存
InventoryClear();
canvasGroup.alpha = 1;
canvasGroup.interactable = true;
canvasGroup.blocksRaycasts = true;
}
public void HidePanel()
{
canvasGroup.alpha = 0;
canvasGroup.interactable = false;
canvasGroup.blocksRaycasts = false;
}
public void Equip()
{
int i = 0;
for (; i < slots.Length; i++)
{
//结束判断
if (slots[i].transform.childCount == 0 || slots[i].transform.GetChild(0).GetComponent<ItemUI>().Item.type == Item.ItemType.EquipMaterial)
return;
Item item = slots[i].transform.GetChild(0).GetComponent<ItemUI>().Item;
int itemID = item.id;
switch(Equipment.GetTypeID(itemID))
{
case "Head":
foreach (Slot slot in rolePanel.Slots)
{
//如果类型匹配
if (slot.name == "Head")
{
int equipCount = slot.transform.childCount;
//装备栏为空
if (equipCount == 0 || slot.transform.GetChild(equipCount - 1).GetComponent<ItemUI>().durability <= 0)
{
//将背包内的装备放入装备栏
GameObject go1 = slots[i].transform.Find("Items(Clone)").gameObject;
go1.transform.SetParent(slot.transform);
go1.transform.localPosition = Vector3.zero;
//为角色加上装备属性
Equipment equipment = InventoryManager.Instance.GetEquipmentID(go1.GetComponent<ItemUI>().Item.id);
if (equipment != null)
{
rolePanel.Hp += equipment.hp;
rolePanel.Str += equipment.str;
rolePanel.Def += equipment.def;
rolePanel.Atk += equipment.atk;
rolePanel.Crit += equipment.crit;
rolePanel.CritDam += equipment.critDam;
}
//隐藏装备类型文字
rolePanel.txtHead.alpha = 0;
//整理背包
InventoryClear();
i--;
}
//优先穿戴高阶装备
else if (equipCount == 1 && slot.transform.GetChild(0).GetComponent<ItemUI>().Item.id < itemID)
{
//将背包内的装备和装备栏的互换
GameObject go1 = slots[i].transform.Find("Items(Clone)").gameObject;
GameObject go2 = slot.transform.Find("Items(Clone)").gameObject;
go1.transform.SetParent(slot.transform);
go1.transform.localPosition = Vector3.zero;
go2.transform.SetParent(slots[i].transform);
go2.transform.localPosition = Vector3.zero;
//角色属性调整
Equipment equipment1 = InventoryManager.Instance.GetEquipmentID(slots[i].transform.GetChild(0).GetComponent<ItemUI>().Item.id);
Equipment equipment2 = InventoryManager.Instance.GetEquipmentID(slot.transform.GetChild(0).GetComponent<ItemUI>().Item.id);
rolePanel.Hp += equipment2.hp - equipment1.hp;
rolePanel.Str += equipment2.str - equipment1.str;
rolePanel.Def += equipment2.def - equipment1.def;
rolePanel.Atk += equipment2.atk - equipment1.atk;
rolePanel.Crit += equipment2.crit - equipment1.crit;
rolePanel.CritDam += equipment2.critDam - equipment1.critDam;
//整理背包
InventoryClear();
}
}
}
break;
case "Chest":
foreach (Slot slot in rolePanel.Slots)
{
//如果装备类型匹配
if (slot.name == "Chest")
{
int equipCount = slot.transform.childCount;
//装备栏为空
if (equipCount == 0 || slot.transform.GetChild(equipCount - 1).GetComponent<ItemUI>().durability <= 0)
{
//将背包内的装备放入装备栏
GameObject go1 = slots[i].transform.Find("Items(Clone)").gameObject;
go1.transform.SetParent(slot.transform);
go1.transform.localPosition = Vector3.zero;
//为角色加上装备属性
Equipment equipment = InventoryManager.Instance.GetEquipmentID(go1.GetComponent<ItemUI>().Item.id);
if (equipment != null)
{
rolePanel.Hp += equipment.hp;
rolePanel.Str += equipment.str;
rolePanel.Def += equipment.def;
rolePanel.Atk += equipment.atk;
rolePanel.Crit += equipment.crit;
rolePanel.CritDam += equipment.critDam;
}
//隐藏装备类型文字
rolePanel.txtChest.alpha = 0;
//整理背包
InventoryClear();
i--;
}
//优先穿戴高阶装备
else if (equipCount == 1 && slot.transform.GetChild(0).GetComponent<ItemUI>().Item.id < itemID)
{
//将背包内的装备和装备栏的互换
GameObject go1 = slots[i].transform.Find("Items(Clone)").gameObject;
GameObject go2 = slot.transform.Find("Items(Clone)").gameObject;
go1.transform.SetParent(slot.transform);
go1.transform.localPosition = Vector3.zero;
go2.transform.SetParent(slots[i].transform);
go2.transform.localPosition = Vector3.zero;
//角色属性调整
Equipment equipment1 = InventoryManager.Instance.GetEquipmentID(slots[i].transform.GetChild(0).GetComponent<ItemUI>().Item.id);
Equipment equipment2 = InventoryManager.Instance.GetEquipmentID(slot.transform.GetChild(0).GetComponent<ItemUI>().Item.id);
rolePanel.Hp += equipment2.hp - equipment1.hp;
rolePanel.Str += equipment2.str - equipment1.str;
rolePanel.Def += equipment2.def - equipment1.def;
rolePanel.Atk += equipment2.atk - equipment1.atk;
rolePanel.Crit += equipment2.crit - equipment1.crit;
rolePanel.CritDam += equipment2.critDam - equipment1.critDam;
//整理背包
InventoryClear();
}
}
}
break;
case "Weapon":
foreach (Slot slot in rolePanel.Slots)
{
//如果装备类型匹配
if (slot.name == "Weapon")
{
int equipCount = slot.transform.childCount;
//装备栏为空
if (equipCount == 0 || slot.transform.GetChild(equipCount - 1).GetComponent<ItemUI>().durability <= 0)
{
//将背包内的装备放入装备栏
GameObject go1 = slots[i].transform.Find("Items(Clone)").gameObject;
go1.transform.SetParent(slot.transform);
go1.transform.localPosition = Vector3.zero;
//为角色加上装备属性
Equipment equipment = InventoryManager.Instance.GetEquipmentID(go1.GetComponent<ItemUI>().Item.id);
if (equipment != null)
{
rolePanel.Hp += equipment.hp;
rolePanel.Str += equipment.str;
rolePanel.Def += equipment.def;
rolePanel.Atk += equipment.atk;
rolePanel.Crit += equipment.crit;
rolePanel.CritDam += equipment.critDam;
}
//隐藏装备类型文字
rolePanel.txtWeapon.alpha = 0;
//整理背包
InventoryClear();
i--;
}
//优先穿戴高阶装备
else if (equipCount == 1 && slot.transform.GetChild(0).GetComponent<ItemUI>().Item.id < itemID)
{
//将背包内的装备和装备栏的互换
GameObject go1 = slots[i].transform.Find("Items(Clone)").gameObject;
GameObject go2 = slot.transform.Find("Items(Clone)").gameObject;
go1.transform.SetParent(slot.transform);
go1.transform.localPosition = Vector3.zero;
go2.transform.SetParent(slots[i].transform);
go2.transform.localPosition = Vector3.zero;
//角色属性调整
Equipment equipment1 = InventoryManager.Instance.GetEquipmentID(slots[i].transform.GetChild(0).GetComponent<ItemUI>().Item.id);
Equipment equipment2 = InventoryManager.Instance.GetEquipmentID(slot.transform.GetChild(0).GetComponent<ItemUI>().Item.id);
rolePanel.Hp += equipment2.hp - equipment1.hp;
rolePanel.Str += equipment2.str - equipment1.str;
rolePanel.Def += equipment2.def - equipment1.def;
rolePanel.Atk += equipment2.atk - equipment1.atk;
rolePanel.Crit += equipment2.crit - equipment1.crit;
rolePanel.CritDam += equipment2.critDam - equipment1.critDam;
//整理背包
InventoryClear();
}
}
}
break;
case "Glove":
foreach (Slot slot in rolePanel.Slots)
{
//如果装备类型匹配
if (slot.name == "Glove")
{
int equipCount = slot.transform.childCount;
//装备栏为空
if (equipCount == 0 || slot.transform.GetChild(equipCount - 1).GetComponent<ItemUI>().durability <= 0)
{
//将背包内的装备放入装备栏
GameObject go1 = slots[i].transform.Find("Items(Clone)").gameObject;
go1.transform.SetParent(slot.transform);
go1.transform.localPosition = Vector3.zero;
//为角色加上装备属性
Equipment equipment = InventoryManager.Instance.GetEquipmentID(go1.GetComponent<ItemUI>().Item.id);
if (equipment != null)
{
rolePanel.Hp += equipment.hp;
rolePanel.Str += equipment.str;
rolePanel.Def += equipment.def;
rolePanel.Atk += equipment.atk;
rolePanel.Crit += equipment.crit;
rolePanel.CritDam += equipment.critDam;
}
//隐藏装备类型文字
rolePanel.txtGlove.alpha = 0;
//整理背包
InventoryClear();
i--;
}
//优先穿戴高阶装备
else if (equipCount == 1 && slot.transform.GetChild(0).GetComponent<ItemUI>().Item.id < itemID)
{
//将背包内的装备和装备栏的互换
GameObject go1 = slots[i].transform.Find("Items(Clone)").gameObject;
GameObject go2 = slot.transform.Find("Items(Clone)").gameObject;
go1.transform.SetParent(slot.transform);
go1.transform.localPosition = Vector3.zero;
go2.transform.SetParent(slots[i].transform);
go2.transform.localPosition = Vector3.zero;
//角色属性调整
Equipment equipment1 = InventoryManager.Instance.GetEquipmentID(slots[i].transform.GetChild(0).GetComponent<ItemUI>().Item.id);
Equipment equipment2 = InventoryManager.Instance.GetEquipmentID(slot.transform.GetChild(0).GetComponent<ItemUI>().Item.id);
rolePanel.Hp += equipment2.hp - equipment1.hp;
rolePanel.Str += equipment2.str - equipment1.str;
rolePanel.Def += equipment2.def - equipment1.def;
rolePanel.Atk += equipment2.atk - equipment1.atk;
rolePanel.Crit += equipment2.crit - equipment1.crit;
rolePanel.CritDam += equipment2.critDam - equipment1.critDam;
//整理背包
InventoryClear();
}
}
}
break;
case "Jewelry":
foreach (Slot slot in rolePanel.Slots)
{
//如果装备类型匹配
if (slot.name == "Jewelry")
{
int equipCount = slot.transform.childCount;
//装备栏为空
if (equipCount == 0 || slot.transform.GetChild(equipCount - 1).GetComponent<ItemUI>().durability <= 0)
{
//将背包内的装备放入装备栏
GameObject go1 = slots[i].transform.Find("Items(Clone)").gameObject;
go1.transform.SetParent(slot.transform);
go1.transform.localPosition = Vector3.zero;
//为角色加上装备属性
Equipment equipment = InventoryManager.Instance.GetEquipmentID(go1.GetComponent<ItemUI>().Item.id);
if (equipment != null)
{
rolePanel.Hp += equipment.hp;
rolePanel.Str += equipment.str;
rolePanel.Def += equipment.def;
rolePanel.Atk += equipment.atk;
rolePanel.Crit += equipment.crit;
rolePanel.CritDam += equipment.critDam;
}
//隐藏装备类型文字
rolePanel.txtJewelry.alpha = 0;
//整理背包
InventoryClear();
i--;
}
//优先穿戴高阶装备
else if (equipCount == 1 && slot.transform.GetChild(0).GetComponent<ItemUI>().Item.id < itemID)
{
//将背包内的装备和装备栏的互换
GameObject go1 = slots[i].transform.Find("Items(Clone)").gameObject;
GameObject go2 = slot.transform.Find("Items(Clone)").gameObject;
go1.transform.SetParent(slot.transform);
go1.transform.localPosition = Vector3.zero;
go2.transform.SetParent(slots[i].transform);
go2.transform.localPosition = Vector3.zero;
//角色属性调整
Equipment equipment1 = InventoryManager.Instance.GetEquipmentID(slots[i].transform.GetChild(0).GetComponent<ItemUI>().Item.id);
Equipment equipment2 = InventoryManager.Instance.GetEquipmentID(slot.transform.GetChild(0).GetComponent<ItemUI>().Item.id);
rolePanel.Hp += equipment2.hp - equipment1.hp;
rolePanel.Str += equipment2.str - equipment1.str;
rolePanel.Def += equipment2.def - equipment1.def;
rolePanel.Atk += equipment2.atk - equipment1.atk;
rolePanel.Crit += equipment2.crit - equipment1.crit;
rolePanel.CritDam += equipment2.critDam - equipment1.critDam;
//整理背包
InventoryClear();
}
}
}
break;
case "Shield":
foreach (Slot slot in rolePanel.Slots)
{
//如果装备类型匹配
if (slot.name == "Shield")
{
int equipCount = slot.transform.childCount;
//装备栏为空
if (equipCount == 0 || slot.transform.GetChild(equipCount - 1).GetComponent<ItemUI>().durability <= 0)
{
//将背包内的装备放入装备栏
GameObject go1 = slots[i].transform.Find("Items(Clone)").gameObject;
go1.transform.SetParent(slot.transform);
go1.transform.localPosition = Vector3.zero;
//为角色加上装备属性
Equipment equipment = InventoryManager.Instance.GetEquipmentID(go1.GetComponent<ItemUI>().Item.id);
if (equipment != null)
{
rolePanel.Hp += equipment.hp;
rolePanel.Str += equipment.str;
rolePanel.Def += equipment.def;
rolePanel.Atk += equipment.atk;
rolePanel.Crit += equipment.crit;
rolePanel.CritDam += equipment.critDam;
}
//隐藏装备类型文字
rolePanel.txtShield.alpha = 0;
//整理背包
InventoryClear();
i--;
}
//优先穿戴高阶装备
else if (equipCount == 1 && slot.transform.GetChild(0).GetComponent<ItemUI>().Item.id < itemID)
{
//将背包内的装备和装备栏的互换
GameObject go1 = slots[i].transform.Find("Items(Clone)").gameObject;
GameObject go2 = slot.transform.Find("Items(Clone)").gameObject;
go1.transform.SetParent(slot.transform);
go1.transform.localPosition = Vector3.zero;
go2.transform.SetParent(slots[i].transform);
go2.transform.localPosition = Vector3.zero;
//角色属性调整
Equipment equipment1 = InventoryManager.Instance.GetEquipmentID(slots[i].transform.GetChild(0).GetComponent<ItemUI>().Item.id);
Equipment equipment2 = InventoryManager.Instance.GetEquipmentID(slot.transform.GetChild(0).GetComponent<ItemUI>().Item.id);
rolePanel.Hp += equipment2.hp - equipment1.hp;
rolePanel.Str += equipment2.str - equipment1.str;
rolePanel.Def += equipment2.def - equipment1.def;
rolePanel.Atk += equipment2.atk - equipment1.atk;
rolePanel.Crit += equipment2.crit - equipment1.crit;
rolePanel.CritDam += equipment2.critDam - equipment1.critDam;
//整理背包
InventoryClear();
}
}
}
break;
}
}
}
public void ClearMaterial()
{
foreach (Slot slot in slots)
{
if (slot.transform.childCount > 0 && slot.transform.GetChild(0).GetComponent<ItemUI>().Item.type == Item.ItemType.EquipMaterial)
Destroy(slot.transform.GetChild(0).gameObject);
}
}
}
InventoryManager类:
(游戏内物品的信息以及合成配方信息使用JSON格式进行存储读取)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class InventoryManager : MonoBehaviour
{
//单例
private static InventoryManager instance;
public static InventoryManager Instance
{
get
{
return instance;
}
}
private ToolTip toolTip;
private ForgeTip forgeTip;
/// <summary>
/// 判断是否显示ToolTip
/// </summary>
private bool isShowToolTipLeft = false;
private bool isShowToolTipRight = false;
private bool isShowForgeTip = false;
private static Canvas canvas;
private void Awake()
{
//实例化单例
instance = this;
ParseItemsJSON();
}
// Start is called before the first frame update
void Start()
{
toolTip = FindObjectOfType<ToolTip>();
forgeTip = FindObjectOfType<ForgeTip>();
canvas = GameObject.Find("Canvas").GetComponent<Canvas>();
}
// Update is called once per frame
void Update()
{
if (isShowToolTipLeft == true)
{
Vector2 position;
RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.transform as RectTransform, Input.mousePosition, null, out position);
toolTip.SetToolTipPositionLeft(position);
}
if (isShowToolTipRight == true)
{
Vector2 position;
RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.transform as RectTransform, Input.mousePosition, null, out position);
toolTip.SetToolTipPositionRight(position, toolTip.GetComponent<RectTransform>().rect.width);
}
if (isShowForgeTip == true)
{
Vector2 position;
RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.transform as RectTransform, Input.mousePosition, null, out position);
forgeTip.SetForgeTipPosition(position);
}
}
/// <summary>
/// 存放物品的集合
/// </summary>
private List<Item> itemList;
public List<Item> ItemList
{
get
{
return itemList;
}
}
/// <summary>
/// 解析JSON
/// </summary>
private void ParseItemsJSON()
{
itemList = new List<Item>();
TextAsset ta = Resources.Load<TextAsset>("items");
JSONObject j = new JSONObject(ta.text);
foreach (JSONObject temp in j.list)
{
string itemType = temp["type"].str;
Item.ItemType type = (Item.ItemType)System.Enum.Parse(typeof(Item.ItemType), itemType);
int id = (int)temp["id"].n; //公用物品解析
string name = temp["name"].str;
string description = temp["description"].str;
int capacity = (int)(temp["capacity"].n);
int standardPrice = (int)(temp["standardPrice"].n);
string sprite = temp["sprite"].str;
Item item = null;
switch (type)
{
case Item.ItemType.Equipment:
int hp = (int)temp["hp"].n;
int str = (int)temp["str"].n;
int def = (int)temp["def"].n;
int atk = (int)temp["atk"].n;
int crit = (int)temp["crit"].n;
int critDam = (int)temp["critDam"].n;
float durability = (float)temp["durability"].n;
Equipment.EquipmentType equipType = (Equipment.EquipmentType)System.Enum.Parse(typeof(Equipment.EquipmentType), temp["equipType"].str);
item = new Equipment(id, name, type, description, capacity, standardPrice, sprite, hp, str, def, atk, crit, critDam, durability, equipType);
break;
case Item.ItemType.EquipMaterial:
item = new EquipMaterial(id, name, type, description, capacity, standardPrice, sprite);
break;
}
itemList.Add(item);
}
}
/// <summary>
/// 通过ID返回Item
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public Item GetItemID(int id)
{
foreach (Item item in itemList)
{
if (item.id == id)
{
return item;
}
}
return null;
}
/// <summary>
/// 通过id获得装备
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public Equipment GetEquipmentID(int id)
{
foreach (Item item in itemList)
{
if (item.id == id)
{
return item as Equipment;
}
}
return null;
}
public void ShowToolTipContentLeft(string content)
{
isShowToolTipLeft = true;
toolTip.Show(content);
}
public void ShowToolTipContentRight(string content)
{
isShowToolTipRight = true;
toolTip.Show(content);
}
public void HideToolTipContent()
{
isShowToolTipLeft = false;
isShowToolTipRight = false;
toolTip.Hide();
}
public void ShowForgeTipContent(string content)
{
isShowForgeTip = true;
forgeTip.Show(content);
}
public void HideForgeTipContent()
{
isShowForgeTip = false;
forgeTip.Hide();
}
}
Slot类:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class Slot : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
{
public GameObject itemPrefab;
public void CreateItem(Item item)
{
if (transform.childCount == 0)
{
GameObject itemPrefabs = Instantiate(itemPrefab);
itemPrefabs.transform.SetParent(transform);
itemPrefabs.transform.localPosition = Vector3.zero;
itemPrefabs.transform.localScale = Vector3.one;
transform.GetChild(0).GetComponent<ItemUI>().SetItem(item);
}
else
{
transform.GetChild(0).GetComponent<ItemUI>().AddAmount();
}
}
public void CreateItem(Item item,int amount)
{
if (transform.childCount == 0)
{
GameObject itemPrefabs = GameObject.Instantiate(itemPrefab);
itemPrefabs.transform.SetParent(transform);
itemPrefabs.transform.localPosition = Vector3.zero;
itemPrefabs.transform.localScale = Vector3.one;
transform.GetChild(0).GetComponent<ItemUI>().SetItem(item, amount);
}
else
{
return;
}
}
/// <summary>
/// 得到当前物品槽存储的物品类型
/// </summary>
/// <returns></returns>
public Item.ItemType GetItemType()
{
return transform.GetChild(0).GetComponent<ItemUI>().Item.type;
}
/// <summary>
/// 得到当前物品槽存储的物品ID
/// </summary>
/// <returns></returns>
public int GetItemID()
{
return transform.GetChild(0).GetComponent<ItemUI>().Item.id;
}
/// <summary>
/// 当前物品槽的物品容量是否已满
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public bool IsFull()
{
ItemUI itemUI = transform.GetChild(0).GetComponent<ItemUI>();
return itemUI.Amount >= itemUI.Item.capacity;
}
public void OnPointerEnter(PointerEventData eventData)
{
if (transform.childCount > 0)
{
//当指向制作栏中的装备时
if (transform.GetChild(0).GetComponent<ItemUI>().Item.type == Item.ItemType.Equipment && transform.parent.parent.GetComponent<CreatePanel>() == CreatePanel.Instance)
{
string content = transform.GetChild(0).GetComponent<ItemUI>().Item.GetToolTipContent();
InventoryManager.Instance.ShowToolTipContentLeft(content);
string forge = transform.GetChild(0).GetComponent<ItemUI>().Item.GetForgeTipContent();
InventoryManager.Instance.ShowForgeTipContent(forge);
}
//当指向背包和装备栏中的装备时
else if (transform.GetChild(0).GetComponent<ItemUI>().Item.type == Item.ItemType.Equipment && transform.parent.parent.GetComponent<CreatePanel>() != CreatePanel.Instance)
{
string content = transform.GetChild(0).GetComponent<ItemUI>().Item.GetToolTipContent() + string.Format("\n<color=white>耐久度:</color><color=green>{0}</color>", transform.GetChild(0).GetComponent<ItemUI>().durability);
InventoryManager.Instance.ShowToolTipContentRight(content);
}
//当指向仓库中的材料时
else if (transform.parent.parent.GetComponent<StoragePanel>() == StoragePanel.Instance)
{
string content = transform.GetChild(0).GetComponent<ItemUI>().Item.GetToolTipContent();
InventoryManager.Instance.ShowToolTipContentLeft(content);
}
//当指向背包中的材料时
else
{
string content = transform.GetChild(0).GetComponent<ItemUI>().Item.GetToolTipContent();
InventoryManager.Instance.ShowToolTipContentRight(content);
}
}
}
public void OnPointerExit(PointerEventData eventData)
{
if (transform.childCount > 0)
{
if (transform.GetChild(0).GetComponent<ItemUI>().Item.type == Item.ItemType.Equipment)
InventoryManager.Instance.HideForgeTipContent();
InventoryManager.Instance.HideToolTipContent();
}
}
}
为需要实现库存功能的格子Button添加Slot组件:
ItemUI类:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
public class ItemUI : MonoBehaviour
{
public int Amount { get; set; }
public Item Item { get; set; }
private TMP_Text ItemText;
private Image ItemImage;
private Vector3 AnimationScale = new Vector3(1.3f, 1.3f, 1.3f);
private float TargetScale = 1;
private float Smoothing = 3.0f;
public float durability = 100f;
private void Awake()
{
ItemText = GetComponentInChildren<TMP_Text>();
ItemImage = GetComponent<Image>();
}
private void Update()
{
if (transform.localScale.x != TargetScale)
{
float scale = Mathf.Lerp(transform.localScale.x, TargetScale, Smoothing * Time.deltaTime);
transform.localScale = new Vector3(scale, scale, scale);
if (Mathf.Abs(AnimationScale.x - TargetScale) <= 0.03f)
AnimationScale = Vector3.one;
}
}
/// <summary>
/// 设置当前物品信息
/// </summary>
/// <param name="item"></param>
/// <param name="amount"></param>
public void SetItem(Item item, int amount = 1)
{
transform.localScale = AnimationScale;
Item = item;
Amount = amount;
//更新UI
ItemImage.sprite = Resources.Load<Sprite>(item.sprite);
if (item.capacity > 1)
ItemText.text = amount.ToString();
else
ItemText.text = "";
}
public void ReduceAmount(int amount = 1)
{
transform.localScale = AnimationScale;
Amount -= amount;
//更新UI
if (Item.capacity > 1)
ItemText.text = Amount.ToString();
else
ItemText.text = "";
}
public void AddAmount(int amount = 1)
{
transform.localScale = AnimationScale;
Amount += amount;
//更新UI
if (Item.capacity > 1)
ItemText.text = Amount.ToString();
else
ItemText.text = "";
}
public void SetAmount(int amount)
{
transform.localScale = AnimationScale;
Amount = amount;
if (Item.capacity > 1)
ItemText.text = Amount.ToString();
else
ItemText.text = "";
}
}
将挂载ItemUI组件的对象制成预制体,供Slot类动态加载:
交易系统
游戏内的交易通过购买面板和售卖面板两个单例实现。
购买面板:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
public class BuyPanel : MonoBehaviour
{
//单例
private static BuyPanel instance;
public static BuyPanel Instance
{
get
{
return instance;
}
}
public Button btnBuy;
public Button btnBuyClose;
public TMP_Text txtStandardPrice;
public TMP_Text txtTotal;
public TMP_InputField txtPrice;
public TMP_InputField txtQuantity;
public float topCoefficient = 0.5f;
public float bottomCoefficient = 0.5f;
public static int StandardPrice { get; set; }
public static int ItemID { get; set; }
public static int ItemAmount { get; set; }
public static Item item;
public static KnapsackPanel knapsackPanel;
public static GameObject slot;
private int result = 0;
private int total = 0;
private int quantity = 0;
private int price = 0;
private void Start()
{
//默认以标准价格购买1个物品
price = StandardPrice;
txtPrice.text = price.ToString();
quantity = 1;
txtQuantity.text = quantity.ToString();
total = price * quantity;
txtTotal.text = "总价:" + total;
txtPrice.onValueChanged.AddListener((string value) =>
{
//保证输入为数字
if (int.TryParse(value, out result) && int.TryParse(txtQuantity.text, out result))
{
quantity = int.Parse(txtQuantity.text);
price = int.Parse(value);
total = price * quantity;
//更新界面
txtTotal.text = "总价:" + total;
}
});
txtQuantity.onValueChanged.AddListener((string value) =>
{
//保证输入为数字
if (int.TryParse(txtPrice.text, out result) && int.TryParse(value, out result))
{
quantity = int.Parse(value);
price = int.Parse(txtPrice.text);
//检测购买数量是否大于物品总数或小于1,并进行调整
if (quantity > ItemAmount)
quantity = ItemAmount;
//else if (quantity < 1)
// quantity = 1;
total = price * quantity;
//更新界面
txtQuantity.text = quantity.ToString();
txtTotal.text = "总价:" + total;
}
});
//购买逻辑
btnBuy.onClick.AddListener(() =>
{
//提示面板清空
NoticePanel.Instance.HideNotice();
if (txtPrice.text == "" || txtQuantity.text == "")
NoticePanel.Instance.ShowNotice("请输入购买的单价和数量");
//检测购买价格是否低于标准价格一定倍数,并进行调整
else if (price < (int)(StandardPrice * (1 - bottomCoefficient)))
{
NoticePanel.Instance.ShowNotice("不应与标准价差距过大");
price = (int)(StandardPrice * (1 - bottomCoefficient));
total = price * quantity;
txtPrice.text = price.ToString();
txtTotal.text = "总价:" + total;
}
//检测购买价格是否高于标准价格一定倍数,并进行调整
else if (price > (int)(StandardPrice * (1 + topCoefficient)))
{
NoticePanel.Instance.ShowNotice("不应与标准价差距过大");
price = (int)(StandardPrice * (1 + topCoefficient));
total = price * quantity;
txtPrice.text = price.ToString();
txtTotal.text = "总价:" + total;
}
else if (ShopPanel.Instance.ConsumeMoney(total))
{
//角色背包物品减少
knapsackPanel.ReduceItem(item, ItemAmount, quantity);
//更新角色背包剩余物品数量
ItemAmount -= quantity;
//仓库物品增加
for (int i = 0; i < quantity; i++)
StoragePanel.Instance.StoreItem(item);
//角色金钱增加
knapsackPanel.rolePanel.EarnMoney(total);
//根据角色背包剩余物品数量限制购买数量
if (ItemAmount < quantity)
{
quantity = ItemAmount;
total = price * quantity;
//更新界面
txtQuantity.text = quantity.ToString();
txtTotal.text = "总价:" + total;
}
//整理背包和仓库
knapsackPanel.InventoryClear();
StoragePanel.Instance.InventoryClear();
}
});
btnBuyClose.onClick.AddListener(() =>
{
//提示面板清空
NoticePanel.Instance.HideNotice();
CancelBuy();
});
txtStandardPrice.text = "标准价格:" + StandardPrice;
}
private void Update()
{
}
/// <summary>
/// 实例化面板对象
/// </summary>
/// <param name="slot"></param>
public static void CreateBuy(GameObject slot)
{
//槽内没有物品或是和已打开购买面板的物品属于同种物品
if (slot.transform.childCount == 0 || slot.transform.GetChild(0).GetComponent<ItemUI>().Item.id == ItemID)
return;
//点击的物品是装备类型
else if (slot.transform.GetChild(0).GetComponent<ItemUI>().Item.type == Item.ItemType.Equipment)
return;
if (instance == null)
{
item = slot.transform.GetChild(0).GetComponent<ItemUI>().Item;
ItemID = slot.GetComponent<Slot>().GetItemID();
ItemAmount = slot.transform.parent.parent.GetComponent<KnapsackPanel>().CountSameIDSlot(item);
StandardPrice = item.standardPrice;
knapsackPanel = slot.transform.parent.parent.GetComponent<KnapsackPanel>();
GameObject res = Resources.Load<GameObject>("Panels/BuyPanel");
GameObject obj = Instantiate(res);
//设置父对象
obj.transform.SetParent(GameObject.Find("Canvas/Panels").transform, false);
instance = obj.GetComponent<BuyPanel>();
}
else
{
CancelBuy();
CreateBuy(slot);
}
}
public static void CancelBuy()
{
if (instance != null)
{
Destroy(instance.gameObject);
instance = null;
}
StandardPrice = 0;
ItemID = 0;
ItemAmount = 0;
}
}
售卖面板:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using TMPro;
using System;
using UnityEngine.Events;
public class SellPanel : MonoBehaviour
{
//单例
private static SellPanel instance;
public static SellPanel Instance
{
get
{
return instance;
}
}
public Button btnSell;
public Button btnSellClose;
public TMP_Text txtStandardPrice;
public TMP_InputField txtPrice;
public float topCoefficient = 0.5f;
public float bottomCoefficient = 0.5f;
public static int StandardPrice { get; set; }
public static int ItemID { get; set; }
public static Item item;
public static KnapsackPanel knapsackPanel;
public static RolePanel rolePanel;
public static GameObject slot;
private static int choose = 1;
UnityAction a1;
// Start is called before the first frame update
void Start()
{
btnSell.onClick.AddListener(() =>
{
//提示面板清空
NoticePanel.Instance.HideNotice();
if (txtPrice.text == "")
NoticePanel.Instance.ShowNotice("请输入卖出的价格");
//检测售出价格是否低于标准价格一定倍数,并进行调整
else if (int.Parse(txtPrice.text) < (int)(StandardPrice * (1 - bottomCoefficient)))
{
NoticePanel.Instance.ShowNotice("不应与标准价差距过大");
txtPrice.text = ((int)(StandardPrice * (1 - bottomCoefficient))).ToString();
}
//检测售出价格是否高于标准价格一定倍数,并进行调整
else if (int.Parse(txtPrice.text) > (int)(StandardPrice * (1 + topCoefficient)))
{
NoticePanel.Instance.ShowNotice("不应与标准价差距过大");
txtPrice.text = ((int)(StandardPrice * (1 + topCoefficient))).ToString();
}
else if (rolePanel.ConsumeMoney(int.Parse(txtPrice.text)))
{
//制造装备并添加到角色背包
CreatePanel.Instance.ForgeItem(ItemID);
//商店增加金钱
ShopPanel.Instance.EarnMoney(int.Parse(txtPrice.text));
//整理背包
knapsackPanel.InventoryClear();
//穿戴装备
knapsackPanel.Equip();
CancelSell();
}
//判断是否能继续制造
if (!CreatePanel.Instance.CanForge(ItemID))
CancelSell();
});
btnSellClose.onClick.AddListener(() =>
{
//提示面板清空
NoticePanel.Instance.HideNotice();
CancelSell();
});
txtStandardPrice.text = "标准价格:" + StandardPrice;
}
// Update is called once per frame
void Update()
{
}
/// <summary>
/// 实例化面板对象
/// </summary>
/// <param name="slot"></param>
public static void CreateSell(GameObject slot)
{
//点击相同的装备
if (slot.transform.GetChild(0).GetComponent<ItemUI>().Item.id == ItemID)
return;
if (CreatePanel.Instance.CanForge(slot.GetComponent<Slot>().GetItemID()))
{
item = slot.transform.GetChild(0).GetComponent<ItemUI>().Item;
ItemID = slot.GetComponent<Slot>().GetItemID();
StandardPrice = item.standardPrice;
if (GameObject.Find("Role1Panel").GetComponent<CanvasGroup>().alpha == 1)
{
choose = 1;
}
else if (GameObject.Find("Role2Panel").GetComponent<CanvasGroup>().alpha == 1)
{
choose = 2;
}
else if (GameObject.Find("Role3Panel").GetComponent<CanvasGroup>().alpha == 1)
choose = 3;
switch (choose)
{
case 1:
knapsackPanel = GameObject.Find("Knapsack1Panel").GetComponent<KnapsackPanel>();
rolePanel = GameObject.Find("Role1Panel").GetComponent<RolePanel>();
break;
case 2:
knapsackPanel = GameObject.Find("Knapsack2Panel").GetComponent<KnapsackPanel>();
rolePanel = GameObject.Find("Role2Panel").GetComponent<RolePanel>();
break;
case 3:
knapsackPanel = GameObject.Find("Knapsack3Panel").GetComponent<KnapsackPanel>();
rolePanel = GameObject.Find("Role3Panel").GetComponent<RolePanel>();
break;
}
}
else
{
if (instance != null)
CancelSell();
return;
}
if (instance == null)
{
GameObject res = Resources.Load<GameObject>("Panels/SellPanel");
GameObject obj = Instantiate(res);
//设置父对象
obj.transform.SetParent(GameObject.Find("Canvas/Panels").transform, false);
instance = obj.GetComponent<SellPanel>();
}
else
{
CancelSell();
CreateSell(slot);
}
}
public static void CancelSell()
{
if (instance != null)
{
Destroy(instance.gameObject);
instance = null;
}
StandardPrice = 0;
ItemID = 0;
}
}
游戏循环
游戏内循环通过Animator Controller的有限状态机实现,并通过FSM脚本与状态机外的其他对象进行通信。