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.