Unity模拟经营类游戏Demo部分代码及技术总结
Posted 跨越七海之风3504
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity模拟经营类游戏Demo部分代码及技术总结相关的知识,希望对你有一定的参考价值。
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 Sys餐厅经营模拟游戏实战项目
项目介绍
实现了一个游戏的餐厅经营模拟,实现了厨师、顾客等角色的关键操作,完成从顾客等位、点菜、烹饪、用餐、支付的一系列状态变更的数据、信息、交互、展现的变化及处理。涉及的前端知识有移动端 HTML 页面布局及样式实现。
项目上线地址:http://hikari.top:8102/
项目效果图
项目中角色的状态介绍:
状态名称 状态描述 free 未开工,等待顾客点菜 working 已接单,正在做菜 speeding 被顾客疯狂点击,加速做菜一段时间 complete 完成当前菜的制作,等待客人确认
![](https://image.cha138.com/20220203/b5f60242c62744acb813768a505168ab.jpg)
状态名称 状态描述 waiting 位于等待队列,等待就坐 free 当前座位没有人(属于餐厅内座位的状态) siting 已下单,等待厨师接单并制作 eating 正在进食 appease 所有菜都超时,需要安抚后离开 pay 点的菜已经吃完,准备结账
![](https://image.cha138.com/20220203/7c95258e0d954b63838708e497747d51.jpg)
状态名称 状态描述 waiting 顾客已下单,等待厨师接单 doing 厨师已接单,正在做菜 completed 厨师已做完,等待顾客接单 eating 正在被顾客食用 eatup 已被顾客吃完 destroy 等待时间已超时
![](https://image.cha138.com/20220203/2af6100a100345ad976578bab0a3a14d.jpg)
部分游戏设定:
需求文档中的内容基本全部完成,这里介绍一些比较重要的部分和自设定的部分。
时间设定
-
每周7天,每天6小时,每小时30秒
-
头部的时间栏背景色设为动态变化的,用来提示距离下一天的时间。
-
每小时结束时自动收集还未主动收集的金币,并将顾客请走。
-
每一天结束时自动安抚还未主动安抚的顾客,并将顾客请走。
-
每周结束后自动结算厨师工资,每天¥20,若周内某天没有工作,则当天没有工资(万恶的资本家!)。
顾客设定
-
顾客从预先设定的18个候选顾客中随机产生。
-
游戏开始一秒后会来第一个顾客,随后每隔三秒会随机产生一名顾客,顾客可能来,也可能不来。
-
每个顾客每天只能来餐厅一回。等待队列最长为5个,顾客到了之后若等待人数超过5人,则会离开,今天之内不会再来。
-
点击等待队列后顾客立即从队列中去除,先出现菜单栏,点菜之后就坐。
厨师设定
-
厨师开始做菜时,减去做菜的成本,当成本大于现有现金时,显示为负值
-
解雇厨师时,支付厨师¥140
-
厨师做好菜后给顾客上菜,如果该顾客等待已经超时,则等待5秒,若还是没有其他顾客点这个菜,就将这个菜作废掉,进入下一个状态。
-
顾客点击厨师头像可以加速当前菜的制作,同时厨师背景发生改变,一段时间后变回来,每个顾客最多只能让同一个厨师加速一次
食物设定
-
食物的价格和和成本都是预先设定的,各不相同,每个菜成本大约为价格的一半。
-
顾客食用食物的速度也不相同,但比等待食物的速度要快。
-
顾客用餐是串行的,只有完成一个菜的用餐才会进行下一个菜,用餐顺序按上菜顺序进行。
优化及适配
- 已在谷歌浏览器和vivo z3真机中做过测试,显示效果良好
- 项目用vue框架并使用vue-cli脚手架进行开发,使用vuex进行全局状态管理,并使用Apache2在远程服务器上进行了部署上线。
- 背景图进行了压缩,最终大小只有80k左右
以上是关于Unity模拟经营类游戏Demo部分代码及技术总结的主要内容,如果未能解决你的问题,请参考以下文章