From d5f72c3e4c4594aa0d46b54b10427c8d098be081 Mon Sep 17 00:00:00 2001 From: NTG_Lenovo Date: Wed, 13 Aug 2025 21:18:54 +0900 Subject: [PATCH] =?UTF-8?q?smart=20string=20=ED=98=84=EC=A7=80=ED=99=94=20?= =?UTF-8?q?=EC=8B=9C=EC=8A=A4=ED=85=9C=20=EC=97=B0=EB=8F=99=20=EB=B0=8F=20?= =?UTF-8?q?=EA=B8=80=EB=A1=9C=EB=B2=8C=20=EB=A9=94=EC=84=B8=EC=A7=80=20?= =?UTF-8?q?=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=EA=B8=B0=EB=8A=A5=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/_DDD/_Scripts/GameEvent/GameEvents.cs | 12 ++ .../Localization/LocalizationManager.cs | 26 ++++- .../Localization/SmartStringVariables.cs | 107 ++++++++++++++++++ .../_DDD/_Scripts/GameUi/GlobalMessageUi.cs | 6 +- .../TodayMenuInteractorStrategy.cs | 6 +- .../TodayCookwareInteractorStrategy.cs | 6 +- .../RestaurantController.cs | 10 ++ 7 files changed, 162 insertions(+), 11 deletions(-) create mode 100644 Assets/_DDD/_Scripts/GameFramework/Localization/SmartStringVariables.cs diff --git a/Assets/_DDD/_Scripts/GameEvent/GameEvents.cs b/Assets/_DDD/_Scripts/GameEvent/GameEvents.cs index f0db7fdf9..da884fb3a 100644 --- a/Assets/_DDD/_Scripts/GameEvent/GameEvents.cs +++ b/Assets/_DDD/_Scripts/GameEvent/GameEvents.cs @@ -1,5 +1,6 @@ using System; using System.Threading.Tasks; +using UnityEngine; namespace DDD { @@ -56,6 +57,17 @@ public class ShowGlobalMessageEvent : IEvent public string NewMessageKey; public float ShowDuration; public float FadeDuration; + public Color TextColor; + public int FontSize; + + public void Set(string newMessageKey, float newShowDuration = 1f, float newFadeDuration = 0.5f, Color newTextColor = default, int fontSize = 44) + { + NewMessageKey = newMessageKey; + ShowDuration = newShowDuration; + FadeDuration = newFadeDuration; + TextColor = newTextColor == default ? Color.red : newTextColor; + FontSize = fontSize; + } } public class OpenPopupUiEvent : IEvent diff --git a/Assets/_DDD/_Scripts/GameFramework/Localization/LocalizationManager.cs b/Assets/_DDD/_Scripts/GameFramework/Localization/LocalizationManager.cs index bd3d4f17d..ec217c5c7 100644 --- a/Assets/_DDD/_Scripts/GameFramework/Localization/LocalizationManager.cs +++ b/Assets/_DDD/_Scripts/GameFramework/Localization/LocalizationManager.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using UnityEngine; using UnityEngine.Localization; +using UnityEngine.Localization.SmartFormat.PersistentVariables; namespace DDD { @@ -16,6 +17,8 @@ public class LocalizationManager : Singleton, IManager private const string Name = "_name"; private const string Description = "_description"; + private readonly List _singleArgBuffer = new(1); + public async void PreInit() { _localizedCache.Clear(); @@ -83,15 +86,30 @@ public LocalizedString GetLocalizedString(string key) public LocalizedString GetLocalizedDescription(string key) => GetLocalizedString(key + Description); /// - /// Key값 자체를 탐색 + /// 현재 로케일 기준 동기 문자열 반환 (스마트 문자열 인자 지원) /// public string GetString(string key) { var localizedString = GetLocalizedString(key); - return LocalizationSettings.StringDatabase.GetLocalizedString(localizedString.TableReference, key, LocalizationSettings.SelectedLocale); + if (localizedString == null) return key; // fallback + + var tableRef = localizedString.TableReference; + var entryRef = key; + var locale = LocalizationSettings.SelectedLocale; + + VariablesGroupAsset variables = SmartStringVariables.Instance.GetVariablesGroupAsset(); + if (variables != null) + { + _singleArgBuffer.Clear(); + _singleArgBuffer.Add(variables); // SmartFormat이 {name}을 variables에서 찾음 + return LocalizationSettings.StringDatabase.GetLocalizedString(tableRef, key, _singleArgBuffer, locale); + } + + // 인자 없음 + return LocalizationSettings.StringDatabase.GetLocalizedString(tableRef, entryRef, locale); } - public string GetName(string key) => GetString(key + Name); - public string GetDescription(string key) => GetString(key + Description); + public string GetName(string key, VariablesGroupAsset variables = null) => GetString(key + Name); + public string GetDescription(string key, VariablesGroupAsset variables = null) => GetString(key + Description); /// /// 현재 사용 중인 로케일 코드 반환 (예: "ko", "en", "ja") diff --git a/Assets/_DDD/_Scripts/GameFramework/Localization/SmartStringVariables.cs b/Assets/_DDD/_Scripts/GameFramework/Localization/SmartStringVariables.cs new file mode 100644 index 000000000..96b89641e --- /dev/null +++ b/Assets/_DDD/_Scripts/GameFramework/Localization/SmartStringVariables.cs @@ -0,0 +1,107 @@ +using System.Threading.Tasks; +using UnityEngine; +using UnityEngine.Localization.SmartFormat.PersistentVariables; + +namespace DDD +{ + /// + /// Smart String 변수 전담 매니저. + /// - VariablesGroupAsset 안의 변수들을 생성/재사용하고 값만 갱신(무가비지). + /// - {day}, {playerName}, {money} 등 공용 Set API 제공. + /// + public class SmartStringVariables : Singleton, IManager + { + private VariablesGroupAsset _vars; + private GameStateSo _gameStateSo; + + private const string VariablesGroupAssetKey = "SmartStringGroup"; + private const string Daykey = "day"; + + public void PreInit() { } + + public async Task Init() + { + _vars = await AssetManager.LoadAsset(VariablesGroupAssetKey); + Debug.Assert(_vars != null, "_variablesGroupAsset is null"); + + _gameStateSo = await AssetManager.LoadAsset(DataConstants.GameStateSo); + Debug.Assert(_gameStateSo != null, "_gameStateSo is null"); + + // 예시: day 초기 세팅 (없으면 생성, 타입 다르면 교체) + Set(Daykey, _gameStateSo.GetCurrentLevel()); + } + + public void PostInit() { } + + public VariablesGroupAsset GetVariablesGroupAsset() => _vars; + + // ---------- 공용 Set API (가비지 최소화) ---------- + + /// int 변수 세팅. {key} 로 접근. + public void Set(string key, int value) + { + var v = Ensure(key); + if (v.Value != value) v.Value = value; + } + + /// float 변수 세팅. {key} 로 접근. + public void Set(string key, float value) + { + var v = Ensure(key); + if (!Mathf.Approximately(v.Value, value)) v.Value = value; + } + + /// bool 변수 세팅. {key} 로 접근. + public void Set(string key, bool value) + { + var v = Ensure(key); + if (v.Value != value) v.Value = value; + } + + /// string 변수 세팅. {key} 로 접근. + public void Set(string key, string value) + { + var v = Ensure(key); + if (!string.Equals(v.Value, value)) v.Value = value; + } + + /// + /// enum은 보통 현지화 포맷에서 문자열로 취급하는 편이 직관적입니다. + /// 필요시 IntVariable로 바꾸고 (int)(object)value 저장하는 버전도 추가 가능. + /// + public void SetEnum(string key, TEnum value) where TEnum : struct + { + var v = Ensure(key); + string s = value.ToString(); + if (!string.Equals(v.Value, s)) v.Value = s; + } + + // ---------- 유틸 ---------- + + /// + /// key에 해당하는 변수가 있고 타입이 일치하면 캐시 반환. + /// 없거나 타입이 다르면 교체(Add/Remove)하여 보장. + /// + private T Ensure(string key) where T : class, IVariable, new() + { + Debug.Assert(!string.IsNullOrEmpty(key), "SmartStringVariables.Ensure: key is null or empty."); + Debug.Assert(_vars != null, "SmartStringVariables.Ensure: _vars is null (Init 순서 확인)."); + + if (_vars.TryGetValue(key, out var existing)) + { + if (existing is T ok) return ok; + _vars.Remove(key); // 타입 다르면 제거 후 교체 + } + + var created = new T(); + _vars.Add(key, created); + return (T)created; + } + + /// 변수 존재 여부. + public bool Has(string key) => _vars != null && _vars.ContainsKey(key); + + /// 변수 제거. + public bool Remove(string key) => _vars != null && _vars.Remove(key); + } +} diff --git a/Assets/_DDD/_Scripts/GameUi/GlobalMessageUi.cs b/Assets/_DDD/_Scripts/GameUi/GlobalMessageUi.cs index 63978b643..514c7970d 100644 --- a/Assets/_DDD/_Scripts/GameUi/GlobalMessageUi.cs +++ b/Assets/_DDD/_Scripts/GameUi/GlobalMessageUi.cs @@ -50,7 +50,11 @@ private void TryDisplayNext() var evt = _messageQueue.Dequeue(); _isDisplayingMessage = true; - _messageText.text = LocalizationManager.Instance.GetString(evt.NewMessageKey); + _messageText.color = evt.TextColor; + _messageText.fontSize = evt.FontSize; + + string localized = LocalizationManager.Instance.GetString(evt.NewMessageKey); + _messageText.text = localized; OpenPanel(); _fadeTween?.Kill(); diff --git a/Assets/_DDD/_Scripts/GameUi/RestaurantManagementUi/TodayMenuUi/TodayMenuInteractorStrategy.cs b/Assets/_DDD/_Scripts/GameUi/RestaurantManagementUi/TodayMenuUi/TodayMenuInteractorStrategy.cs index ce2b6e936..32bbfd123 100644 --- a/Assets/_DDD/_Scripts/GameUi/RestaurantManagementUi/TodayMenuUi/TodayMenuInteractorStrategy.cs +++ b/Assets/_DDD/_Scripts/GameUi/RestaurantManagementUi/TodayMenuUi/TodayMenuInteractorStrategy.cs @@ -1,3 +1,5 @@ +using UnityEngine; + namespace DDD { public class TodayMenuInteractorStrategy : IItemSlotInteractorStrategy @@ -14,9 +16,7 @@ public void OnAdded(ItemSlotUi itemSlotUi, RestaurantManagementSo restaurantMana { var evt = GameEvents.ShowGlobalMessageEvent; // TODO : 테스트용 메세지 추후 삭제 및 변경 - evt.NewMessageKey = "today_menu_added_error_message_001"; - evt.FadeDuration = 0.5f; - evt.ShowDuration = 1f; + evt.Set("today_menu_added_error_message_001"); EventBus.Broadcast(evt); } } diff --git a/Assets/_DDD/_Scripts/GameUi/RestaurantManagementUi/TodayRestaurantStateUi/TodayCookwareInteractorStrategy.cs b/Assets/_DDD/_Scripts/GameUi/RestaurantManagementUi/TodayRestaurantStateUi/TodayCookwareInteractorStrategy.cs index 3c912097d..8f9e4c930 100644 --- a/Assets/_DDD/_Scripts/GameUi/RestaurantManagementUi/TodayRestaurantStateUi/TodayCookwareInteractorStrategy.cs +++ b/Assets/_DDD/_Scripts/GameUi/RestaurantManagementUi/TodayRestaurantStateUi/TodayCookwareInteractorStrategy.cs @@ -1,3 +1,5 @@ +using UnityEngine; + namespace DDD { public class TodayCookwareInteractorStrategy : IItemSlotInteractorStrategy @@ -14,9 +16,7 @@ public void OnAdded(ItemSlotUi itemSlotUi, RestaurantManagementSo restaurantMana { var evt = GameEvents.ShowGlobalMessageEvent; // TODO : 테스트용 메세지 추후 삭제 및 변경 - evt.NewMessageKey = "today_menu_added_error_message_001"; - evt.FadeDuration = 0.5f; - evt.ShowDuration = 1f; + evt.Set("today_menu_added_error_message_001"); EventBus.Broadcast(evt); } } diff --git a/Assets/_DDD/_Scripts/RestaurantController/RestaurantController.cs b/Assets/_DDD/_Scripts/RestaurantController/RestaurantController.cs index 1a66843b8..fd94cf758 100644 --- a/Assets/_DDD/_Scripts/RestaurantController/RestaurantController.cs +++ b/Assets/_DDD/_Scripts/RestaurantController/RestaurantController.cs @@ -10,6 +10,8 @@ public class RestaurantController : Singleton, IManager, I private const string CreateRestaurantPlayerSo = "CreateRestaurantPlayerSo"; private const string CreateEnvironmentSo = "CreateEnvironmentSo"; + private const string ReadyForRestaurantMessageKey = "ready_for_restaurant_message"; + private const string RunRestaurantMessageKey = "run_restaurnat_message"; public void PreInit() { @@ -75,12 +77,20 @@ public async Task OnReadyNewFlow(GameFlowState newFlowState) // Combine handles and return it InputManager.Instance.SwitchCurrentActionMap(InputActionMaps.Restaurant); await Task.WhenAll(playerHandle, todayMenuHandle); + + var evt = GameEvents.ShowGlobalMessageEvent; + evt.Set(ReadyForRestaurantMessageKey, newShowDuration:3f, newTextColor:Color.yellow, fontSize:60); + EventBus.Broadcast(evt); } else if (newFlowState == GameFlowState.RunRestaurant) { var restaurantCustomerStateHandle = RestaurantCustomerStateSo.OnReadyNewFlow(newFlowState); await Task.WhenAll(restaurantCustomerStateHandle); + + var evt = GameEvents.ShowGlobalMessageEvent; + evt.Set(RunRestaurantMessageKey, newShowDuration:3f, newTextColor:Color.yellow, fontSize:60); + EventBus.Broadcast(evt); } }