smart string 현지화 시스템 연동 및 글로벌 메세지 이벤트 기능 변경

This commit is contained in:
NTG_Lenovo 2025-08-13 21:18:54 +09:00
parent 2ec3ec58f9
commit d5f72c3e4c
7 changed files with 162 additions and 11 deletions

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using UnityEngine;
namespace DDD namespace DDD
{ {
@ -56,6 +57,17 @@ public class ShowGlobalMessageEvent : IEvent
public string NewMessageKey; public string NewMessageKey;
public float ShowDuration; public float ShowDuration;
public float FadeDuration; 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 public class OpenPopupUiEvent : IEvent

View File

@ -4,6 +4,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using UnityEngine; using UnityEngine;
using UnityEngine.Localization; using UnityEngine.Localization;
using UnityEngine.Localization.SmartFormat.PersistentVariables;
namespace DDD namespace DDD
{ {
@ -16,6 +17,8 @@ public class LocalizationManager : Singleton<LocalizationManager>, IManager
private const string Name = "_name"; private const string Name = "_name";
private const string Description = "_description"; private const string Description = "_description";
private readonly List<object> _singleArgBuffer = new(1);
public async void PreInit() public async void PreInit()
{ {
_localizedCache.Clear(); _localizedCache.Clear();
@ -83,15 +86,30 @@ public LocalizedString GetLocalizedString(string key)
public LocalizedString GetLocalizedDescription(string key) => GetLocalizedString(key + Description); public LocalizedString GetLocalizedDescription(string key) => GetLocalizedString(key + Description);
/// <summary> /// <summary>
/// Key값 자체를 탐색 /// 현재 로케일 기준 동기 문자열 반환 (스마트 문자열 인자 지원)
/// </summary> /// </summary>
public string GetString(string key) public string GetString(string key)
{ {
var localizedString = GetLocalizedString(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);
} }
public string GetName(string key) => GetString(key + Name);
public string GetDescription(string key) => GetString(key + Description); // 인자 없음
return LocalizationSettings.StringDatabase.GetLocalizedString(tableRef, entryRef, locale);
}
public string GetName(string key, VariablesGroupAsset variables = null) => GetString(key + Name);
public string GetDescription(string key, VariablesGroupAsset variables = null) => GetString(key + Description);
/// <summary> /// <summary>
/// 현재 사용 중인 로케일 코드 반환 (예: "ko", "en", "ja") /// 현재 사용 중인 로케일 코드 반환 (예: "ko", "en", "ja")

View File

@ -0,0 +1,107 @@
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Localization.SmartFormat.PersistentVariables;
namespace DDD
{
/// <summary>
/// Smart String 변수 전담 매니저.
/// - VariablesGroupAsset 안의 변수들을 생성/재사용하고 값만 갱신(무가비지).
/// - {day}, {playerName}, {money} 등 공용 Set API 제공.
/// </summary>
public class SmartStringVariables : Singleton<SmartStringVariables>, 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<VariablesGroupAsset>(VariablesGroupAssetKey);
Debug.Assert(_vars != null, "_variablesGroupAsset is null");
_gameStateSo = await AssetManager.LoadAsset<GameStateSo>(DataConstants.GameStateSo);
Debug.Assert(_gameStateSo != null, "_gameStateSo is null");
// 예시: day 초기 세팅 (없으면 생성, 타입 다르면 교체)
Set(Daykey, _gameStateSo.GetCurrentLevel());
}
public void PostInit() { }
public VariablesGroupAsset GetVariablesGroupAsset() => _vars;
// ---------- 공용 Set API (가비지 최소화) ----------
/// <summary>int 변수 세팅. {key} 로 접근.</summary>
public void Set(string key, int value)
{
var v = Ensure<IntVariable>(key);
if (v.Value != value) v.Value = value;
}
/// <summary>float 변수 세팅. {key} 로 접근.</summary>
public void Set(string key, float value)
{
var v = Ensure<FloatVariable>(key);
if (!Mathf.Approximately(v.Value, value)) v.Value = value;
}
/// <summary>bool 변수 세팅. {key} 로 접근.</summary>
public void Set(string key, bool value)
{
var v = Ensure<BoolVariable>(key);
if (v.Value != value) v.Value = value;
}
/// <summary>string 변수 세팅. {key} 로 접근.</summary>
public void Set(string key, string value)
{
var v = Ensure<StringVariable>(key);
if (!string.Equals(v.Value, value)) v.Value = value;
}
/// <summary>
/// enum은 보통 현지화 포맷에서 문자열로 취급하는 편이 직관적입니다.
/// 필요시 IntVariable로 바꾸고 (int)(object)value 저장하는 버전도 추가 가능.
/// </summary>
public void SetEnum<TEnum>(string key, TEnum value) where TEnum : struct
{
var v = Ensure<StringVariable>(key);
string s = value.ToString();
if (!string.Equals(v.Value, s)) v.Value = s;
}
// ---------- 유틸 ----------
/// <summary>
/// key에 해당하는 변수가 있고 타입이 일치하면 캐시 반환.
/// 없거나 타입이 다르면 교체(Add/Remove)하여 보장.
/// </summary>
private T Ensure<T>(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;
}
/// <summary>변수 존재 여부.</summary>
public bool Has(string key) => _vars != null && _vars.ContainsKey(key);
/// <summary>변수 제거.</summary>
public bool Remove(string key) => _vars != null && _vars.Remove(key);
}
}

View File

@ -50,7 +50,11 @@ private void TryDisplayNext()
var evt = _messageQueue.Dequeue(); var evt = _messageQueue.Dequeue();
_isDisplayingMessage = true; _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(); OpenPanel();
_fadeTween?.Kill(); _fadeTween?.Kill();

View File

@ -1,3 +1,5 @@
using UnityEngine;
namespace DDD namespace DDD
{ {
public class TodayMenuInteractorStrategy : IItemSlotInteractorStrategy public class TodayMenuInteractorStrategy : IItemSlotInteractorStrategy
@ -14,9 +16,7 @@ public void OnAdded(ItemSlotUi itemSlotUi, RestaurantManagementSo restaurantMana
{ {
var evt = GameEvents.ShowGlobalMessageEvent; var evt = GameEvents.ShowGlobalMessageEvent;
// TODO : 테스트용 메세지 추후 삭제 및 변경 // TODO : 테스트용 메세지 추후 삭제 및 변경
evt.NewMessageKey = "today_menu_added_error_message_001"; evt.Set("today_menu_added_error_message_001");
evt.FadeDuration = 0.5f;
evt.ShowDuration = 1f;
EventBus.Broadcast(evt); EventBus.Broadcast(evt);
} }
} }

View File

@ -1,3 +1,5 @@
using UnityEngine;
namespace DDD namespace DDD
{ {
public class TodayCookwareInteractorStrategy : IItemSlotInteractorStrategy public class TodayCookwareInteractorStrategy : IItemSlotInteractorStrategy
@ -14,9 +16,7 @@ public void OnAdded(ItemSlotUi itemSlotUi, RestaurantManagementSo restaurantMana
{ {
var evt = GameEvents.ShowGlobalMessageEvent; var evt = GameEvents.ShowGlobalMessageEvent;
// TODO : 테스트용 메세지 추후 삭제 및 변경 // TODO : 테스트용 메세지 추후 삭제 및 변경
evt.NewMessageKey = "today_menu_added_error_message_001"; evt.Set("today_menu_added_error_message_001");
evt.FadeDuration = 0.5f;
evt.ShowDuration = 1f;
EventBus.Broadcast(evt); EventBus.Broadcast(evt);
} }
} }

View File

@ -10,6 +10,8 @@ public class RestaurantController : Singleton<RestaurantController>, IManager, I
private const string CreateRestaurantPlayerSo = "CreateRestaurantPlayerSo"; private const string CreateRestaurantPlayerSo = "CreateRestaurantPlayerSo";
private const string CreateEnvironmentSo = "CreateEnvironmentSo"; private const string CreateEnvironmentSo = "CreateEnvironmentSo";
private const string ReadyForRestaurantMessageKey = "ready_for_restaurant_message";
private const string RunRestaurantMessageKey = "run_restaurnat_message";
public void PreInit() public void PreInit()
{ {
@ -75,12 +77,20 @@ public async Task OnReadyNewFlow(GameFlowState newFlowState)
// Combine handles and return it // Combine handles and return it
InputManager.Instance.SwitchCurrentActionMap(InputActionMaps.Restaurant); InputManager.Instance.SwitchCurrentActionMap(InputActionMaps.Restaurant);
await Task.WhenAll(playerHandle, todayMenuHandle); 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) else if (newFlowState == GameFlowState.RunRestaurant)
{ {
var restaurantCustomerStateHandle = RestaurantCustomerStateSo.OnReadyNewFlow(newFlowState); var restaurantCustomerStateHandle = RestaurantCustomerStateSo.OnReadyNewFlow(newFlowState);
await Task.WhenAll(restaurantCustomerStateHandle); await Task.WhenAll(restaurantCustomerStateHandle);
var evt = GameEvents.ShowGlobalMessageEvent;
evt.Set(RunRestaurantMessageKey, newShowDuration:3f, newTextColor:Color.yellow, fontSize:60);
EventBus.Broadcast(evt);
} }
} }