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