인벤토리 및 ui 로직 변경

This commit is contained in:
NTG_Lenovo 2025-08-06 15:02:37 +09:00
parent ec6afcd2ae
commit 2084bbcb32
5 changed files with 123 additions and 49 deletions

View File

@ -6,11 +6,20 @@
namespace DDD namespace DDD
{ {
public enum InventorySortType
{
None = 0,
NameAscending,
NameDescending,
QuantityAscending,
QuantityDescending
}
public class InventoryManager : Singleton<InventoryManager>, IManager public class InventoryManager : Singleton<InventoryManager>, IManager
{ {
[Title("아이템 전체 목록")] [Title("아이템 전체 목록")]
[ShowInInspector, ReadOnly] [ShowInInspector, ReadOnly]
private Dictionary<string, ItemData> _itemDataLookup; private Dictionary<string, ItemData> _allItemDataLookup;
[Title("아이템 보유 목록")] [Title("아이템 보유 목록")]
[ShowInInspector, ReadOnly] [ShowInInspector, ReadOnly]
@ -41,7 +50,7 @@ private void InitializeItemData()
var itemDataSo = DataManager.Instance.GetDataSo<ItemDataSo>(); var itemDataSo = DataManager.Instance.GetDataSo<ItemDataSo>();
Debug.Assert(itemDataSo != null, "itemDataSo != null"); Debug.Assert(itemDataSo != null, "itemDataSo != null");
_itemDataLookup = itemDataSo.GetDataList() _allItemDataLookup = itemDataSo.GetDataList()
.Where(item => !string.IsNullOrEmpty(item.Id)) .Where(item => !string.IsNullOrEmpty(item.Id))
.ToDictionary(item => item.Id, item => item); .ToDictionary(item => item.Id, item => item);
@ -72,7 +81,7 @@ private async void ApplyEditorTestData()
public bool AddItem(string id, int quantity = 1) public bool AddItem(string id, int quantity = 1)
{ {
if (!_itemDataLookup.ContainsKey(id)) if (!_allItemDataLookup.ContainsKey(id))
{ {
Debug.LogError($"[Inventory] 등록되지 않은 아이템 ID: {id}"); Debug.LogError($"[Inventory] 등록되지 않은 아이템 ID: {id}");
return false; return false;
@ -123,12 +132,14 @@ public bool RemoveItem(string id, int quantity = 1)
return true; return true;
} }
public IReadOnlyDictionary<string, ItemData> AllItemDataLookup => _allItemDataLookup;
public IReadOnlyDictionary<string, InventoryItemData> InventoryItems => _inventoryItemDatas; public IReadOnlyDictionary<string, InventoryItemData> InventoryItems => _inventoryItemDatas;
public bool TryGetItemData(string id, out ItemData itemData) => _itemDataLookup.TryGetValue(id, out itemData); public bool ContainInventoryItem(string id) => _inventoryItemDatas.ContainsKey(id);
public bool TryGetInventoryItemData(string id, out InventoryItemData inventoryItemData) => _inventoryItemDatas.TryGetValue(id, out inventoryItemData);
public int GetItemCount(string id) => _inventoryItemDatas.TryGetValue(id, out var itemData) ? itemData.Quantity : 0; public int GetItemCount(string id) => _inventoryItemDatas.TryGetValue(id, out var itemData) ? itemData.Quantity : 0;
public ItemData GetItemDataByIdOrNull(string id) public ItemData GetItemDataByIdOrNull(string id)
{ {
_itemDataLookup.TryGetValue(id, out var itemData); _allItemDataLookup.TryGetValue(id, out var itemData);
return itemData; return itemData;
} }
} }

View File

@ -8,11 +8,34 @@ public class InventorySlotUiStrategy : IItemSlotUiStrategy
public string AnimatorControllerKey => "InventorySlotUi"; public string AnimatorControllerKey => "InventorySlotUi";
public void Setup(ItemSlotUi ui, ItemViewModel model) public void Setup(ItemSlotUi ui, ItemViewModel model)
{
if (InventoryManager.Instance.ContainInventoryItem(model.Id))
{ {
ui.SetIcon(model.ItemSprite); ui.SetIcon(model.ItemSprite);
ui.ShowCountText(); ui.ShowCountText();
ui.HideMark(); ui.HideMark();
ui.SetButtonInteractable(true); ui.SetButtonInteractable(true);
return;
}
// TODO : 임시 초기화 값
string emptySpriteKey = SpriteConstants.EmptyFoodSpriteKey;
if (model.ItemType == ItemType.Recipe)
{
if (model.RecipeType == RecipeType.FoodRecipe)
{
emptySpriteKey = SpriteConstants.EmptyFoodSpriteKey;
}
else if (model.RecipeType == RecipeType.DrinkRecipe)
{
emptySpriteKey = SpriteConstants.EmptyDrinkSpriteKey;
}
}
ui.SetIcon(DataManager.Instance.GetSprite(emptySpriteKey));
ui.HideCountText();
ui.HideMark();
ui.SetButtonInteractable(false);
} }
public async Task<RuntimeAnimatorController> GetAnimatorController() public async Task<RuntimeAnimatorController> GetAnimatorController()
@ -22,11 +45,13 @@ public async Task<RuntimeAnimatorController> GetAnimatorController()
public void OnInventoryChanged(ItemSlotUi ui) public void OnInventoryChanged(ItemSlotUi ui)
{ {
if (ui.Model == null) return; var model = ui.Model;
if (InventoryManager.Instance.ContainInventoryItem(model.Id))
{
ui.Model.UpdateCount(); ui.Model.UpdateCount();
ui.ShowCountText(); ui.ShowCountText();
} }
}
public bool CanCrafting(ItemSlotUi ui) public bool CanCrafting(ItemSlotUi ui)
{ {

View File

@ -1,12 +1,12 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using UnityEngine; using UnityEngine;
using UnityEngine.AddressableAssets;
namespace DDD namespace DDD
{ {
public class InventoryView : MonoBehaviour, IEventHandler<InventoryChangedEvent>, IEventHandler<TodayMenuAddedEvent>, IEventHandler<TodayMenuRemovedEvent> public class InventoryView : MonoBehaviour, IEventHandler<InventoryChangedEvent>,
IEventHandler<TodayMenuAddedEvent>, IEventHandler<TodayMenuRemovedEvent>
{ {
[SerializeField] private Transform _slotParent; [SerializeField] private Transform _slotParent;
@ -15,6 +15,7 @@ public class InventoryView : MonoBehaviour, IEventHandler<InventoryChangedEvent>
private readonly Dictionary<string, ItemSlotUi> _slotLookup = new(); private readonly Dictionary<string, ItemSlotUi> _slotLookup = new();
private GameObject _firstSlot; private GameObject _firstSlot;
private InventorySortType _currentSortType = InventorySortType.None;
private const string ItemSlotUiName = "ItemSlotUi_"; private const string ItemSlotUiName = "ItemSlotUi_";
@ -33,9 +34,11 @@ private void OnDisable()
} }
public GameObject GetInitialSelected() => _firstSlot; public GameObject GetInitialSelected() => _firstSlot;
public async Task Initialize() public async Task Initialize()
{ {
_restaurantManagementSo = await AssetManager.LoadAsset<RestaurantManagementSo>(DataConstants.RestaurantManagementSo); _restaurantManagementSo =
await AssetManager.LoadAsset<RestaurantManagementSo>(DataConstants.RestaurantManagementSo);
Debug.Assert(_restaurantManagementSo != null, "_todayMenuDataSo != null"); Debug.Assert(_restaurantManagementSo != null, "_todayMenuDataSo != null");
Clear(); Clear();
@ -67,33 +70,57 @@ public async Task Initialize()
} }
} }
public void SetSortType(InventorySortType sortType)
{
_currentSortType = sortType;
UpdateView();
}
private IEnumerable<ItemSlotUi> SortSlots(IEnumerable<ItemSlotUi> slots)
{
return _currentSortType switch
{
InventorySortType.NameAscending => slots.OrderByDescending(slot => slot.Model.HasItem).ThenBy(slot => slot.Model.DisplayName),
InventorySortType.NameDescending => slots.OrderByDescending(slot => slot.Model.HasItem).ThenByDescending(slot => slot.Model.DisplayName),
InventorySortType.QuantityAscending => slots.OrderByDescending(slot => slot.Model.HasItem).ThenBy(slot => slot.Model.Count),
InventorySortType.QuantityDescending => slots.OrderByDescending(slot => slot.Model.HasItem).ThenByDescending(slot => slot.Model.Count),
InventorySortType.None => slots.OrderBy(slot => slot.Model.Id),
_ => slots
};
}
public void UpdateCategoryView(InventoryCategoryType category) public void UpdateCategoryView(InventoryCategoryType category)
{ {
_currenInventoryCategoryType = category; _currenInventoryCategoryType = category;
_firstSlot = null; _firstSlot = null;
foreach (var kvp in _slotLookup)
var filteredSlots = _slotLookup.Values;
var sortedSlots = SortSlots(filteredSlots);
int siblingIndex = 0;
foreach (var slot in sortedSlots)
{ {
var id = kvp.Key;
var slot = kvp.Value;
var model = slot.Model; var model = slot.Model;
string id = model.Id;
// 1. 오늘의 메뉴에 등록된 경우 필터링
bool isRegisteredTodayMenu = model.ItemType == ItemType.Recipe && _restaurantManagementSo.IsContainTodayMenu(id); bool isRegisteredTodayMenu = model.ItemType == ItemType.Recipe && _restaurantManagementSo.IsContainTodayMenu(id);
// 2. 현재 선택된 카테고리에 맞는지 필터링
bool matchCategory = MatchesCategory(model, _currenInventoryCategoryType); bool matchCategory = MatchesCategory(model, _currenInventoryCategoryType);
// 3. 조건을 모두 만족할 경우만 활성화
bool shouldShow = !isRegisteredTodayMenu && matchCategory; bool shouldShow = !isRegisteredTodayMenu && matchCategory;
slot.SetActive(shouldShow); slot.SetActive(shouldShow);
if (shouldShow && _firstSlot == null) if (shouldShow && model.HasItem)
{
slot.transform.SetSiblingIndex(siblingIndex++);
if (_firstSlot == null)
{ {
_firstSlot = slot.gameObject; _firstSlot = slot.gameObject;
} }
} }
} }
}
private bool MatchesCategory(ItemViewModel model, InventoryCategoryType category) private bool MatchesCategory(ItemViewModel model, InventoryCategoryType category)
{ {
@ -102,15 +129,19 @@ private bool MatchesCategory(ItemViewModel model, InventoryCategoryType category
case InventoryCategoryType.Food: case InventoryCategoryType.Food:
if (model.ItemType != ItemType.Recipe) return false; if (model.ItemType != ItemType.Recipe) return false;
return DataManager.Instance.GetDataSo<RecipeDataSo>().TryGetDataById(model.Id, out var foodRecipe) && foodRecipe.RecipeType == RecipeType.FoodRecipe; return DataManager.Instance.GetDataSo<RecipeDataSo>()
.TryGetDataById(model.Id, out var foodRecipe) && foodRecipe.RecipeType == RecipeType.FoodRecipe;
case InventoryCategoryType.Drink: case InventoryCategoryType.Drink:
if (model.ItemType != ItemType.Recipe) return false; if (model.ItemType != ItemType.Recipe) return false;
return DataManager.Instance.GetDataSo<RecipeDataSo>().TryGetDataById(model.Id, out var drinkRecipe) && drinkRecipe.RecipeType == RecipeType.DrinkRecipe; return DataManager.Instance.GetDataSo<RecipeDataSo>()
.TryGetDataById(model.Id, out var drinkRecipe) &&
drinkRecipe.RecipeType == RecipeType.DrinkRecipe;
case InventoryCategoryType.Ingredient: case InventoryCategoryType.Ingredient:
return model.ItemType == ItemType.Ingredient; return model.ItemType == ItemType.Ingredient;
case InventoryCategoryType.Cookware: case InventoryCategoryType.Cookware:
return DataManager.Instance.GetDataSo<CookwareDataSo>().TryGetDataById(model.Id, out var cookwareData); return DataManager.Instance.GetDataSo<CookwareDataSo>()
.TryGetDataById(model.Id, out var cookwareData);
case InventoryCategoryType.Special: case InventoryCategoryType.Special:
return false; return false;
default: default:

View File

@ -25,6 +25,8 @@ public ItemViewModel(string id, ItemType itemType)
Count = 0; Count = 0;
} }
public bool HasItem => Count > 0;
public string DisplayName => LocalizationManager.Instance.GetName(Id);
public RecipeType RecipeType => ItemType == ItemType.Recipe ? DataManager.Instance.GetDataSo<RecipeDataSo>().GetDataById(Id).RecipeType : RecipeType.None; public RecipeType RecipeType => ItemType == ItemType.Recipe ? DataManager.Instance.GetDataSo<RecipeDataSo>().GetDataById(Id).RecipeType : RecipeType.None;
public Sprite ItemSprite public Sprite ItemSprite
{ {

View File

@ -7,18 +7,23 @@ public static class ItemViewModelFactory
public static List<ItemViewModel> CreateRestaurantManagementInventoryItem() public static List<ItemViewModel> CreateRestaurantManagementInventoryItem()
{ {
var result = new List<ItemViewModel>(); var result = new List<ItemViewModel>();
foreach (var kvp in InventoryManager.Instance.InventoryItems) var allItemDataLookup = InventoryManager.Instance.AllItemDataLookup;
foreach (var keyItemDataPair in allItemDataLookup)
{ {
var id = kvp.Key; var id = keyItemDataPair.Key;
var item = InventoryManager.Instance.GetItemDataByIdOrNull(id); var itemData = keyItemDataPair.Value;
if (item == null) continue;
var modelCount = item.ItemType switch int modelCount = 0;
if (InventoryManager.Instance.ContainInventoryItem(id))
{ {
ItemType.Recipe => CalculateCraftableCount(item.Id), modelCount = itemData.ItemType switch
{
ItemType.Recipe => CalculateCraftableCount(itemData.Id),
_ => InventoryManager.Instance.GetItemCount(id) _ => InventoryManager.Instance.GetItemCount(id)
}; };
var model = new ItemViewModel(item.Id, item.ItemType, modelCount); }
var model = new ItemViewModel(id, itemData.ItemType, modelCount);
result.Add(model); result.Add(model);
} }