ui 로직 개선
This commit is contained in:
parent
b0ffb9df08
commit
4ca10808a9
@ -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: []
|
||||
|
@ -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,53 +29,41 @@ public abstract class BaseUi : MonoBehaviour
|
||||
protected virtual void Awake()
|
||||
{
|
||||
_canvasGroup = GetComponent<CanvasGroup>();
|
||||
_panel = transform.Find(CommonConstants.Panel)?.gameObject;
|
||||
_blockImage = transform.Find(CommonConstants.BlockImage)?.gameObject;
|
||||
_panel = transform.Find(CommonConstants.Panel).gameObject;
|
||||
_blockImage = transform.Find(CommonConstants.BlockImage).gameObject;
|
||||
|
||||
if (_enableBlockImage)
|
||||
{
|
||||
_blockImage.SetActive(false);
|
||||
}
|
||||
|
||||
_panel.SetActive(false);
|
||||
}
|
||||
|
||||
protected virtual void Start() { }
|
||||
protected virtual void Update() { }
|
||||
|
||||
protected virtual void OnDestroy()
|
||||
{
|
||||
_bindingContext?.Dispose();
|
||||
|
||||
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 OnEnable()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected virtual void Start()
|
||||
{
|
||||
ClosePanel();
|
||||
}
|
||||
|
||||
protected virtual void Update()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
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;
|
||||
|
@ -11,9 +11,9 @@ protected override void Awake()
|
||||
_viewModel = GetComponent<TViewModel>();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -5,27 +5,24 @@ namespace DDD
|
||||
{
|
||||
public class FadeUi : BaseUi, IEventHandler<FadeInEvent>, IEventHandler<FadeOutEvent>
|
||||
{
|
||||
protected override void Awake()
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
base.Awake();
|
||||
base.OnDestroy();
|
||||
|
||||
_canvasGroup.alpha = 0f;
|
||||
EventBus.Unregister<FadeInEvent>(this);
|
||||
EventBus.Unregister<FadeOutEvent>(this);
|
||||
}
|
||||
|
||||
protected override void TryRegister()
|
||||
protected override void OnCreatedInitialize()
|
||||
{
|
||||
base.TryRegister();
|
||||
base.OnCreatedInitialize();
|
||||
|
||||
_canvasGroup.alpha = 0f;
|
||||
|
||||
EventBus.Register<FadeInEvent>(this);
|
||||
EventBus.Register<FadeOutEvent>(this);
|
||||
}
|
||||
|
||||
protected override void TryUnregister()
|
||||
{
|
||||
EventBus.Unregister<FadeInEvent>(this);
|
||||
EventBus.Unregister<FadeOutEvent>(this);
|
||||
}
|
||||
|
||||
public void Invoke(FadeInEvent evt)
|
||||
{
|
||||
_ = FadeInAsync(evt);
|
||||
|
@ -13,29 +13,24 @@ public class GlobalMessageUi : BaseUi, IEventHandler<ShowGlobalMessageEvent>
|
||||
private readonly Queue<ShowGlobalMessageEvent> _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);
|
||||
|
@ -2,6 +2,11 @@ namespace DDD
|
||||
{
|
||||
public class RestaurantHud : BaseUi
|
||||
{
|
||||
protected override void OnCreatedInitialize()
|
||||
{
|
||||
base.OnCreatedInitialize();
|
||||
|
||||
OpenPanel();
|
||||
}
|
||||
}
|
||||
}
|
@ -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<ShowInteractionUiEvent>(this);
|
||||
EventBus.Register<HideInteractionUiEvent>(this);
|
||||
}
|
||||
|
||||
protected override void TryUnregister()
|
||||
protected override void OnClosedEvents()
|
||||
{
|
||||
base.TryUnregister();
|
||||
base.OnClosedEvents();
|
||||
|
||||
EventBus.Unregister<ShowInteractionUiEvent>(this);
|
||||
EventBus.Unregister<HideInteractionUiEvent>(this);
|
||||
|
@ -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();
|
||||
|
@ -35,9 +35,9 @@ protected override void Awake()
|
||||
_viewModel = GetComponent<TViewModel>();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -14,30 +14,34 @@ public enum ChecklistLocalizationKey
|
||||
Checklist3,
|
||||
}
|
||||
|
||||
public class ChecklistView : MonoBehaviour, IEventHandler<TodayMenuAddedEvent>, IEventHandler<TodayMenuRemovedEvent>
|
||||
public class ChecklistView : MonoBehaviour, IUiView<RestaurantManagementViewModel>, IEventHandler<TodayMenuAddedEvent>, IEventHandler<TodayMenuRemovedEvent>
|
||||
{
|
||||
[SerializeField] private Transform _parent;
|
||||
|
||||
private RestaurantManagementViewModel _viewModel;
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
EventBus.Unregister<TodayMenuAddedEvent>(this);
|
||||
EventBus.Unregister<TodayMenuRemovedEvent>(this);
|
||||
}
|
||||
|
||||
public void Initialize(RestaurantManagementViewModel viewModel)
|
||||
{
|
||||
_viewModel = viewModel;
|
||||
|
||||
ClearObject(_parent);
|
||||
Utils.DestroyAllChildren(_parent);
|
||||
_viewModel.CreateChecklist(_parent);
|
||||
}
|
||||
|
||||
public void OnOpenedEvents()
|
||||
{
|
||||
UpdateView();
|
||||
|
||||
EventBus.Register<TodayMenuAddedEvent>(this);
|
||||
EventBus.Register<TodayMenuRemovedEvent>(this);
|
||||
}
|
||||
|
||||
public void OnClosedEvents()
|
||||
{
|
||||
EventBus.Unregister<TodayMenuAddedEvent>(this);
|
||||
EventBus.Unregister<TodayMenuRemovedEvent>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace DDD
|
||||
{
|
||||
public class InventoryView : MonoBehaviour, IEventHandler<InventoryChangedEvent>,
|
||||
public class InventoryView : MonoBehaviour, IUiView<RestaurantManagementViewModel>, IEventHandler<InventoryChangedEvent>,
|
||||
IEventHandler<TodayMenuAddedEvent>, IEventHandler<TodayMenuRemovedEvent>
|
||||
{
|
||||
private RestaurantManagementViewModel _viewModel;
|
||||
@ -11,25 +11,36 @@ public class InventoryView : MonoBehaviour, IEventHandler<InventoryChangedEvent>
|
||||
|
||||
public GameObject GetInitialSelected() => _viewModel.GetInitialSelectedByInventory();
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
EventBus.Unregister<InventoryChangedEvent>(this);
|
||||
EventBus.Unregister<TodayMenuAddedEvent>(this);
|
||||
EventBus.Unregister<TodayMenuRemovedEvent>(this);
|
||||
}
|
||||
|
||||
public void Initialize(RestaurantManagementViewModel viewModel)
|
||||
{
|
||||
_viewModel = viewModel;
|
||||
}
|
||||
|
||||
ClearObject(_slotParent);
|
||||
public void OnOpenedEvents()
|
||||
{
|
||||
_viewModel.CreateInventoryItemSlot(_slotParent);
|
||||
|
||||
UpdateCategoryView(InventoryCategoryType.Food);
|
||||
|
||||
_viewModel.OnCategoryChanged += UpdateCategoryView;
|
||||
|
||||
EventBus.Register<InventoryChangedEvent>(this);
|
||||
EventBus.Register<TodayMenuAddedEvent>(this);
|
||||
EventBus.Register<TodayMenuRemovedEvent>(this);
|
||||
}
|
||||
|
||||
public void OnClosedEvents()
|
||||
{
|
||||
if (_viewModel)
|
||||
{
|
||||
_viewModel.OnCategoryChanged -= UpdateCategoryView;
|
||||
}
|
||||
|
||||
EventBus.Unregister<InventoryChangedEvent>(this);
|
||||
EventBus.Unregister<TodayMenuAddedEvent>(this);
|
||||
EventBus.Unregister<TodayMenuRemovedEvent>(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();
|
||||
|
@ -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<ItemSlotSelectedEvent>
|
||||
public class ItemDetailView : MonoBehaviour, IUiView<RestaurantManagementViewModel>, IEventHandler<ItemSlotSelectedEvent>
|
||||
{
|
||||
[SerializeField] private Image _viewImage;
|
||||
[SerializeField] private TextMeshProUGUI _nameLabel;
|
||||
@ -21,102 +19,82 @@ public class ItemDetailView : MonoBehaviour, IEventHandler<ItemSlotSelectedEvent
|
||||
[SerializeField] private HorizontalLayoutGroup _tasteHashTagContentLayoutGroup;
|
||||
[SerializeField] private RectTransform _tasteHashTagContent2;
|
||||
|
||||
private RestaurantManagementData restaurantManagementDataSo;
|
||||
private RestaurantManagementViewModel _viewModel;
|
||||
|
||||
private List<TasteHashTagSlotUi> _tasteHashTagSlotUis = new();
|
||||
private ItemViewModel _currentItemViewModel;
|
||||
|
||||
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<ItemSlotSelectedEvent>(this);
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
public void OnClosedEvents()
|
||||
{
|
||||
if (_viewModel)
|
||||
{
|
||||
_viewModel.OnCategoryChanged -= UpdateCategory;
|
||||
}
|
||||
|
||||
EventBus.Unregister<ItemSlotSelectedEvent>(this);
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
public void UpdateView()
|
||||
{
|
||||
restaurantManagementDataSo = RestaurantData.Instance.ManagementData;
|
||||
}
|
||||
UpdateCategory(_viewModel.CurrentCategory);
|
||||
|
||||
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)
|
||||
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<TasteData> 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);
|
||||
var instance = _viewModel.CreateHashTag(_tasteHashTagContent1);
|
||||
instance.Initialize(material, taste);
|
||||
|
||||
LayoutRebuilder.ForceRebuildLayoutImmediate(newTasteHashTag.RectTransform);
|
||||
float slotWidth = newTasteHashTag.RectTransform.rect.width;
|
||||
if (currentLineWidth + slotWidth > maxWidth)
|
||||
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);
|
||||
@ -125,46 +103,26 @@ private void UpdateTasteHashTags(ItemViewModel model)
|
||||
|
||||
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);
|
||||
|
||||
if (!_tasteHashTagPanel.gameObject.activeInHierarchy) return;
|
||||
bool showTaste = _viewModel.ShouldShowTaste(category);
|
||||
bool showCookware = _viewModel.ShouldShowCookware(category);
|
||||
_tasteHashTagPanel.gameObject.SetActive(showTaste);
|
||||
_cookWarePanel.gameObject.SetActive(showCookware);
|
||||
|
||||
Canvas.ForceUpdateCanvases();
|
||||
UpdateTasteHashTags(_currentItemViewModel);
|
||||
}
|
||||
|
||||
private void ClearHashTags()
|
||||
{
|
||||
foreach (Transform content in _tasteHashTagContent1)
|
||||
{
|
||||
Destroy(content?.gameObject);
|
||||
}
|
||||
Utils.DestroyAllChildren(_tasteHashTagContent1);
|
||||
Utils.DestroyAllChildren(_tasteHashTagContent2);
|
||||
}
|
||||
|
||||
foreach (Transform content in _tasteHashTagContent2)
|
||||
{
|
||||
Destroy(content?.gameObject);
|
||||
}
|
||||
public void Invoke(ItemSlotSelectedEvent evt)
|
||||
{
|
||||
_viewModel.SetSelectedItem(evt.Model);
|
||||
UpdateView();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.EventSystems;
|
||||
@ -23,6 +24,8 @@ public class RestaurantManagementUi : PopupUi<RestaurantUiActions, RestaurantMan
|
||||
[Header("Hold Progress UI")]
|
||||
[SerializeField] private Image _completeBatchFilledImage;
|
||||
|
||||
private List<IUiView<RestaurantManagementViewModel>> _subViews;
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.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<IUiView<RestaurantManagementViewModel>>
|
||||
{
|
||||
_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;
|
||||
@ -165,6 +176,19 @@ private void UpdateCategoryTabs()
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -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<TodayMenuRemovedEvent>
|
||||
{
|
||||
// View에서 구독할 이벤트들
|
||||
public System.Action OnBatchCompleted;
|
||||
public System.Action OnChecklistFailed;
|
||||
public System.Action OnMenuSectionSelected;
|
||||
public System.Action OnCookwareSectionSelected;
|
||||
public System.Action<InventoryCategoryType> OnCategoryChanged;
|
||||
public System.Action<int> OnTabMoved;
|
||||
public System.Action OnInteractRequested;
|
||||
public System.Action OnCloseRequested;
|
||||
public System.Action<InventoryCategoryType> OnMenuCategorySelected;
|
||||
public Action OnBatchCompleted;
|
||||
public Action OnChecklistFailed;
|
||||
public Action OnMenuSectionSelected;
|
||||
public Action OnCookwareSectionSelected;
|
||||
public Action<InventoryCategoryType> OnCategoryChanged;
|
||||
public Action<int> OnTabMoved;
|
||||
public Action OnInteractRequested;
|
||||
public Action OnCloseRequested;
|
||||
public Action<InventoryCategoryType> OnMenuCategorySelected;
|
||||
public Action<ItemViewModel> 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();
|
||||
@ -275,7 +281,11 @@ private IEnumerable<ItemSlotUi> SortSlots(IEnumerable<ItemSlotUi> 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)
|
||||
{
|
||||
@ -352,6 +365,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<TasteData> 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
|
||||
|
||||
private List<ItemSlotUi> _foodSlots;
|
||||
@ -359,6 +446,7 @@ public void OnInventoryChanged()
|
||||
|
||||
public void CreateFoodSlot(Transform parent)
|
||||
{
|
||||
Utils.DestroyAllChildren(parent);
|
||||
var foodMaxCount = GetRestaurantManagementData().MaxFoodCount;
|
||||
_foodSlots = new List<ItemSlotUi>(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<ItemSlotUi>(drinkMaxCount);
|
||||
for (int i = 0; i < drinkMaxCount; i++)
|
||||
@ -427,5 +516,85 @@ public void UpdateTodayMenuItems()
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region TodayRestaurantStateView
|
||||
|
||||
private List<ItemSlotUi> _workerSlots;
|
||||
private List<ItemSlotUi> _cookwareSlots;
|
||||
|
||||
public void CreateTodayWorkerSlot(Transform parent)
|
||||
{
|
||||
Utils.DestroyAllChildren(parent);
|
||||
|
||||
int maxWorkerCount = GetRestaurantManagementData().MaxWorkerCount;
|
||||
_workerSlots = new List<ItemSlotUi>(maxWorkerCount);
|
||||
for (int i = 0; i < maxWorkerCount; i++)
|
||||
{
|
||||
var instance = Instantiate(GetRestaurantManagementData().ItemSlotUiPrefab, parent);
|
||||
var slot = instance.GetComponent<ItemSlotUi>();
|
||||
slot.Initialize(null, new TodayWorkerSlotUiStrategy());
|
||||
var itemSlotInteractor = instance.GetComponent<ItemSlotInteractor>();
|
||||
itemSlotInteractor.Initialize(TodayMenuEventType.Remove, new TodayCookwareInteractorStrategy());
|
||||
|
||||
_workerSlots.Add(slot);
|
||||
}
|
||||
}
|
||||
|
||||
public void CreateTodayCookwareSlot(Transform parent)
|
||||
{
|
||||
Utils.DestroyAllChildren(parent);
|
||||
|
||||
int maxCookwareCount = GetRestaurantManagementData().MaxCookwareCount;
|
||||
_cookwareSlots = new List<ItemSlotUi>(maxCookwareCount);
|
||||
for (int i = 0; i < maxCookwareCount; i++)
|
||||
{
|
||||
var instance = Instantiate(GetRestaurantManagementData().ItemSlotUiPrefab, parent);
|
||||
var slot = instance.GetComponent<ItemSlotUi>();
|
||||
slot.Initialize(null, new TodayWorkerSlotUiStrategy());
|
||||
var itemSlotInteractor = instance.GetComponent<ItemSlotInteractor>();
|
||||
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
|
||||
}
|
||||
}
|
@ -2,26 +2,20 @@
|
||||
|
||||
namespace DDD
|
||||
{
|
||||
public class TodayMenuView : MonoBehaviour, IEventHandler<TodayMenuAddedEvent>, IEventHandler<TodayMenuRemovedEvent>
|
||||
public class TodayMenuView : MonoBehaviour, IUiView<RestaurantManagementViewModel>, IEventHandler<TodayMenuAddedEvent>, IEventHandler<TodayMenuRemovedEvent>
|
||||
{
|
||||
[SerializeField] private Transform _todayFoodContent;
|
||||
[SerializeField] private Transform _todayDrinkContent;
|
||||
|
||||
private RestaurantManagementViewModel _viewModel;
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
EventBus.Unregister<TodayMenuAddedEvent>(this);
|
||||
EventBus.Unregister<TodayMenuRemovedEvent>(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<TodayMenuRemovedEvent>(this);
|
||||
}
|
||||
|
||||
private void ClearObject(Transform root)
|
||||
public void OnClosedEvents()
|
||||
{
|
||||
foreach (Transform child in root)
|
||||
{
|
||||
Destroy(child.gameObject);
|
||||
}
|
||||
EventBus.Unregister<TodayMenuAddedEvent>(this);
|
||||
EventBus.Unregister<TodayMenuRemovedEvent>(this);
|
||||
}
|
||||
|
||||
public void Invoke(TodayMenuAddedEvent evt)
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
namespace DDD
|
||||
{
|
||||
public class TodayRestaurantStateView : MonoBehaviour, IEventHandler<TodayMenuAddedEvent>, IEventHandler<TodayMenuRemovedEvent>
|
||||
public class TodayRestaurantStateView : MonoBehaviour, IUiView<RestaurantManagementViewModel>, IEventHandler<TodayMenuAddedEvent>, IEventHandler<TodayMenuRemovedEvent>
|
||||
{
|
||||
[SerializeField] private Transform _todayWorkerContent;
|
||||
[SerializeField] private Transform _todayCookwareContent;
|
||||
@ -11,61 +11,30 @@ public class TodayRestaurantStateView : MonoBehaviour, IEventHandler<TodayMenuAd
|
||||
private List<ItemSlotUi> _workerSlots;
|
||||
private List<ItemSlotUi> _cookwareSlots;
|
||||
|
||||
private RestaurantManagementState restaurantManagementStateSo;
|
||||
private RestaurantManagementData restaurantManagementDataSo;
|
||||
private RestaurantManagementViewModel _viewModel;
|
||||
|
||||
private void OnDestroy()
|
||||
public void Initialize(RestaurantManagementViewModel viewModel)
|
||||
{
|
||||
EventBus.Unregister<TodayMenuAddedEvent>(this);
|
||||
EventBus.Unregister<TodayMenuRemovedEvent>(this);
|
||||
_viewModel = viewModel;
|
||||
|
||||
_viewModel.CreateTodayWorkerSlot(_todayWorkerContent);
|
||||
_viewModel.CreateTodayCookwareSlot(_todayCookwareContent);
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
public void OnOpenedEvents()
|
||||
{
|
||||
restaurantManagementStateSo = RestaurantState.Instance.ManagementState;
|
||||
restaurantManagementDataSo = RestaurantData.Instance.ManagementData;
|
||||
|
||||
foreach (Transform child in _todayWorkerContent)
|
||||
{
|
||||
Destroy(child.gameObject);
|
||||
}
|
||||
|
||||
int maxCookwareCount = restaurantManagementDataSo!.MaxCookwareCount;
|
||||
_workerSlots = new List<ItemSlotUi>(maxCookwareCount);
|
||||
for (int i = 0; i < restaurantManagementDataSo.MaxCookwareCount; i++)
|
||||
{
|
||||
var go = Instantiate(restaurantManagementDataSo.ItemSlotUiPrefab, _todayWorkerContent);
|
||||
var slot = go.GetComponent<ItemSlotUi>();
|
||||
slot.Initialize(null, new TodayWorkerSlotUiStrategy());
|
||||
var itemSlotInteractor = go.GetComponent<ItemSlotInteractor>();
|
||||
itemSlotInteractor.Initialize(TodayMenuEventType.Remove, new TodayCookwareInteractorStrategy());
|
||||
|
||||
_workerSlots.Add(slot);
|
||||
}
|
||||
|
||||
foreach (Transform child in _todayCookwareContent)
|
||||
{
|
||||
Destroy(child.gameObject);
|
||||
}
|
||||
|
||||
_cookwareSlots = new List<ItemSlotUi>(maxCookwareCount);
|
||||
for (int i = 0; i < restaurantManagementDataSo.MaxCookwareCount; i++)
|
||||
{
|
||||
var go = Instantiate(restaurantManagementDataSo.ItemSlotUiPrefab, _todayCookwareContent);
|
||||
var slot = go.GetComponent<ItemSlotUi>();
|
||||
slot.Initialize(null, new TodayCookwareSlotUiStrategy());
|
||||
var itemSlotInteractor = go.GetComponent<ItemSlotInteractor>();
|
||||
itemSlotInteractor.Initialize(TodayMenuEventType.Remove, new TodayCookwareInteractorStrategy());
|
||||
|
||||
_cookwareSlots.Add(slot);
|
||||
}
|
||||
|
||||
UpdateView();
|
||||
|
||||
EventBus.Register<TodayMenuAddedEvent>(this);
|
||||
EventBus.Register<TodayMenuRemovedEvent>(this);
|
||||
}
|
||||
|
||||
public void OnClosedEvents()
|
||||
{
|
||||
EventBus.Unregister<TodayMenuAddedEvent>(this);
|
||||
EventBus.Unregister<TodayMenuRemovedEvent>(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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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<ShopData>();
|
||||
_playerData = PlayerState.Instance.InventoryData;
|
||||
}
|
||||
|
||||
public void Cleanup() { }
|
||||
public void UpdateUiState() { }
|
||||
|
||||
/// <summary>
|
||||
/// 카테고리별 상품 목록 가져오기
|
||||
/// </summary>
|
||||
public IEnumerable<ShopItemViewModel> GetItemsByCategory(ShopCategoryType category)
|
||||
{
|
||||
return _shopData.Items
|
||||
.Where(item => MatchesCategory(item, category))
|
||||
.Select(item => new ShopItemViewModel(item))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 상품 구매 가능 여부 확인
|
||||
/// </summary>
|
||||
public bool CanPurchaseItem(string itemId, int quantity = 1)
|
||||
{
|
||||
var item = _shopData.GetItemById(itemId);
|
||||
return _playerData.Money >= (item.Price * quantity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 상품 구매 처리
|
||||
/// </summary>
|
||||
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<MoneyChangedEvent>
|
||||
{
|
||||
[Header("Services")]
|
||||
[SerializeField] private ShopService _shopService;
|
||||
|
||||
// Private fields for properties
|
||||
private ShopCategoryType _currentCategory = ShopCategoryType.All;
|
||||
private List<ShopItemViewModel> _visibleItems = new();
|
||||
private ShopItemViewModel _selectedItem;
|
||||
private int _playerMoney;
|
||||
|
||||
/// <summary>
|
||||
/// 현재 선택된 카테고리
|
||||
/// </summary>
|
||||
public ShopCategoryType CurrentCategory
|
||||
{
|
||||
get => _currentCategory;
|
||||
set
|
||||
{
|
||||
if (SetField(ref _currentCategory, value))
|
||||
{
|
||||
UpdateVisibleItems();
|
||||
OnPropertyChanged(nameof(CategoryDisplayText));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 보이는 상품 목록
|
||||
/// </summary>
|
||||
public List<ShopItemViewModel> VisibleItems
|
||||
{
|
||||
get => _visibleItems;
|
||||
private set => SetField(ref _visibleItems, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 현재 선택된 상품
|
||||
/// </summary>
|
||||
public ShopItemViewModel SelectedItem
|
||||
{
|
||||
get => _selectedItem;
|
||||
set => SetField(ref _selectedItem, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 플레이어 소지금
|
||||
/// </summary>
|
||||
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<MoneyChangedEvent>(this);
|
||||
}
|
||||
|
||||
private void UnregisterEvents()
|
||||
{
|
||||
EventBus.Unregister<MoneyChangedEvent>(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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 카테고리 변경
|
||||
/// </summary>
|
||||
public void SetCategory(ShopCategoryType category)
|
||||
{
|
||||
CurrentCategory = category;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 상품 선택
|
||||
/// </summary>
|
||||
public void SelectItem(ShopItemViewModel item)
|
||||
{
|
||||
SelectedItem = item;
|
||||
OnPropertyChanged(nameof(CanPurchaseSelected));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 상품 구매
|
||||
/// </summary>
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// 상점 UI - RestaurantManagementUi 구조를 참고하여 구현
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(ShopViewModel))]
|
||||
public class ShopUi : IntegratedBasePopupUi<ShopUiActions, ShopViewModel>
|
||||
{
|
||||
[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<ShopItemSlot>();
|
||||
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<TViewModel>
|
||||
- 팝업 UI: IntegratedBasePopupUi<TViewModel>
|
||||
- 입력 필요 팝업: IntegratedBasePopupUi<TInputEnum, TViewModel>
|
||||
- [ ] 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를 효율적으로 개발할 수 있습니다.
|
@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 31ff6b5920be49feb652a1b614d62c15
|
||||
timeCreated: 1755681370
|
9
Assets/_DDD/_Scripts/GameUi/Utils/IUiView.cs
Normal file
9
Assets/_DDD/_Scripts/GameUi/Utils/IUiView.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace DDD
|
||||
{
|
||||
public interface IUiView<T> where T : SimpleViewModel
|
||||
{
|
||||
public void Initialize(T viewModel);
|
||||
public void OnOpenedEvents();
|
||||
public void OnClosedEvents();
|
||||
}
|
||||
}
|
3
Assets/_DDD/_Scripts/GameUi/Utils/IUiView.cs.meta
Normal file
3
Assets/_DDD/_Scripts/GameUi/Utils/IUiView.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ae6782400fed433399774db2670ab219
|
||||
timeCreated: 1756027826
|
@ -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;
|
||||
|
@ -2,6 +2,7 @@
|
||||
using System.Collections;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
@ -59,6 +60,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)
|
||||
{
|
||||
yield return new WaitForSeconds(waitTime);
|
||||
|
Loading…
Reference in New Issue
Block a user