From 4ca10808a966a7af358617b1062ada3b9cea9126 Mon Sep 17 00:00:00 2001 From: NTG Date: Sun, 24 Aug 2025 20:44:32 +0900 Subject: [PATCH] =?UTF-8?q?ui=20=EB=A1=9C=EC=A7=81=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CustomerTable/CustomerTable.png.meta | 13 + Assets/_DDD/_Scripts/GameUi/BaseUi/BaseUi.cs | 75 +-- .../_Scripts/GameUi/BaseUi/BaseViewModelUi.cs | 8 +- .../GameUi/BaseUi/CommonUis/FadeUi.cs | 19 +- .../BaseUi/CommonUis/GlobalMessageUi.cs | 29 +- .../GameUi/BaseUi/Huds/RestaurantHud.cs | 7 +- .../InteractionUis/InteractionMessageUi.cs | 8 +- .../GameUi/BaseUi/PopupUis/BasePopupUi.cs | 31 +- .../GameUi/BaseUi/PopupUis/PopupUi.cs | 8 +- .../RestaurantManagementUi/ChecklistView.cs | 28 +- .../InventoryUi/InventoryView.cs | 37 +- .../RestaurantManagementUi/ItemDetailView.cs | 164 ++---- .../RestaurantManagementUi.cs | 142 +++-- .../RestaurantManagementViewModel.cs | 195 ++++++- .../TodayMenuUi/TodayMenuView.cs | 22 +- .../TodayRestaurantStateView.cs | 97 +--- .../PopupUis/{PopupUiState.cs => UiState.cs} | 0 .../{PopupUiState.cs.meta => UiState.cs.meta} | 0 Assets/_DDD/_Scripts/GameUi/UiGuideline | 545 ------------------ Assets/_DDD/_Scripts/GameUi/UiGuideline.meta | 3 - Assets/_DDD/_Scripts/GameUi/Utils/IUiView.cs | 9 + .../_Scripts/GameUi/Utils/IUiView.cs.meta | 3 + .../DataObjects/RestaurantManagementData.cs | 1 + Assets/_DDD/_Scripts/Utilities/Utils.cs | 9 + 24 files changed, 495 insertions(+), 958 deletions(-) rename Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/{PopupUiState.cs => UiState.cs} (100%) rename Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/{PopupUiState.cs.meta => UiState.cs.meta} (100%) delete mode 100644 Assets/_DDD/_Scripts/GameUi/UiGuideline delete mode 100644 Assets/_DDD/_Scripts/GameUi/UiGuideline.meta create mode 100644 Assets/_DDD/_Scripts/GameUi/Utils/IUiView.cs create mode 100644 Assets/_DDD/_Scripts/GameUi/Utils/IUiView.cs.meta diff --git a/Assets/_DDD/Restaurant/Environments/Props/CustomerTable/CustomerTable.png.meta b/Assets/_DDD/Restaurant/Environments/Props/CustomerTable/CustomerTable.png.meta index d752622f8..dca41ef80 100644 --- a/Assets/_DDD/Restaurant/Environments/Props/CustomerTable/CustomerTable.png.meta +++ b/Assets/_DDD/Restaurant/Environments/Props/CustomerTable/CustomerTable.png.meta @@ -119,6 +119,19 @@ TextureImporter: ignorePlatformSupport: 0 androidETC2FallbackOverride: 0 forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: iOS + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 spriteSheet: serializedVersion: 2 sprites: [] diff --git a/Assets/_DDD/_Scripts/GameUi/BaseUi/BaseUi.cs b/Assets/_DDD/_Scripts/GameUi/BaseUi/BaseUi.cs index 90cd7f2c6..669077e61 100644 --- a/Assets/_DDD/_Scripts/GameUi/BaseUi/BaseUi.cs +++ b/Assets/_DDD/_Scripts/GameUi/BaseUi/BaseUi.cs @@ -1,10 +1,5 @@ -using System; using System.ComponentModel; -using System.Linq; -using System.Reflection; -using TMPro; using UnityEngine; -using UnityEngine.UI; namespace DDD { @@ -34,54 +29,42 @@ public abstract class BaseUi : MonoBehaviour protected virtual void Awake() { _canvasGroup = GetComponent(); - _panel = transform.Find(CommonConstants.Panel)?.gameObject; - _blockImage = transform.Find(CommonConstants.BlockImage)?.gameObject; - - _bindingContext = new BindingContext(); - SetupBindings(); - } - - protected virtual void OnEnable() - { + _panel = transform.Find(CommonConstants.Panel).gameObject; + _blockImage = transform.Find(CommonConstants.BlockImage).gameObject; + if (_enableBlockImage) + { + _blockImage.SetActive(false); + } + + _panel.SetActive(false); } - protected virtual void Start() - { - ClosePanel(); - } - - protected virtual void Update() - { - - } - - protected virtual void OnDisable() - { - - } + protected virtual void Start() { } + protected virtual void Update() { } protected virtual void OnDestroy() { - TryUnregister(); _bindingContext?.Dispose(); - } - - public virtual void CreateInitialize() - { - TryRegister(); - } - - protected virtual void TryRegister() - { - UiManager.Instance.UiState.RegisterUI(this); - } - - protected virtual void TryUnregister() - { + UiManager.Instance.UiState.UnregisterUI(this); } + public void CreateInitialize() + { + OnCreatedInitialize(); + } + + protected virtual void OnCreatedInitialize() + { + UiManager.Instance.UiState.RegisterUI(this); + + _bindingContext = new BindingContext(); + SetupBindings(); + } + protected virtual void OnOpenedEvents() { } + protected virtual void OnClosedEvents() { } + // BaseUi 메서드들을 직접 구현 public virtual void OpenPanel() { @@ -91,6 +74,8 @@ public virtual void OpenPanel() } _panel.SetActive(true); + + OnOpenedEvents(); } public virtual void ClosePanel() @@ -101,12 +86,14 @@ public virtual void ClosePanel() } _panel.SetActive(false); + OnClosedEvents(); + IsInitialized = false; } public virtual void SetUiInteractable(bool active) { - if (_canvasGroup != null) + if (_canvasGroup) { _canvasGroup.interactable = active; _canvasGroup.blocksRaycasts = active; diff --git a/Assets/_DDD/_Scripts/GameUi/BaseUi/BaseViewModelUi.cs b/Assets/_DDD/_Scripts/GameUi/BaseUi/BaseViewModelUi.cs index 6a3a6b243..41a2ef426 100644 --- a/Assets/_DDD/_Scripts/GameUi/BaseUi/BaseViewModelUi.cs +++ b/Assets/_DDD/_Scripts/GameUi/BaseUi/BaseViewModelUi.cs @@ -11,9 +11,9 @@ protected override void Awake() _viewModel = GetComponent(); } - protected override void OnEnable() + protected override void OnOpenedEvents() { - base.OnEnable(); + base.OnOpenedEvents(); if (_viewModel && _bindingContext != null) { @@ -22,9 +22,9 @@ protected override void OnEnable() } } - protected override void OnDisable() + protected override void OnClosedEvents() { - base.OnDisable(); + base.OnClosedEvents(); if (_viewModel != null) { diff --git a/Assets/_DDD/_Scripts/GameUi/BaseUi/CommonUis/FadeUi.cs b/Assets/_DDD/_Scripts/GameUi/BaseUi/CommonUis/FadeUi.cs index 41c49a618..e9ea42a6f 100644 --- a/Assets/_DDD/_Scripts/GameUi/BaseUi/CommonUis/FadeUi.cs +++ b/Assets/_DDD/_Scripts/GameUi/BaseUi/CommonUis/FadeUi.cs @@ -5,27 +5,24 @@ namespace DDD { public class FadeUi : BaseUi, IEventHandler, IEventHandler { - protected override void Awake() + protected override void OnDestroy() { - base.Awake(); + base.OnDestroy(); - _canvasGroup.alpha = 0f; + EventBus.Unregister(this); + EventBus.Unregister(this); } - protected override void TryRegister() + protected override void OnCreatedInitialize() { - base.TryRegister(); + base.OnCreatedInitialize(); + + _canvasGroup.alpha = 0f; EventBus.Register(this); EventBus.Register(this); } - protected override void TryUnregister() - { - EventBus.Unregister(this); - EventBus.Unregister(this); - } - public void Invoke(FadeInEvent evt) { _ = FadeInAsync(evt); diff --git a/Assets/_DDD/_Scripts/GameUi/BaseUi/CommonUis/GlobalMessageUi.cs b/Assets/_DDD/_Scripts/GameUi/BaseUi/CommonUis/GlobalMessageUi.cs index 099fc779d..7dc140664 100644 --- a/Assets/_DDD/_Scripts/GameUi/BaseUi/CommonUis/GlobalMessageUi.cs +++ b/Assets/_DDD/_Scripts/GameUi/BaseUi/CommonUis/GlobalMessageUi.cs @@ -13,29 +13,24 @@ public class GlobalMessageUi : BaseUi, IEventHandler private readonly Queue _messageQueue = new(); private bool _isDisplayingMessage = false; - protected override void Awake() + protected override void OnDestroy() { - base.Awake(); - - _canvasGroup.alpha = 0; - _messageText.text = null; - } - - protected override void TryRegister() - { - base.TryRegister(); - - EventBus.Register(this); - } - - protected override void TryUnregister() - { - base.TryUnregister(); + base.OnDestroy(); EventBus.Unregister(this); _fadeTween?.Kill(); } + protected override void OnCreatedInitialize() + { + base.OnCreatedInitialize(); + + _canvasGroup.alpha = 0; + _messageText.text = null; + + EventBus.Register(this); + } + public void Invoke(ShowGlobalMessageEvent evt) { _messageQueue.Enqueue(evt); diff --git a/Assets/_DDD/_Scripts/GameUi/BaseUi/Huds/RestaurantHud.cs b/Assets/_DDD/_Scripts/GameUi/BaseUi/Huds/RestaurantHud.cs index a5672ae45..6d0fefe4e 100644 --- a/Assets/_DDD/_Scripts/GameUi/BaseUi/Huds/RestaurantHud.cs +++ b/Assets/_DDD/_Scripts/GameUi/BaseUi/Huds/RestaurantHud.cs @@ -2,6 +2,11 @@ namespace DDD { public class RestaurantHud : BaseUi { - + protected override void OnCreatedInitialize() + { + base.OnCreatedInitialize(); + + OpenPanel(); + } } } \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/GameUi/BaseUi/InteractionUis/InteractionMessageUi.cs b/Assets/_DDD/_Scripts/GameUi/BaseUi/InteractionUis/InteractionMessageUi.cs index 0aff28d0f..13b3bddfb 100644 --- a/Assets/_DDD/_Scripts/GameUi/BaseUi/InteractionUis/InteractionMessageUi.cs +++ b/Assets/_DDD/_Scripts/GameUi/BaseUi/InteractionUis/InteractionMessageUi.cs @@ -25,17 +25,17 @@ protected override void Awake() _filledImage.fillAmount = 0f; } - protected override void TryRegister() + protected override void OnOpenedEvents() { - base.TryRegister(); + base.OnOpenedEvents(); EventBus.Register(this); EventBus.Register(this); } - protected override void TryUnregister() + protected override void OnClosedEvents() { - base.TryUnregister(); + base.OnClosedEvents(); EventBus.Unregister(this); EventBus.Unregister(this); diff --git a/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/BasePopupUi.cs b/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/BasePopupUi.cs index fd9a0be80..3ff61fca8 100644 --- a/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/BasePopupUi.cs +++ b/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/BasePopupUi.cs @@ -10,22 +10,15 @@ public abstract class BasePopupUi : BaseUi protected override void Awake() { - base.Awake(); - - // BasePopupUi의 기본값 적용 _enableBlockImage = true; - } - - protected override void OnEnable() - { - base.OnEnable(); + + base.Awake(); } protected override void Update() { base.Update(); - - // BasePopupUi의 Update 로직 구현 + if (IsOpenPanel() == false || IsInitialized == false) return; var currentSelectedGameObject = EventSystem.current.currentSelectedGameObject; @@ -39,20 +32,20 @@ protected override void Update() } } - protected override void TryRegister() + protected override void OnDestroy() { - base.TryRegister(); - - UiManager.Instance.UiState.RegisterPopupUI(this); - } - - protected override void TryUnregister() - { - base.TryUnregister(); + base.OnDestroy(); UiManager.Instance?.UiState?.UnregisterPopupUI(this); } + protected override void OnCreatedInitialize() + { + base.OnCreatedInitialize(); + + UiManager.Instance.UiState.RegisterPopupUI(this); + } + public virtual void Open(OpenPopupUiEvent evt) { OpenPanel(); diff --git a/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/PopupUi.cs b/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/PopupUi.cs index 947567de3..d6111f94f 100644 --- a/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/PopupUi.cs +++ b/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/PopupUi.cs @@ -35,9 +35,9 @@ protected override void Awake() _viewModel = GetComponent(); } - protected override void TryRegister() + protected override void OnOpenedEvents() { - base.TryRegister(); + base.OnOpenedEvents(); if (_viewModel && _bindingContext != null) { @@ -80,9 +80,9 @@ protected override void TryRegister() InputActionMaps = _uiActionsInputBinding.InputActionMaps; } - protected override void TryUnregister() + protected override void OnClosedEvents() { - base.TryUnregister(); + base.OnClosedEvents(); if (_viewModel != null) { diff --git a/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/RestaurantManagementUi/ChecklistView.cs b/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/RestaurantManagementUi/ChecklistView.cs index bd899f1fc..a3368d1e7 100644 --- a/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/RestaurantManagementUi/ChecklistView.cs +++ b/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/RestaurantManagementUi/ChecklistView.cs @@ -14,30 +14,34 @@ public enum ChecklistLocalizationKey Checklist3, } - public class ChecklistView : MonoBehaviour, IEventHandler, IEventHandler + public class ChecklistView : MonoBehaviour, IUiView, IEventHandler, IEventHandler { [SerializeField] private Transform _parent; private RestaurantManagementViewModel _viewModel; - - private void OnDestroy() - { - EventBus.Unregister(this); - EventBus.Unregister(this); - } public void Initialize(RestaurantManagementViewModel viewModel) { _viewModel = viewModel; - ClearObject(_parent); + Utils.DestroyAllChildren(_parent); _viewModel.CreateChecklist(_parent); + } + + public void OnOpenedEvents() + { UpdateView(); EventBus.Register(this); EventBus.Register(this); } + public void OnClosedEvents() + { + EventBus.Unregister(this); + EventBus.Unregister(this); + } + public void UpdateView() { _viewModel.UpdateChecklistView(); @@ -45,13 +49,5 @@ public void UpdateView() public void Invoke(TodayMenuRemovedEvent evt) => UpdateView(); public void Invoke(TodayMenuAddedEvent evt) => UpdateView(); - - private void ClearObject(Transform parent) - { - foreach (Transform child in _parent) - { - Destroy(child.gameObject); - } - } } } \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/RestaurantManagementUi/InventoryUi/InventoryView.cs b/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/RestaurantManagementUi/InventoryUi/InventoryView.cs index 64668dc55..31f3f85b3 100644 --- a/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/RestaurantManagementUi/InventoryUi/InventoryView.cs +++ b/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/RestaurantManagementUi/InventoryUi/InventoryView.cs @@ -2,7 +2,7 @@ namespace DDD { - public class InventoryView : MonoBehaviour, IEventHandler, + public class InventoryView : MonoBehaviour, IUiView, IEventHandler, IEventHandler, IEventHandler { private RestaurantManagementViewModel _viewModel; @@ -11,25 +11,36 @@ public class InventoryView : MonoBehaviour, IEventHandler public GameObject GetInitialSelected() => _viewModel.GetInitialSelectedByInventory(); - private void OnDestroy() - { - EventBus.Unregister(this); - EventBus.Unregister(this); - EventBus.Unregister(this); - } - public void Initialize(RestaurantManagementViewModel viewModel) { _viewModel = viewModel; + } - ClearObject(_slotParent); + public void OnOpenedEvents() + { _viewModel.CreateInventoryItemSlot(_slotParent); + UpdateCategoryView(InventoryCategoryType.Food); + + _viewModel.OnCategoryChanged += UpdateCategoryView; + EventBus.Register(this); EventBus.Register(this); EventBus.Register(this); } + public void OnClosedEvents() + { + if (_viewModel) + { + _viewModel.OnCategoryChanged -= UpdateCategoryView; + } + + EventBus.Unregister(this); + EventBus.Unregister(this); + EventBus.Unregister(this); + } + public void UpdateView() { _viewModel.UpdateCategoryView(); @@ -37,14 +48,6 @@ public void UpdateView() public void UpdateCategoryView(InventoryCategoryType category) => _viewModel.UpdateCategoryViewByCategory(category); - private void ClearObject(Transform root) - { - foreach (Transform child in root) - { - Destroy(child.gameObject); - } - } - public void Invoke(TodayMenuAddedEvent evt) { UpdateView(); diff --git a/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/RestaurantManagementUi/ItemDetailView.cs b/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/RestaurantManagementUi/ItemDetailView.cs index 48c554123..f91219213 100644 --- a/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/RestaurantManagementUi/ItemDetailView.cs +++ b/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/RestaurantManagementUi/ItemDetailView.cs @@ -1,5 +1,3 @@ -using System; -using System.Collections.Generic; using TMPro; using UnityEngine; using UnityEngine.Localization.Components; @@ -7,7 +5,7 @@ namespace DDD { - public class ItemDetailView : MonoBehaviour, IEventHandler + public class ItemDetailView : MonoBehaviour, IUiView, IEventHandler { [SerializeField] private Image _viewImage; [SerializeField] private TextMeshProUGUI _nameLabel; @@ -20,151 +18,111 @@ public class ItemDetailView : MonoBehaviour, IEventHandler _tasteHashTagSlotUis = new(); - private ItemViewModel _currentItemViewModel; + private RestaurantManagementViewModel _viewModel; - private const string CookwareDetailPanel = "CookwareDetailPanel"; - private const string IngredientDetailPanel = "IngredientDetailPanel"; - private const string RecipeDetailPanel = "RecipeDetailPanel"; - - private void Start() + public void Initialize(RestaurantManagementViewModel viewModel) { + _viewModel = viewModel; + _nameLabel.text = string.Empty; _descriptionLabel.text = string.Empty; _cookwareImage.sprite = null; } - private void OnEnable() + public void OnOpenedEvents() { + UpdateView(); + + _viewModel.OnCategoryChanged += UpdateCategory; + EventBus.Register(this); } - private void OnDisable() + public void OnClosedEvents() { + if (_viewModel) + { + _viewModel.OnCategoryChanged -= UpdateCategory; + } + EventBus.Unregister(this); } - public void Initialize() + public void UpdateView() { - restaurantManagementDataSo = RestaurantData.Instance.ManagementData; - } - - public void Invoke(ItemSlotSelectedEvent evt) - { - Show(evt.Model); - } - - public void Show(ItemViewModel model) - { - _currentItemViewModel = model; - - if (_currentItemViewModel == null) return; - - string viewItemKey = null; - if (_currentItemViewModel.ItemType == ItemType.Recipe) + UpdateCategory(_viewModel.CurrentCategory); + + if (_viewModel.SelectedItem == null) { - viewItemKey = _currentItemViewModel.GetRecipeResultKey; + _labelLocalizer.StringReference = null; + _descriptionLocalizer.StringReference = null; + _cookwareImage.sprite = null; + ClearHashTags(); + return; } - else - { - viewItemKey = _currentItemViewModel.Id; - } - _labelLocalizer.StringReference = LocalizationManager.Instance.GetLocalizedName(viewItemKey); - _descriptionLocalizer.StringReference = LocalizationManager.Instance.GetLocalizedDescription(viewItemKey); - _cookwareImage.sprite = _currentItemViewModel.GetCookwareIcon; - UpdateTasteHashTags(_currentItemViewModel); + + _labelLocalizer.StringReference = _viewModel.GetItemName(); + _descriptionLocalizer.StringReference = _viewModel.GetItemDescription(); + _cookwareImage.sprite = _viewModel.GetCookwareSprite(); + + UpdateTasteHashTags(); } - private void UpdateTasteHashTags(ItemViewModel model) + private void UpdateTasteHashTags() { ClearHashTags(); + var tastes = _viewModel.GetTastes(); + if (tastes == null || tastes.Count == 0) return; - if (model == null) return; - - _tasteHashTagSlotUis.Clear(); - List tasteDatas = model.GetTasteDatas; - - if (tasteDatas == null || tasteDatas.Count <= 0) return; - - var backgroundMaterial = model.RecipeType switch - { - RecipeType.FoodRecipe => restaurantManagementDataSo.FoodTasteMaterial, - RecipeType.DrinkRecipe => restaurantManagementDataSo.DrinkTasteMaterial, - _ => throw new ArgumentOutOfRangeException() - }; - + var material = _viewModel.GetTasteMaterial(); float maxWidth = _tasteHashTagContent1.rect.width; float currentLineWidth = 0f; - - foreach (var tasteData in tasteDatas) + + foreach (var taste in tastes) { - var newTasteHashTag = Instantiate(restaurantManagementDataSo.TasteHashTagSlotUiPrefab, _tasteHashTagContent1, false); - newTasteHashTag.Initialize(backgroundMaterial, tasteData); - - LayoutRebuilder.ForceRebuildLayoutImmediate(newTasteHashTag.RectTransform); - float slotWidth = newTasteHashTag.RectTransform.rect.width; - if (currentLineWidth + slotWidth > maxWidth) + var instance = _viewModel.CreateHashTag(_tasteHashTagContent1); + instance.Initialize(material, taste); + + LayoutRebuilder.ForceRebuildLayoutImmediate(instance.RectTransform); + float w = instance.RectTransform.rect.width; + if (currentLineWidth + w > maxWidth) { - newTasteHashTag.transform.SetParent(_tasteHashTagContent2, false); - currentLineWidth = slotWidth + _tasteHashTagContentLayoutGroup.spacing; + instance.transform.SetParent(_tasteHashTagContent2, false); + currentLineWidth = w + _tasteHashTagContentLayoutGroup.spacing; } else { - currentLineWidth += slotWidth + _tasteHashTagContentLayoutGroup.spacing; + currentLineWidth += w + _tasteHashTagContentLayoutGroup.spacing; } - - _tasteHashTagSlotUis.Add(newTasteHashTag); } - + LayoutRebuilder.ForceRebuildLayoutImmediate(_tasteHashTagContent1); LayoutRebuilder.ForceRebuildLayoutImmediate(_tasteHashTagContent2); } public void UpdateCategory(InventoryCategoryType category) { - switch (category) - { - case InventoryCategoryType.Food: - case InventoryCategoryType.Drink: - _viewImage.sprite = DataManager.Instance.GetSprite(RecipeDetailPanel); - _tasteHashTagPanel.gameObject.SetActive(true); - _cookWarePanel.gameObject.SetActive(true); - break; - case InventoryCategoryType.Ingredient: - _viewImage.sprite = DataManager.Instance.GetSprite(IngredientDetailPanel); - _tasteHashTagPanel.gameObject.SetActive(true); - _cookWarePanel.gameObject.SetActive(false); - break; - case InventoryCategoryType.Cookware: - case InventoryCategoryType.Special: - _viewImage.sprite = DataManager.Instance.GetSprite(CookwareDetailPanel); - _tasteHashTagPanel.gameObject.SetActive(false); - _cookWarePanel.gameObject.SetActive(false); - break; - default: - throw new ArgumentOutOfRangeException(nameof(category), category, null); - } + _viewImage.sprite = _viewModel.GetDetailBackground(category); + + bool showTaste = _viewModel.ShouldShowTaste(category); + bool showCookware = _viewModel.ShouldShowCookware(category); + _tasteHashTagPanel.gameObject.SetActive(showTaste); + _cookWarePanel.gameObject.SetActive(showCookware); - if (!_tasteHashTagPanel.gameObject.activeInHierarchy) return; - Canvas.ForceUpdateCanvases(); - UpdateTasteHashTags(_currentItemViewModel); } private void ClearHashTags() { - foreach (Transform content in _tasteHashTagContent1) - { - Destroy(content?.gameObject); - } - - foreach (Transform content in _tasteHashTagContent2) - { - Destroy(content?.gameObject); - } + Utils.DestroyAllChildren(_tasteHashTagContent1); + Utils.DestroyAllChildren(_tasteHashTagContent2); + } + + public void Invoke(ItemSlotSelectedEvent evt) + { + _viewModel.SetSelectedItem(evt.Model); + UpdateView(); } } } \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/RestaurantManagementUi/RestaurantManagementUi.cs b/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/RestaurantManagementUi/RestaurantManagementUi.cs index c7f50ab07..64fef8e37 100644 --- a/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/RestaurantManagementUi/RestaurantManagementUi.cs +++ b/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/RestaurantManagementUi/RestaurantManagementUi.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using UnityEngine.EventSystems; @@ -22,6 +23,8 @@ public class RestaurantManagementUi : PopupUi> _subViews; protected override void Update() { @@ -33,23 +36,64 @@ protected override void Update() } } - protected override void TryRegister() + protected override void OnCreatedInitialize() { - base.TryRegister(); - - SetupViewModelEvents(); - } - - public override void Open(OpenPopupUiEvent evt) - { - base.Open(evt); + base.OnCreatedInitialize(); InitializeViews(); - SetupTabs(); + InitializeTabGroups(); + SetupCategoryTabs(); + } + protected override void OnOpenedEvents() + { + base.OnOpenedEvents(); + + _sectionTabs.SelectFirstTab(); + _menuCategoryTabs.SelectFirstTab(); + + if (_viewModel) + { + _viewModel.OnBatchCompleted += HandleBatchCompleted; + _viewModel.OnChecklistFailed += HandleChecklistFailed; + _viewModel.OnMenuSectionSelected += HandleMenuSectionSelected; + _viewModel.OnCookwareSectionSelected += HandleCookwareSectionSelected; + _viewModel.OnTabMoved += HandleTabMoved; + _viewModel.OnInteractRequested += HandleInteractRequested; + _viewModel.OnCloseRequested += HandleCloseRequested; + _viewModel.OnMenuCategorySelected += HandleMenuCategorySelected; + } + + foreach (var view in _subViews) + { + view.OnOpenedEvents(); + } + IsInitialized = true; } - + + protected override void OnClosedEvents() + { + base.OnClosedEvents(); + + if (_viewModel) + { + _viewModel.OnBatchCompleted -= HandleBatchCompleted; + _viewModel.OnChecklistFailed -= HandleChecklistFailed; + _viewModel.OnMenuSectionSelected -= HandleMenuSectionSelected; + _viewModel.OnCookwareSectionSelected -= HandleCookwareSectionSelected; + _viewModel.OnTabMoved -= HandleTabMoved; + _viewModel.OnInteractRequested -= HandleInteractRequested; + _viewModel.OnCloseRequested -= HandleCloseRequested; + _viewModel.OnMenuCategorySelected -= HandleMenuCategorySelected; + } + + foreach (var view in _subViews) + { + view.OnClosedEvents(); + } + } + protected override GameObject GetInitialSelected() { if (IsInitialized == false) return null; @@ -81,48 +125,21 @@ protected override void SetupBindings() BindingHelper.BindImageFilled(_bindingContext, _completeBatchFilledImage, nameof(RestaurantManagementViewModel.NormalizedHoldProgress)); } - protected override void HandleCustomPropertyChanged(string propertyName) - { - switch (propertyName) - { - case nameof(RestaurantManagementViewModel.CurrentSection): - UpdateSectionTabs(); - break; - case nameof(RestaurantManagementViewModel.CurrentCategory): - UpdateCategoryTabs(); - break; - } - } - - private void SetupViewModelEvents() - { - if (!_viewModel) return; - - _viewModel.OnBatchCompleted = HandleBatchCompleted; - _viewModel.OnChecklistFailed = HandleChecklistFailed; - _viewModel.OnMenuSectionSelected = HandleMenuSectionSelected; - _viewModel.OnCookwareSectionSelected = HandleCookwareSectionSelected; - _viewModel.OnCategoryChanged = HandleCategoryChanged; - _viewModel.OnTabMoved = HandleTabMoved; - _viewModel.OnInteractRequested = HandleInteractRequested; - _viewModel.OnCloseRequested = HandleCloseRequested; - _viewModel.OnMenuCategorySelected = HandleMenuCategorySelected; - } - private void InitializeViews() { - _checklistView.Initialize(_viewModel); - _inventoryView.Initialize(_viewModel); - _itemDetailView.Initialize(); - _todayMenuView.Initialize(_viewModel); - _todayRestaurantStateView.Initialize(); - } + _subViews = new List> + { + _checklistView, + _inventoryView, + _itemDetailView, + _todayMenuView, + _todayRestaurantStateView + }; - private void SetupTabs() - { - SetupCategoryTabs(); - InitializeTabGroups(); - SelectInitialTabs(); + foreach (var uiView in _subViews) + { + uiView.Initialize(_viewModel); + } } private void SetupCategoryTabs() @@ -138,12 +155,6 @@ private void InitializeTabGroups() _cookwareCategoryTabs.Initialize(OnCategoryTabSelected); } - private void SelectInitialTabs() - { - _sectionTabs.SelectFirstTab(); - _menuCategoryTabs.SelectFirstTab(); - } - private void UpdateSectionTabs() { if (_viewModel == null) return; @@ -164,6 +175,19 @@ private void UpdateCategoryTabs() break; } } + + protected override void HandleCustomPropertyChanged(string propertyName) + { + switch (propertyName) + { + case nameof(RestaurantManagementViewModel.CurrentSection): + UpdateSectionTabs(); + break; + case nameof(RestaurantManagementViewModel.CurrentCategory): + UpdateCategoryTabs(); + break; + } + } // ViewModel 이벤트 핸들러들 private void HandleBatchCompleted() @@ -191,12 +215,6 @@ private void HandleCookwareSectionSelected() _cookwareCategoryTabs.SelectFirstTab(); } - private void HandleCategoryChanged(InventoryCategoryType category) - { - _inventoryView.UpdateCategoryView(category); - _itemDetailView.UpdateCategory(category); - } - private void HandleTabMoved(int direction) { _sectionTabs.Move(direction); diff --git a/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/RestaurantManagementUi/RestaurantManagementViewModel.cs b/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/RestaurantManagementUi/RestaurantManagementViewModel.cs index 8acb3046a..6ca153f20 100644 --- a/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/RestaurantManagementUi/RestaurantManagementViewModel.cs +++ b/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/RestaurantManagementUi/RestaurantManagementViewModel.cs @@ -1,21 +1,24 @@ +using System; using System.Collections.Generic; using System.Linq; using UnityEngine; +using UnityEngine.Localization; namespace DDD { public class RestaurantManagementViewModel : SimpleViewModel, IEventHandler { // View에서 구독할 이벤트들 - public System.Action OnBatchCompleted; - public System.Action OnChecklistFailed; - public System.Action OnMenuSectionSelected; - public System.Action OnCookwareSectionSelected; - public System.Action OnCategoryChanged; - public System.Action OnTabMoved; - public System.Action OnInteractRequested; - public System.Action OnCloseRequested; - public System.Action OnMenuCategorySelected; + public Action OnBatchCompleted; + public Action OnChecklistFailed; + public Action OnMenuSectionSelected; + public Action OnCookwareSectionSelected; + public Action OnCategoryChanged; + public Action OnTabMoved; + public Action OnInteractRequested; + public Action OnCloseRequested; + public Action OnMenuCategorySelected; + public Action OnSelectedItemChanged; private RestaurantManagementData GetRestaurantManagementData() => RestaurantData.Instance.ManagementData; private RestaurantManagementState GetRestaurantManagementState() => RestaurantState.Instance.ManagementState; @@ -169,6 +172,8 @@ public void SetSection(SectionButtonType section) public void SetCategory(InventoryCategoryType category) { + if (CurrentCategory == category) return; + CurrentCategory = category; OnCategoryChanged?.Invoke(category); } @@ -227,6 +232,7 @@ public void UpdateChecklistView() public void CreateInventoryItemSlot(Transform parent) { + Utils.DestroyAllChildren(parent); var models = ItemViewModelFactory.CreateRestaurantManagementInventoryItem(); _slotLookup.Clear(); @@ -274,8 +280,12 @@ private IEnumerable SortSlots(IEnumerable slots) _ => slots }; } - - public void UpdateCategoryView() => UpdateCategoryViewByCategory(_currenInventoryCategoryType); + + public void UpdateCategoryView() + { + _currenInventoryCategoryType = _currenInventoryCategoryType == InventoryCategoryType.None ? InventoryCategoryType.Food : _currenInventoryCategoryType; + UpdateCategoryViewByCategory(_currenInventoryCategoryType); + } public void UpdateCategoryViewByCategory(InventoryCategoryType category) { @@ -298,9 +308,12 @@ public void UpdateCategoryViewByCategory(InventoryCategoryType category) slot.SetActive(shouldShow); - if (shouldShow && model.HasItem) + if (shouldShow) { - slot.transform.SetSiblingIndex(siblingIndex++); + if (model.HasItem) + { + slot.transform.SetSiblingIndex(siblingIndex++); + } if (firstValidSlot == null) { @@ -351,6 +364,80 @@ public void OnInventoryChanged() } #endregion + + #region ItemDetailView + + private const string CookwareDetailPanel = "CookwareDetailPanel"; + private const string IngredientDetailPanel = "IngredientDetailPanel"; + private const string RecipeDetailPanel = "RecipeDetailPanel"; + + public ItemViewModel SelectedItem { get; private set; } + + public void SetSelectedItem(ItemViewModel item) + { + if (SelectedItem == item) return; + SelectedItem = item; + } + + public TasteHashTagSlotUi CreateHashTag(RectTransform parent) + { + return Instantiate(GetRestaurantManagementData().TasteHashTagSlotUiPrefab, parent, false); + } + + private string GetViewItemKey() + { + return SelectedItem.ItemType == ItemType.Recipe ? SelectedItem.GetRecipeResultKey : SelectedItem.Id; + } + + public LocalizedString GetItemName() + { + var key = GetViewItemKey(); + return LocalizationManager.Instance.GetLocalizedName(key); + } + + public LocalizedString GetItemDescription() + { + var key = GetViewItemKey(); + return LocalizationManager.Instance.GetLocalizedDescription(key); + } + + public Sprite GetDetailBackground(InventoryCategoryType category) + { + return category switch + { + InventoryCategoryType.Food or InventoryCategoryType.Drink + => DataManager.Instance.GetSprite(RecipeDetailPanel), + InventoryCategoryType.Ingredient + => DataManager.Instance.GetSprite(IngredientDetailPanel), + InventoryCategoryType.Cookware or InventoryCategoryType.Special + => DataManager.Instance.GetSprite(CookwareDetailPanel), + _ => null + }; + } + + public Sprite GetCookwareSprite() => SelectedItem.GetCookwareIcon; + + public IReadOnlyList GetTastes() => SelectedItem?.GetTasteDatas; + + public Material GetTasteMaterial() + { + if (SelectedItem == null) return null; + var restaurantManagementData = RestaurantData.Instance.ManagementData; + return SelectedItem.RecipeType switch + { + RecipeType.FoodRecipe => restaurantManagementData.FoodTasteMaterial, + RecipeType.DrinkRecipe => restaurantManagementData.DrinkTasteMaterial, + _ => null + }; + } + + public bool ShouldShowTaste(InventoryCategoryType category) + => category is InventoryCategoryType.Food or InventoryCategoryType.Drink or InventoryCategoryType.Ingredient; + + public bool ShouldShowCookware(InventoryCategoryType category) + => category is InventoryCategoryType.Food or InventoryCategoryType.Drink; + + #endregion #region TodayMenuView @@ -359,6 +446,7 @@ public void OnInventoryChanged() public void CreateFoodSlot(Transform parent) { + Utils.DestroyAllChildren(parent); var foodMaxCount = GetRestaurantManagementData().MaxFoodCount; _foodSlots = new List(foodMaxCount); for (int i = 0; i < foodMaxCount; i++) @@ -375,6 +463,7 @@ public void CreateFoodSlot(Transform parent) public void CreateDrinkSlot(Transform parent) { + Utils.DestroyAllChildren(parent); var drinkMaxCount = GetRestaurantManagementData().MaxDrinkCount; _drinkSlots = new List(drinkMaxCount); for (int i = 0; i < drinkMaxCount; i++) @@ -427,5 +516,85 @@ public void UpdateTodayMenuItems() } #endregion + + #region TodayRestaurantStateView + + private List _workerSlots; + private List _cookwareSlots; + + public void CreateTodayWorkerSlot(Transform parent) + { + Utils.DestroyAllChildren(parent); + + int maxWorkerCount = GetRestaurantManagementData().MaxWorkerCount; + _workerSlots = new List(maxWorkerCount); + for (int i = 0; i < maxWorkerCount; i++) + { + var instance = Instantiate(GetRestaurantManagementData().ItemSlotUiPrefab, parent); + var slot = instance.GetComponent(); + slot.Initialize(null, new TodayWorkerSlotUiStrategy()); + var itemSlotInteractor = instance.GetComponent(); + itemSlotInteractor.Initialize(TodayMenuEventType.Remove, new TodayCookwareInteractorStrategy()); + + _workerSlots.Add(slot); + } + } + + public void CreateTodayCookwareSlot(Transform parent) + { + Utils.DestroyAllChildren(parent); + + int maxCookwareCount = GetRestaurantManagementData().MaxCookwareCount; + _cookwareSlots = new List(maxCookwareCount); + for (int i = 0; i < maxCookwareCount; i++) + { + var instance = Instantiate(GetRestaurantManagementData().ItemSlotUiPrefab, parent); + var slot = instance.GetComponent(); + slot.Initialize(null, new TodayWorkerSlotUiStrategy()); + var itemSlotInteractor = instance.GetComponent(); + itemSlotInteractor.Initialize(TodayMenuEventType.Remove, new TodayCookwareInteractorStrategy()); + + _cookwareSlots.Add(slot); + } + } + + public void UpdateTodayRestaurantStateView() + { + int workerIndex = 0; + foreach (var workerKey in GetRestaurantManagementState().TodayWorkerIds) + { + if (workerIndex >= _workerSlots.Count) break; + + var model = ItemViewModelFactory.CreateByItemId(workerKey); + var newWorkerSlot = _workerSlots[workerIndex]; + newWorkerSlot.Initialize(model, new TodayWorkerSlotUiStrategy()); + newWorkerSlot.Model.SetCount(1); + workerIndex++; + } + + for (int i = workerIndex; i < _workerSlots.Count; i++) + { + _workerSlots[i].Initialize(null, new TodayWorkerSlotUiStrategy()); + } + + int cookwareIndex = 0; + foreach (var cookwareKey in GetRestaurantManagementState().CookwareToRecipeIds.Keys) + { + if (cookwareIndex >= _cookwareSlots.Count) break; + + var model = ItemViewModelFactory.CreateByItemId(cookwareKey); + var newCookwareSlot = _cookwareSlots[cookwareIndex]; + newCookwareSlot.Initialize(model, new TodayCookwareSlotUiStrategy()); + newCookwareSlot.Model.SetCount(1); + cookwareIndex++; + } + + for (int i = cookwareIndex; i < _cookwareSlots.Count; i++) + { + _cookwareSlots[i].Initialize(null, new TodayCookwareSlotUiStrategy()); + } + } + + #endregion } } \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/RestaurantManagementUi/TodayMenuUi/TodayMenuView.cs b/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/RestaurantManagementUi/TodayMenuUi/TodayMenuView.cs index ddb149174..cdf650c90 100644 --- a/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/RestaurantManagementUi/TodayMenuUi/TodayMenuView.cs +++ b/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/RestaurantManagementUi/TodayMenuUi/TodayMenuView.cs @@ -2,26 +2,20 @@ namespace DDD { - public class TodayMenuView : MonoBehaviour, IEventHandler, IEventHandler + public class TodayMenuView : MonoBehaviour, IUiView, IEventHandler, IEventHandler { [SerializeField] private Transform _todayFoodContent; [SerializeField] private Transform _todayDrinkContent; private RestaurantManagementViewModel _viewModel; - private void OnDestroy() - { - EventBus.Unregister(this); - EventBus.Unregister(this); - } - public void Initialize(RestaurantManagementViewModel viewModel) { _viewModel = viewModel; + } - ClearObject(_todayFoodContent); - ClearObject(_todayDrinkContent); - + public void OnOpenedEvents() + { _viewModel.CreateFoodSlot(_todayFoodContent); _viewModel.CreateDrinkSlot(_todayDrinkContent); @@ -31,12 +25,10 @@ public void Initialize(RestaurantManagementViewModel viewModel) EventBus.Register(this); } - private void ClearObject(Transform root) + public void OnClosedEvents() { - foreach (Transform child in root) - { - Destroy(child.gameObject); - } + EventBus.Unregister(this); + EventBus.Unregister(this); } public void Invoke(TodayMenuAddedEvent evt) diff --git a/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/RestaurantManagementUi/TodayRestaurantStateUi/TodayRestaurantStateView.cs b/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/RestaurantManagementUi/TodayRestaurantStateUi/TodayRestaurantStateView.cs index 8d9257c3b..fc5ad5c0d 100644 --- a/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/RestaurantManagementUi/TodayRestaurantStateUi/TodayRestaurantStateView.cs +++ b/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/RestaurantManagementUi/TodayRestaurantStateUi/TodayRestaurantStateView.cs @@ -3,69 +3,38 @@ namespace DDD { - public class TodayRestaurantStateView : MonoBehaviour, IEventHandler, IEventHandler + public class TodayRestaurantStateView : MonoBehaviour, IUiView, IEventHandler, IEventHandler { [SerializeField] private Transform _todayWorkerContent; [SerializeField] private Transform _todayCookwareContent; private List _workerSlots; private List _cookwareSlots; - - private RestaurantManagementState restaurantManagementStateSo; - private RestaurantManagementData restaurantManagementDataSo; - - private void OnDestroy() - { - EventBus.Unregister(this); - EventBus.Unregister(this); - } - public void Initialize() + private RestaurantManagementViewModel _viewModel; + + public void Initialize(RestaurantManagementViewModel viewModel) { - restaurantManagementStateSo = RestaurantState.Instance.ManagementState; - restaurantManagementDataSo = RestaurantData.Instance.ManagementData; + _viewModel = viewModel; - foreach (Transform child in _todayWorkerContent) - { - Destroy(child.gameObject); - } - - int maxCookwareCount = restaurantManagementDataSo!.MaxCookwareCount; - _workerSlots = new List(maxCookwareCount); - for (int i = 0; i < restaurantManagementDataSo.MaxCookwareCount; i++) - { - var go = Instantiate(restaurantManagementDataSo.ItemSlotUiPrefab, _todayWorkerContent); - var slot = go.GetComponent(); - slot.Initialize(null, new TodayWorkerSlotUiStrategy()); - var itemSlotInteractor = go.GetComponent(); - itemSlotInteractor.Initialize(TodayMenuEventType.Remove, new TodayCookwareInteractorStrategy()); - - _workerSlots.Add(slot); - } - - foreach (Transform child in _todayCookwareContent) - { - Destroy(child.gameObject); - } - - _cookwareSlots = new List(maxCookwareCount); - for (int i = 0; i < restaurantManagementDataSo.MaxCookwareCount; i++) - { - var go = Instantiate(restaurantManagementDataSo.ItemSlotUiPrefab, _todayCookwareContent); - var slot = go.GetComponent(); - slot.Initialize(null, new TodayCookwareSlotUiStrategy()); - var itemSlotInteractor = go.GetComponent(); - itemSlotInteractor.Initialize(TodayMenuEventType.Remove, new TodayCookwareInteractorStrategy()); - - _cookwareSlots.Add(slot); - } + _viewModel.CreateTodayWorkerSlot(_todayWorkerContent); + _viewModel.CreateTodayCookwareSlot(_todayCookwareContent); + } + public void OnOpenedEvents() + { UpdateView(); EventBus.Register(this); EventBus.Register(this); } + public void OnClosedEvents() + { + EventBus.Unregister(this); + EventBus.Unregister(this); + } + public void Invoke(TodayMenuAddedEvent evt) { UpdateView(); @@ -78,39 +47,7 @@ public void Invoke(TodayMenuRemovedEvent evt) private void UpdateView() { - int workerIndex = 0; - foreach (var workerKey in restaurantManagementStateSo.TodayWorkerIds) - { - if (workerIndex >= _workerSlots.Count) break; - - var model = ItemViewModelFactory.CreateByItemId(workerKey); - var newWorkerSlot = _workerSlots[workerIndex]; - newWorkerSlot.Initialize(model, new TodayWorkerSlotUiStrategy()); - newWorkerSlot.Model.SetCount(1); - workerIndex++; - } - - for (int i = workerIndex; i < _workerSlots.Count; i++) - { - _workerSlots[i].Initialize(null, new TodayWorkerSlotUiStrategy()); - } - - int cookwareIndex = 0; - foreach (var cookwareKey in restaurantManagementStateSo.CookwareToRecipeIds.Keys) - { - if (cookwareIndex >= _cookwareSlots.Count) break; - - var model = ItemViewModelFactory.CreateByItemId(cookwareKey); - var newCookwareSlot = _cookwareSlots[cookwareIndex]; - newCookwareSlot.Initialize(model, new TodayCookwareSlotUiStrategy()); - newCookwareSlot.Model.SetCount(1); - cookwareIndex++; - } - - for (int i = cookwareIndex; i < _cookwareSlots.Count; i++) - { - _cookwareSlots[i].Initialize(null, new TodayCookwareSlotUiStrategy()); - } + _viewModel.UpdateTodayRestaurantStateView(); } } } \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/PopupUiState.cs b/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/UiState.cs similarity index 100% rename from Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/PopupUiState.cs rename to Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/UiState.cs diff --git a/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/PopupUiState.cs.meta b/Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/UiState.cs.meta similarity index 100% rename from Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/PopupUiState.cs.meta rename to Assets/_DDD/_Scripts/GameUi/BaseUi/PopupUis/UiState.cs.meta diff --git a/Assets/_DDD/_Scripts/GameUi/UiGuideline b/Assets/_DDD/_Scripts/GameUi/UiGuideline deleted file mode 100644 index f6e909b2b..000000000 --- a/Assets/_DDD/_Scripts/GameUi/UiGuideline +++ /dev/null @@ -1,545 +0,0 @@ -# 새로운 UI 개발을 위한 단계별 가이드라인 - -Unity 프로젝트에서 RestaurantManagementUi를 기반으로 새로운 UI를 만들 때 따라야 할 구체적인 단계별 가이드라인을 제공합니다. - -## 개발 순서 및 클래스 생성 가이드 - -### 1단계: 요구사항 분석 및 설계 - -#### 먼저 결정해야 할 사항들 -- **UI 타입**: 일반 UI인가? 팝업 UI인가? -- **입력 처리**: 키보드/게임패드 입력이 필요한가? -- **데이터 복잡도**: 단순한 표시용인가? 복잡한 상태 관리가 필요한가? -- **재사용성**: 다른 곳에서도 사용될 가능성이 있는가? - -#### 예시: ShopUi를 만든다고 가정 -``` -요구사항: -- 상품 목록 표시 (카테고리별 필터링) -- 상품 구매 기능 -- 소지금 표시 -- 키보드 입력 지원 -- 팝업 형태로 동작 -``` - -### 2단계: Service 클래스 생성 (비즈니스 로직) - -**가장 먼저 Service부터 시작하는 이유:** -- 비즈니스 로직이 명확해야 UI 설계가 가능 -- 테스트 가능한 코드 작성 -- ViewModel에서 사용할 데이터와 기능 정의 - -#### ShopService.cs -```csharp -// Assets/_DDD/_Scripts/GameUi/New/Services/ShopService.cs -namespace DDD.MVVM -{ - public class ShopService : IUiService - { - private ShopData _shopData; - private PlayerInventoryData _playerData; - - public void Initialize() - { - _shopData = DataManager.Instance.GetDataSo(); - _playerData = PlayerState.Instance.InventoryData; - } - - public void Cleanup() { } - public void UpdateUiState() { } - - /// - /// 카테고리별 상품 목록 가져오기 - /// - public IEnumerable GetItemsByCategory(ShopCategoryType category) - { - return _shopData.Items - .Where(item => MatchesCategory(item, category)) - .Select(item => new ShopItemViewModel(item)) - .ToList(); - } - - /// - /// 상품 구매 가능 여부 확인 - /// - public bool CanPurchaseItem(string itemId, int quantity = 1) - { - var item = _shopData.GetItemById(itemId); - return _playerData.Money >= (item.Price * quantity); - } - - /// - /// 상품 구매 처리 - /// - public bool PurchaseItem(string itemId, int quantity = 1) - { - if (!CanPurchaseItem(itemId, quantity)) return false; - - var item = _shopData.GetItemById(itemId); - var totalCost = item.Price * quantity; - - _playerData.Money -= totalCost; - _playerData.AddItem(itemId, quantity); - - return true; - } - - private bool MatchesCategory(ShopItemData item, ShopCategoryType category) - { - // 카테고리 매칭 로직 - return category switch - { - ShopCategoryType.Food => item.Type == ItemType.Food, - ShopCategoryType.Equipment => item.Type == ItemType.Equipment, - _ => true - }; - } - } -} -``` - -### 3단계: ViewModel 클래스 생성 (상태 관리) - -Service가 준비되었으니 이를 활용하는 ViewModel을 생성합니다. - -#### ShopViewModel.cs -```csharp -// Assets/_DDD/_Scripts/GameUi/New/ViewModels/ShopViewModel.cs -namespace DDD.MVVM -{ - public class ShopViewModel : SimpleViewModel, IEventHandler - { - [Header("Services")] - [SerializeField] private ShopService _shopService; - - // Private fields for properties - private ShopCategoryType _currentCategory = ShopCategoryType.All; - private List _visibleItems = new(); - private ShopItemViewModel _selectedItem; - private int _playerMoney; - - /// - /// 현재 선택된 카테고리 - /// - public ShopCategoryType CurrentCategory - { - get => _currentCategory; - set - { - if (SetField(ref _currentCategory, value)) - { - UpdateVisibleItems(); - OnPropertyChanged(nameof(CategoryDisplayText)); - } - } - } - - /// - /// 보이는 상품 목록 - /// - public List VisibleItems - { - get => _visibleItems; - private set => SetField(ref _visibleItems, value); - } - - /// - /// 현재 선택된 상품 - /// - public ShopItemViewModel SelectedItem - { - get => _selectedItem; - set => SetField(ref _selectedItem, value); - } - - /// - /// 플레이어 소지금 - /// - public int PlayerMoney - { - get => _playerMoney; - set => SetField(ref _playerMoney, value); - } - - // 계산된 속성들 - public string CategoryDisplayText => CurrentCategory switch - { - ShopCategoryType.Food => "음식", - ShopCategoryType.Equipment => "장비", - _ => "전체" - }; - - public string MoneyDisplayText => $"{PlayerMoney:N0} G"; - public bool HasVisibleItems => VisibleItems.Any(); - public bool CanPurchaseSelected => - SelectedItem != null && _shopService.CanPurchaseItem(SelectedItem.Id); - - protected override void Awake() - { - base.Awake(); - - if (_shopService == null) - _shopService = new ShopService(); - } - - public override void Initialize() - { - base.Initialize(); - - _shopService.Initialize(); - LoadShopData(); - RegisterEvents(); - } - - public override void Cleanup() - { - base.Cleanup(); - - UnregisterEvents(); - _shopService?.Cleanup(); - } - - private void RegisterEvents() - { - EventBus.Register(this); - } - - private void UnregisterEvents() - { - EventBus.Unregister(this); - } - - private void LoadShopData() - { - PlayerMoney = PlayerState.Instance.InventoryData.Money; - UpdateVisibleItems(); - } - - private void UpdateVisibleItems() - { - BeginUpdate(); - - var items = _shopService.GetItemsByCategory(CurrentCategory); - VisibleItems = items.ToList(); - - OnPropertyChanged(nameof(HasVisibleItems)); - OnPropertyChanged(nameof(CanPurchaseSelected)); - - EndUpdate(); - } - - /// - /// 카테고리 변경 - /// - public void SetCategory(ShopCategoryType category) - { - CurrentCategory = category; - } - - /// - /// 상품 선택 - /// - public void SelectItem(ShopItemViewModel item) - { - SelectedItem = item; - OnPropertyChanged(nameof(CanPurchaseSelected)); - } - - /// - /// 상품 구매 - /// - public bool PurchaseSelectedItem(int quantity = 1) - { - if (SelectedItem == null || !CanPurchaseSelected) return false; - - var success = _shopService.PurchaseItem(SelectedItem.Id, quantity); - if (success) - { - // 구매 성공 시 UI 업데이트 - LoadShopData(); - } - - return success; - } - - // 이벤트 핸들러 - public void Invoke(MoneyChangedEvent evt) - { - PlayerMoney = evt.NewAmount; - OnPropertyChanged(nameof(MoneyDisplayText)); - OnPropertyChanged(nameof(CanPurchaseSelected)); - } - } -} -``` - -### 4단계: ItemViewModel 클래스 생성 (필요시) - -복잡한 아이템이 있다면 별도의 ItemViewModel을 생성합니다. - -#### ShopItemViewModel.cs -```csharp -// Assets/_DDD/_Scripts/GameUi/New/ViewModels/ShopItemViewModel.cs -namespace DDD.MVVM -{ - public class ShopItemViewModel : SimpleViewModel - { - private ShopItemData _itemData; - private bool _isSelected; - - public string Id => _itemData?.Id ?? ""; - public string Name => _itemData?.Name ?? ""; - public string Description => _itemData?.Description ?? ""; - public int Price => _itemData?.Price ?? 0; - public Sprite Icon => _itemData?.Icon; - - public bool IsSelected - { - get => _isSelected; - set => SetField(ref _isSelected, value); - } - - public string PriceDisplayText => $"{Price:N0} G"; - - public ShopItemViewModel(ShopItemData itemData) - { - _itemData = itemData; - } - } -} -``` - -### 5단계: View 클래스 생성 - -마지막으로 UI View를 생성합니다. RestaurantManagementUi를 참고하여 구조를 만듭니다. - -#### ShopUi.cs -```csharp -// Assets/_DDD/_Scripts/GameUi/PopupUi/ShopUi/ShopUi.cs -namespace DDD -{ - /// - /// 상점 UI - RestaurantManagementUi 구조를 참고하여 구현 - /// - [RequireComponent(typeof(ShopViewModel))] - public class ShopUi : IntegratedBasePopupUi - { - [Header("UI References")] - // Attribute 기반 자동 바인딩 - [SerializeField, BindTo(nameof(ShopViewModel.CategoryDisplayText))] - private Text _categoryLabel; - - [SerializeField, BindTo(nameof(ShopViewModel.MoneyDisplayText))] - private Text _moneyLabel; - - [SerializeField, BindTo(nameof(ShopViewModel.HasVisibleItems), typeof(InvertBoolConverter))] - private GameObject _emptyMessage; - - // 수동 처리가 필요한 UI 요소들 - [SerializeField] private Transform _itemSlotParent; - [SerializeField] private GameObject _itemSlotPrefab; - [SerializeField] private Button[] _categoryButtons; - [SerializeField] private Button _purchaseButton; - - public override InputActionMaps InputActionMaps => InputActionMaps.Shop; - - protected override GameObject GetInitialSelected() - { - // 첫 번째 상품 슬롯 또는 카테고리 버튼 반환 - var firstSlot = _itemSlotParent.childCount > 0 ? - _itemSlotParent.GetChild(0).gameObject : null; - - return firstSlot ?? (_categoryButtons.Length > 0 ? _categoryButtons[0].gameObject : null); - } - - protected override void SetupBindings() - { - // Attribute로 처리하기 어려운 복잡한 바인딩들은 여기서 수동 설정 - } - - protected override void HandleCustomPropertyChanged(string propertyName) - { - switch (propertyName) - { - case nameof(ShopViewModel.VisibleItems): - UpdateItemSlots(); - break; - - case nameof(ShopViewModel.CurrentCategory): - UpdateCategoryButtons(); - break; - - case nameof(ShopViewModel.CanPurchaseSelected): - _purchaseButton.interactable = ViewModel.CanPurchaseSelected; - break; - } - } - - private void UpdateItemSlots() - { - // 기존 슬롯 정리 - foreach (Transform child in _itemSlotParent) - { - Destroy(child.gameObject); - } - - // 새 슬롯 생성 - if (ViewModel?.VisibleItems != null) - { - foreach (var item in ViewModel.VisibleItems) - { - var slotGO = Instantiate(_itemSlotPrefab, _itemSlotParent); - var slotComponent = slotGO.GetComponent(); - slotComponent.Initialize(item, OnItemClicked); - } - } - } - - private void UpdateCategoryButtons() - { - if (_categoryButtons == null || ViewModel == null) return; - - for (int i = 0; i < _categoryButtons.Length; i++) - { - var isSelected = (int)ViewModel.CurrentCategory == i; - _categoryButtons[i].interactable = !isSelected; - } - } - - // UI 이벤트 핸들러들 - public void OnCategoryButtonClicked(int categoryIndex) - { - ViewModel?.SetCategory((ShopCategoryType)categoryIndex); - } - - public void OnPurchaseButtonClicked() - { - if (ViewModel?.PurchaseSelectedItem() == true) - { - // 구매 성공 피드백 - ShowPurchaseSuccessEffect(); - } - } - - private void OnItemClicked(ShopItemViewModel item) - { - ViewModel?.SelectItem(item); - } - - private void ShowPurchaseSuccessEffect() - { - // 구매 성공 시 시각적 피드백 - } - - // 입력 처리 - protected override bool OnInputPerformed(ShopUiActions actionEnum, UnityEngine.InputSystem.InputAction.CallbackContext context) - { - var isHandled = base.OnInputPerformed(actionEnum, context); - - if (isHandled && ViewModel != null) - { - switch (actionEnum) - { - case ShopUiActions.Purchase: - OnPurchaseButtonClicked(); - break; - case ShopUiActions.Cancel: - Close(); - break; - } - } - - return isHandled; - } - } -} -``` - -### 6단계: 입력 액션 정의 (필요시) - -새로운 UI에 특별한 입력이 필요하다면 입력 액션을 정의합니다. - -#### ShopUiActions.cs -```csharp -// Assets/_DDD/_Scripts/Input/ShopUiActions.cs -namespace DDD -{ - [System.Flags] - public enum ShopUiActions - { - None = 0, - Navigate = 1 << 0, - Submit = 1 << 1, - Cancel = 1 << 2, - Purchase = 1 << 3, - PreviousCategory = 1 << 4, - NextCategory = 1 << 5, - } -} -``` - -## 개발 체크리스트 - -### Service 개발 체크리스트 -- [ ] IService 인터페이스 구현 -- [ ] 데이터 접근 로직 캡슐화 -- [ ] 비즈니스 로직 구현 -- [ ] 에러 처리 및 검증 로직 -- [ ] 단위 테스트 가능한 구조 - -### ViewModel 개발 체크리스트 -- [ ] SimpleViewModel 상속 -- [ ] 모든 상태를 속성으로 정의 -- [ ] SetField를 통한 PropertyChanged 알림 -- [ ] 계산된 속성 구현 -- [ ] Service 의존성 주입 -- [ ] 이벤트 등록/해제 -- [ ] 생명주기 메서드 구현 - -### View 개발 체크리스트 -- [ ] 적절한 Base 클래스 상속 선택 - - 일반 UI: IntegratedBaseUi - - 팝업 UI: IntegratedBasePopupUi - - 입력 필요 팝업: IntegratedBasePopupUi -- [ ] BindTo Attribute 적용 -- [ ] GetInitialSelected() 구현 -- [ ] SetupBindings() 구현 (필요시) -- [ ] HandleCustomPropertyChanged() 구현 -- [ ] UI 이벤트 핸들러 ViewModel로 연결 - -## 폴더 구조 권장사항 - -``` -Assets/_DDD/_Scripts/GameUi/PopupUi/ShopUi/ -├── ShopUi.cs # View 클래스 -├── ShopItemSlot.cs # 하위 UI 컴포넌트 -└── ShopUiData.cs # UI 설정 데이터 (필요시) - -Assets/_DDD/_Scripts/GameUi/New/ -├── Services/ -│ └── ShopService.cs # Service 클래스 -├── ViewModels/ -│ ├── ShopViewModel.cs # 메인 ViewModel -│ └── ShopItemViewModel.cs # 아이템 ViewModel -└── Converters/ - └── PriceConverter.cs # 커스텀 컨버터 (필요시) -``` - -## 개발 팁 - -### 1. 기존 코드 활용 -- RestaurantManagementUi의 패턴을 최대한 재활용 -- InventoryView의 필터링/정렬 로직 참고 -- 기존 Service 클래스들의 구조 패턴 따라하기 - -### 2. 점진적 개발 -1. 먼저 기본 기능만 구현 (Service → ViewModel → View) -2. UI 바인딩은 단순한 것부터 시작 -3. 복잡한 기능은 나중에 추가 - -### 3. 테스트 우선 -- Service 로직은 반드시 단위 테스트 -- ViewModel은 Mock Service로 테스트 -- View는 수동 테스트로 검증 - -이 가이드라인을 따라하면 일관성 있고 유지보수가 쉬운 UI를 효율적으로 개발할 수 있습니다. \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/GameUi/UiGuideline.meta b/Assets/_DDD/_Scripts/GameUi/UiGuideline.meta deleted file mode 100644 index dd529aea6..000000000 --- a/Assets/_DDD/_Scripts/GameUi/UiGuideline.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 31ff6b5920be49feb652a1b614d62c15 -timeCreated: 1755681370 \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/GameUi/Utils/IUiView.cs b/Assets/_DDD/_Scripts/GameUi/Utils/IUiView.cs new file mode 100644 index 000000000..31aed0bb5 --- /dev/null +++ b/Assets/_DDD/_Scripts/GameUi/Utils/IUiView.cs @@ -0,0 +1,9 @@ +namespace DDD +{ + public interface IUiView where T : SimpleViewModel + { + public void Initialize(T viewModel); + public void OnOpenedEvents(); + public void OnClosedEvents(); + } +} \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/GameUi/Utils/IUiView.cs.meta b/Assets/_DDD/_Scripts/GameUi/Utils/IUiView.cs.meta new file mode 100644 index 000000000..57bf306b9 --- /dev/null +++ b/Assets/_DDD/_Scripts/GameUi/Utils/IUiView.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ae6782400fed433399774db2670ab219 +timeCreated: 1756027826 \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/RestaurantData/DataObjects/RestaurantManagementData.cs b/Assets/_DDD/_Scripts/RestaurantData/DataObjects/RestaurantManagementData.cs index 28232ce3c..4d9cc6968 100644 --- a/Assets/_DDD/_Scripts/RestaurantData/DataObjects/RestaurantManagementData.cs +++ b/Assets/_DDD/_Scripts/RestaurantData/DataObjects/RestaurantManagementData.cs @@ -12,6 +12,7 @@ public class RestaurantManagementData : ScriptableObject public int MaxFoodCount = 8; public int MaxDrinkCount = 6; public int MaxCookwareCount = 6; + public int MaxWorkerCount = 6; [Title("체크리스트 조건")] public int ChecklistCount = 3; diff --git a/Assets/_DDD/_Scripts/Utilities/Utils.cs b/Assets/_DDD/_Scripts/Utilities/Utils.cs index f489841e7..5556bf42c 100644 --- a/Assets/_DDD/_Scripts/Utilities/Utils.cs +++ b/Assets/_DDD/_Scripts/Utilities/Utils.cs @@ -2,6 +2,7 @@ using System.Collections; using System.IO; using UnityEngine; +using Object = UnityEngine.Object; #if UNITY_EDITOR using UnityEditor; @@ -58,6 +59,14 @@ public static void MakeFolderFromFilePath(string filePath) } } } + + public static void DestroyAllChildren(Transform parent) + { + foreach (Transform child in parent) + { + Object.Destroy(child.gameObject); + } + } public static IEnumerator CoolDownCoroutine(float waitTime, Action onCooldownComplete = null) {