so 싱글톤 추가
This commit is contained in:
parent
b8eec075ab
commit
9d57bcb040
@ -10,22 +10,22 @@ public class GameController : Singleton<GameController>, IManager, IGameFlowHand
|
||||
{
|
||||
[SerializeField] private AssetReference _gameData;
|
||||
|
||||
public GameData GameData { get; private set; }
|
||||
public GameState GameState { get; private set; }
|
||||
public GameData GetGameData() => GameData.Instance;
|
||||
public GameState GetGameState() => GameState.Instance;
|
||||
|
||||
private List<FlowController> _gameFlowControllers = new();
|
||||
private static readonly List<Type> GameFlowControllerTypes = new();
|
||||
|
||||
public void PreInit()
|
||||
{
|
||||
LoadOrCreateRestaurantState();
|
||||
CreateGameState();
|
||||
RegisterFlowHandler();
|
||||
}
|
||||
|
||||
public async Task Init()
|
||||
{
|
||||
await LoadData();
|
||||
await GameData.LoadData();
|
||||
await GetGameData().LoadData();
|
||||
await InitializeAllFlowControllers();
|
||||
}
|
||||
|
||||
@ -33,10 +33,9 @@ public void PostInit()
|
||||
{
|
||||
}
|
||||
|
||||
private void LoadOrCreateRestaurantState()
|
||||
private void CreateGameState()
|
||||
{
|
||||
// TODO : Load states from saved files. if none, create them.
|
||||
GameState = ScriptableObject.CreateInstance<GameState>();
|
||||
GameState.CreateScriptSingleton();
|
||||
}
|
||||
|
||||
private void RegisterFlowHandler()
|
||||
@ -49,7 +48,7 @@ private async Task InitializeAllFlowControllers()
|
||||
// Create controllers and initialize them
|
||||
foreach (var gameFlowControllerType in GameFlowControllerTypes)
|
||||
{
|
||||
// create new controllers from restaurantFlowControllerType
|
||||
// create new controllers from gameFlowControllerType
|
||||
var newController = ScriptableObject.CreateInstance(gameFlowControllerType);
|
||||
var newFlowController = newController as FlowController;
|
||||
_gameFlowControllers.Add(newFlowController);
|
||||
@ -64,14 +63,6 @@ private async Task InitializeAllFlowControllers()
|
||||
|
||||
private async Task LoadData()
|
||||
{
|
||||
var gameDataHandle = _gameData.LoadAssetAsync<GameData>();
|
||||
|
||||
await gameDataHandle.Task;
|
||||
|
||||
GameData = gameDataHandle.Result;
|
||||
|
||||
Debug.Assert(GameData != null, "GameData is null");
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
namespace DDD
|
||||
{
|
||||
[CreateAssetMenu(fileName = "GameData", menuName = "GameData/GameData")]
|
||||
public class GameData : ScriptableObject
|
||||
public class GameData : ScriptSingleton<GameData>
|
||||
{
|
||||
[SerializeField] private AssetReference _gameLocalizationData;
|
||||
|
||||
|
@ -97,7 +97,7 @@ public string GetString(string key)
|
||||
var entryRef = key;
|
||||
var locale = LocalizationSettings.SelectedLocale;
|
||||
|
||||
VariablesGroupAsset variables = GameController.Instance.GameData.LocalizationData.SmartStringVariableGroup;
|
||||
VariablesGroupAsset variables = GameData.Instance.LocalizationData.SmartStringVariableGroup;
|
||||
if (variables != null)
|
||||
{
|
||||
_singleArgBuffer.Clear();
|
||||
|
@ -50,8 +50,8 @@ public void PreInit() { }
|
||||
|
||||
public async Task Init()
|
||||
{
|
||||
var gameLevelStateSo = GameController.Instance.GameState.LevelState;
|
||||
var restaurantStateSo = RestaurantController.Instance.RestaurantState.ManagementState;
|
||||
var gameLevelStateSo = GameState.Instance.LevelState;
|
||||
var restaurantStateSo = RestaurantState.Instance.ManagementState;
|
||||
|
||||
// 예시: day 초기 세팅 (없으면 생성, 타입 다르면 교체)
|
||||
Set(_smartStringKeys[smartStringKey.Day], gameLevelStateSo.Level);
|
||||
@ -71,7 +71,7 @@ public void PostInit()
|
||||
EventBus.Register<SmartVariablesDirtyEvent>(this);
|
||||
}
|
||||
|
||||
private RestaurantManagementState GetRestaurantState() => RestaurantController.Instance.RestaurantState.ManagementState;
|
||||
private RestaurantManagementState GetRestaurantState() => RestaurantState.Instance.ManagementState;
|
||||
|
||||
public void Invoke(SmartVariablesDirtyEvent evt)
|
||||
{
|
||||
@ -113,7 +113,7 @@ public void RefreshChecklistTargets()
|
||||
|
||||
public void RefreshDay()
|
||||
{
|
||||
var gameLevelStateSo = GameController.Instance.GameState.LevelState;
|
||||
var gameLevelStateSo = GameState.Instance.LevelState;
|
||||
Set(_smartStringKeys[smartStringKey.Day], gameLevelStateSo.Level);
|
||||
}
|
||||
|
||||
@ -179,7 +179,7 @@ public void SetEnum<TEnum>(string key, TEnum value) where TEnum : struct
|
||||
return null;
|
||||
}
|
||||
|
||||
var smartStringVariableGroup = GameController.Instance.GameData.LocalizationData.SmartStringVariableGroup;
|
||||
var smartStringVariableGroup = GameData.Instance.LocalizationData.SmartStringVariableGroup;
|
||||
|
||||
if (smartStringVariableGroup.TryGetValue(key, out var existing))
|
||||
{
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
namespace DDD
|
||||
{
|
||||
public class GameState : ScriptableObject
|
||||
public class GameState : ScriptSingleton<GameState>
|
||||
{
|
||||
[SerializeField] private AssetReference _gameLevelState;
|
||||
|
||||
|
@ -29,7 +29,7 @@ public class ChecklistView : MonoBehaviour, IEventHandler<TodayMenuAddedEvent>,
|
||||
|
||||
public void Initalize()
|
||||
{
|
||||
restaurantManagementStateSo = RestaurantController.Instance.RestaurantState.ManagementState;
|
||||
restaurantManagementStateSo = RestaurantState.Instance.ManagementState;
|
||||
|
||||
_checklistDatas = new List<ChecklistData>(3);
|
||||
_checklistDatas = GetComponentsInChildren<ChecklistData>().ToList();
|
||||
|
@ -40,7 +40,7 @@ public void Setup(ItemSlotUi ui, ItemViewModel model)
|
||||
|
||||
public RuntimeAnimatorController GetAnimatorController()
|
||||
{
|
||||
return RestaurantController.Instance.RestaurantData.ManagementData.InventorySlotUiAnimatorController;
|
||||
return RestaurantData.Instance.ManagementData.InventorySlotUiAnimatorController;
|
||||
}
|
||||
|
||||
public void OnInventoryChanged(ItemSlotUi ui)
|
||||
|
@ -38,8 +38,8 @@ private void OnDisable()
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
restaurantManagementStateSo = RestaurantController.Instance.RestaurantState.ManagementState;
|
||||
restaurantManagementDataSo = RestaurantController.Instance.RestaurantData.ManagementData;
|
||||
restaurantManagementStateSo = RestaurantState.Instance.ManagementState;
|
||||
restaurantManagementDataSo = RestaurantData.Instance.ManagementData;
|
||||
Debug.Assert(restaurantManagementDataSo != null, "_todayMenuDataSo != null");
|
||||
|
||||
Clear();
|
||||
|
@ -49,7 +49,7 @@ private void OnDisable()
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
restaurantManagementDataSo = RestaurantController.Instance.RestaurantData.ManagementData;
|
||||
restaurantManagementDataSo = RestaurantData.Instance.ManagementData;
|
||||
}
|
||||
|
||||
public void Invoke(ItemSlotSelectedEvent evt)
|
||||
|
@ -60,7 +60,7 @@ private void UpdateHoldProgress()
|
||||
|
||||
private void ProcessCompleteBatchAction()
|
||||
{
|
||||
if (RestaurantController.Instance.RestaurantState.ManagementState.GetChecklistStates().Any(state => state == false))
|
||||
if (RestaurantState.Instance.ManagementState.GetChecklistStates().Any(state => state == false))
|
||||
{
|
||||
ShowChecklistFailedPopup();
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ public void OnAdded(ItemSlotUi itemSlotUi)
|
||||
|
||||
if (inventorySlotUiStrategy.CanCrafting(itemSlotUi))
|
||||
{
|
||||
RestaurantController.Instance.RestaurantState.ManagementState.TryAddTodayMenu(itemSlotUi.Model);
|
||||
RestaurantState.Instance.ManagementState.TryAddTodayMenu(itemSlotUi.Model);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -25,7 +25,7 @@ public void OnRemoved(ItemSlotUi itemSlotUi)
|
||||
{
|
||||
if (itemSlotUi.Strategy is InventorySlotUiStrategy) return;
|
||||
|
||||
RestaurantController.Instance.RestaurantState.ManagementState.TryRemoveTodayMenu(itemSlotUi.Model);
|
||||
RestaurantState.Instance.ManagementState.TryRemoveTodayMenu(itemSlotUi.Model);
|
||||
}
|
||||
}
|
||||
}
|
@ -35,7 +35,7 @@ public void Setup(ItemSlotUi ui, ItemViewModel model)
|
||||
}
|
||||
|
||||
string markSpriteKey = null;
|
||||
if (RestaurantController.Instance.RestaurantState.ManagementState.IsCookwareMatched(ui.Model.Id))
|
||||
if (RestaurantState.Instance.ManagementState.IsCookwareMatched(ui.Model.Id))
|
||||
{
|
||||
markSpriteKey = SpriteConstants.CheckYesSpriteKey;
|
||||
}
|
||||
@ -51,7 +51,7 @@ public void Setup(ItemSlotUi ui, ItemViewModel model)
|
||||
|
||||
public RuntimeAnimatorController GetAnimatorController()
|
||||
{
|
||||
return RestaurantController.Instance.RestaurantData.ManagementData.TodayMenuSlotUiAnimatorController;
|
||||
return RestaurantData.Instance.ManagementData.TodayMenuSlotUiAnimatorController;
|
||||
}
|
||||
}
|
||||
}
|
@ -23,8 +23,8 @@ private void OnDestroy()
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
restaurantManagementStateSo = RestaurantController.Instance.RestaurantState.ManagementState;
|
||||
restaurantManagementDataSo = RestaurantController.Instance.RestaurantData.ManagementData;
|
||||
restaurantManagementStateSo = RestaurantState.Instance.ManagementState;
|
||||
restaurantManagementDataSo = RestaurantData.Instance.ManagementData;
|
||||
|
||||
foreach (Transform child in _todayFoodContent)
|
||||
{
|
||||
|
@ -10,7 +10,7 @@ public void OnAdded(ItemSlotUi itemSlotUi)
|
||||
|
||||
if (inventorySlotUiStrategy.CanCrafting(itemSlotUi))
|
||||
{
|
||||
RestaurantController.Instance.RestaurantState.ManagementState.TryAddTodayCookware(itemSlotUi.Model);
|
||||
RestaurantState.Instance.ManagementState.TryAddTodayCookware(itemSlotUi.Model);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -25,7 +25,7 @@ public void OnRemoved(ItemSlotUi itemSlotUi)
|
||||
{
|
||||
if (itemSlotUi.Strategy is InventorySlotUiStrategy) return;
|
||||
|
||||
RestaurantController.Instance.RestaurantState.ManagementState.TryRemoveTodayCookware(itemSlotUi.Model);
|
||||
RestaurantState.Instance.ManagementState.TryRemoveTodayCookware(itemSlotUi.Model);
|
||||
}
|
||||
}
|
||||
}
|
@ -18,7 +18,7 @@ public void Setup(ItemSlotUi ui, ItemViewModel model)
|
||||
}
|
||||
|
||||
string markSpriteKey = null;
|
||||
if (RestaurantController.Instance.RestaurantState.ManagementState.IsTodayMenuMatched(ui.Model.Id))
|
||||
if (RestaurantState.Instance.ManagementState.IsTodayMenuMatched(ui.Model.Id))
|
||||
{
|
||||
markSpriteKey = SpriteConstants.CheckYesSpriteKey;
|
||||
}
|
||||
@ -34,7 +34,7 @@ public void Setup(ItemSlotUi ui, ItemViewModel model)
|
||||
|
||||
public RuntimeAnimatorController GetAnimatorController()
|
||||
{
|
||||
return RestaurantController.Instance.RestaurantData.ManagementData.TodayMenuSlotUiAnimatorController;
|
||||
return RestaurantData.Instance.ManagementData.TodayMenuSlotUiAnimatorController;
|
||||
}
|
||||
}
|
||||
}
|
@ -23,8 +23,8 @@ private void OnDestroy()
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
restaurantManagementStateSo = RestaurantController.Instance.RestaurantState.ManagementState;
|
||||
restaurantManagementDataSo = RestaurantController.Instance.RestaurantData.ManagementData;
|
||||
restaurantManagementStateSo = RestaurantState.Instance.ManagementState;
|
||||
restaurantManagementDataSo = RestaurantData.Instance.ManagementData;
|
||||
|
||||
foreach (Transform child in _todayWorkerContent)
|
||||
{
|
||||
|
@ -26,7 +26,7 @@ public void Setup(ItemSlotUi ui, ItemViewModel model)
|
||||
|
||||
public RuntimeAnimatorController GetAnimatorController()
|
||||
{
|
||||
return RestaurantController.Instance.RestaurantData.ManagementData.TodayMenuSlotUiAnimatorController;
|
||||
return RestaurantData.Instance.ManagementData.TodayMenuSlotUiAnimatorController;
|
||||
}
|
||||
}
|
||||
}
|
@ -9,7 +9,7 @@ public class RestaurantPlayerInput : MonoBehaviour
|
||||
|
||||
private void Start()
|
||||
{
|
||||
_playerDataSo = RestaurantController.Instance.RestaurantData.PlayerData;
|
||||
_playerDataSo = RestaurantData.Instance.PlayerData;
|
||||
_playerDataSo.OpenManagementUiAction = InputManager.Instance.GetAction(InputActionMaps.Restaurant, nameof(RestaurantActions.OpenManagementUi));
|
||||
|
||||
_playerDataSo.OpenManagementUiAction.performed += OnOpenManagementUi;
|
||||
|
@ -17,7 +17,7 @@ protected override void Start()
|
||||
|
||||
private Task Initialize()
|
||||
{
|
||||
_restaurantPlayerDataSo = RestaurantController.Instance.RestaurantData.PlayerData;
|
||||
_restaurantPlayerDataSo = RestaurantData.Instance.PlayerData;
|
||||
Debug.Assert(_restaurantPlayerDataSo != null, "_restaurantPlayerDataSo is null");
|
||||
|
||||
_restaurantPlayerDataSo!.InteractAction = InputManager.Instance.GetAction(InputActionMaps.Restaurant, nameof(RestaurantActions.Interact));
|
||||
|
@ -81,7 +81,7 @@ private System.Threading.Tasks.Task InitializePlayerData()
|
||||
{
|
||||
try
|
||||
{
|
||||
_playerDataSo = RestaurantController.Instance.RestaurantData.PlayerData;
|
||||
_playerDataSo = RestaurantData.Instance.PlayerData;
|
||||
SubscribeToInputEvents();
|
||||
_isInitialized = true;
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ public override Task InitializeController()
|
||||
|
||||
public override Task InitializeState()
|
||||
{
|
||||
_environmentState = RestaurantController.Instance.RestaurantState.EnvironmentState;
|
||||
_environmentState = RestaurantState.Instance.EnvironmentState;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,7 @@ public override Task InitializeController()
|
||||
public override Task InitializeState()
|
||||
{
|
||||
// Load default asset
|
||||
RestaurantController.Instance.RestaurantState.ManagementState.InitializeReadyForRestaurant();
|
||||
RestaurantState.Instance.ManagementState.InitializeReadyForRestaurant();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
@ -8,14 +8,13 @@ public class RestaurantRunController : FlowController
|
||||
RestaurantCustomerState _restaurantCustomerStateSo;
|
||||
public override Task InitializeController()
|
||||
{
|
||||
_restaurantCustomerStateSo = RestaurantController.Instance.RestaurantState.CustomerState;
|
||||
_restaurantCustomerStateSo = RestaurantState.Instance.CustomerState;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public override Task InitializeState()
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
|
||||
}
|
||||
|
||||
public override async Task OnReadyNewFlow(GameFlowState newFlowState)
|
||||
|
@ -15,7 +15,7 @@ public override Task RunFlowTask()
|
||||
{
|
||||
// TODO : Base prefab from EnvironmentDataSo
|
||||
|
||||
var props = RestaurantController.Instance.RestaurantState.EnvironmentState.Props;
|
||||
var props = RestaurantState.Instance.EnvironmentState.Props;
|
||||
foreach (var prop in props)
|
||||
{
|
||||
// TODO : Instantiate and Initialize
|
||||
|
@ -20,7 +20,7 @@ public override Task RunFlowTask()
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
var playerPrefab = RestaurantController.Instance.RestaurantData.PlayerData.PlayerPrefab;
|
||||
var playerPrefab = RestaurantData.Instance.PlayerData.PlayerPrefab;
|
||||
if (playerPrefab == null)
|
||||
{
|
||||
Debug.LogError("PlayerPrefab이 설정되지 않았습니다!");
|
||||
|
@ -10,8 +10,8 @@ public class RestaurantController : Singleton<RestaurantController>, IManager, I
|
||||
{
|
||||
[SerializeField] private AssetReference _restaurantData;
|
||||
|
||||
public RestaurantData RestaurantData { get; private set; }
|
||||
public RestaurantState RestaurantState { get; private set; }
|
||||
public RestaurantData GetRestaurantData() => RestaurantData.Instance;
|
||||
public RestaurantState GetRestaurantState() => RestaurantState.Instance;
|
||||
|
||||
private List<FlowController> _restaurantFlowControllers = new();
|
||||
|
||||
@ -27,14 +27,14 @@ public class RestaurantController : Singleton<RestaurantController>, IManager, I
|
||||
|
||||
public void PreInit()
|
||||
{
|
||||
LoadOrCreateRestaurantState();
|
||||
CreateRestaurantState();
|
||||
RegisterFlowHandler();
|
||||
}
|
||||
|
||||
public async Task Init()
|
||||
{
|
||||
await LoadData();
|
||||
await RestaurantData.LoadData();
|
||||
await GetRestaurantData().LoadData();
|
||||
await InitializeAllFlowControllers();
|
||||
}
|
||||
|
||||
@ -42,10 +42,9 @@ public void PostInit()
|
||||
{
|
||||
}
|
||||
|
||||
private void LoadOrCreateRestaurantState()
|
||||
private void CreateRestaurantState()
|
||||
{
|
||||
// TODO : Load states from saved files. if none, create them.
|
||||
RestaurantState = ScriptableObject.CreateInstance<RestaurantState>();
|
||||
RestaurantState.CreateScriptSingleton();
|
||||
}
|
||||
|
||||
private void RegisterFlowHandler()
|
||||
@ -73,13 +72,7 @@ private async Task InitializeAllFlowControllers()
|
||||
|
||||
private async Task LoadData()
|
||||
{
|
||||
var restaurantDataHandle = _restaurantData.LoadAssetAsync<RestaurantData>();
|
||||
|
||||
await restaurantDataHandle.Task;
|
||||
|
||||
RestaurantData = restaurantDataHandle.Result;
|
||||
|
||||
Debug.Assert(RestaurantData != null, "RestaurantData is null");
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task OnReadyNewFlow(GameFlowState newFlowState)
|
||||
|
@ -5,7 +5,7 @@
|
||||
namespace DDD
|
||||
{
|
||||
[CreateAssetMenu(fileName = "RestaurantData", menuName = "RestaurantData/RestaurantData", order = 0)]
|
||||
public class RestaurantData : ScriptableObject
|
||||
public class RestaurantData : ScriptSingleton<RestaurantData>
|
||||
{
|
||||
[SerializeField] private AssetReference _restaurantPlayerData;
|
||||
[SerializeField] private AssetReference _restaurantManagementData;
|
||||
|
@ -15,7 +15,7 @@ public bool ExecuteInteraction(IInteractor interactor, IInteractable interactabl
|
||||
|
||||
private RestaurantManagementState GetManagementState()
|
||||
{
|
||||
return RestaurantController.Instance.RestaurantState.ManagementState;
|
||||
return RestaurantState.Instance.ManagementState;
|
||||
}
|
||||
|
||||
public bool CanExecuteInteraction(IInteractor interactor = null, IInteractable interactable = null, ScriptableObject payloadSo = null)
|
||||
|
@ -51,7 +51,7 @@ private async Task InitializeRunRestaurant()
|
||||
{
|
||||
_iCustomerFactory = new CustomerFactory();
|
||||
|
||||
var currentGameLevel = GameController.Instance.GameState.LevelState.Level;
|
||||
var currentGameLevel = GameState.Instance.LevelState.Level;
|
||||
if (_levelDataSo == null)
|
||||
{
|
||||
_levelDataSo = DataManager.Instance.GetDataSo<LevelDataSo>();
|
||||
|
@ -86,7 +86,7 @@ public bool IsOpenable()
|
||||
|
||||
public RestaurantManagementData GetManagementData()
|
||||
{
|
||||
return RestaurantController.Instance.RestaurantData.ManagementData;
|
||||
return RestaurantData.Instance.ManagementData;
|
||||
}
|
||||
|
||||
public bool TryAddTodayMenu(ItemViewModel model)
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace DDD
|
||||
{
|
||||
public class RestaurantState : ScriptableObject
|
||||
public class RestaurantState : ScriptSingleton<RestaurantState>
|
||||
{
|
||||
public RestaurantManagementState ManagementState { get; private set; }
|
||||
public RestaurantRunState RunState { get; private set; }
|
||||
|
128
Assets/_DDD/_Scripts/Utilities/ScriptSingleton.cs
Normal file
128
Assets/_DDD/_Scripts/Utilities/ScriptSingleton.cs
Normal file
@ -0,0 +1,128 @@
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
|
||||
namespace DDD
|
||||
{
|
||||
/// <summary>
|
||||
/// Addressables를 통해 ScriptableObject 에셋을 로드하여 싱글톤으로 제공하는 베이스 클래스.
|
||||
/// - 첫 접근 시 Addressables에서 타입명(네임스페이스 제외)을 키로 동기 로드합니다.
|
||||
/// - 로드에 실패하면 예외를 발생합니다. (새로 생성하지 않습니다)
|
||||
/// - 이미 로드된 경우 캐시된 인스턴스를 반환합니다.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">구현 타입</typeparam>
|
||||
public abstract class ScriptSingleton<T> : ScriptableObject where T : ScriptSingleton<T>
|
||||
{
|
||||
#region Fields
|
||||
[CanBeNull]
|
||||
private static T _instance;
|
||||
|
||||
[NotNull]
|
||||
private static readonly object _lock = new();
|
||||
|
||||
private static bool _isQuitting;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
[NotNull]
|
||||
public static T Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance != null)
|
||||
return _instance;
|
||||
|
||||
if (_isQuitting)
|
||||
throw new InvalidOperationException($"애플리케이션 종료 중에는 '{typeof(T).Name}' 인스턴스를 로드할 수 없습니다.");
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
// 이중 체크 락킹 패턴
|
||||
if (_instance != null)
|
||||
return _instance;
|
||||
|
||||
try
|
||||
{
|
||||
var key = ResolveAddressKey();
|
||||
var handle = Addressables.LoadAssetAsync<T>(key);
|
||||
|
||||
// 동기 로드: 메인 스레드에서 호출할 것을 권장합니다.
|
||||
var loaded = handle.WaitForCompletion();
|
||||
|
||||
if (handle.Status != AsyncOperationStatus.Succeeded || loaded == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Addressables 로드 실패: 타입='{typeof(T).Name}', key='{key}'");
|
||||
}
|
||||
|
||||
_instance = loaded;
|
||||
_instance.hideFlags = HideFlags.DontUnloadUnusedAsset;
|
||||
_instance.OnInstanceLoaded();
|
||||
|
||||
return _instance;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// 새로운 인스턴스를 생성하고 싱글톤으로 등록합니다.
|
||||
/// Addressables에 등록되지 않은 경우 사용합니다.
|
||||
/// </summary>
|
||||
public static T CreateScriptSingleton()
|
||||
{
|
||||
if (_instance != null)
|
||||
{
|
||||
Debug.LogWarning($"[ScriptSingleton] {typeof(T).Name} 인스턴스가 이미 존재합니다. 기존 인스턴스를 반환합니다.");
|
||||
return _instance;
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
if (_instance != null)
|
||||
return _instance;
|
||||
|
||||
var newInstance = ScriptableObject.CreateInstance<T>();
|
||||
_instance = newInstance;
|
||||
_instance.hideFlags = HideFlags.DontUnloadUnusedAsset;
|
||||
_instance.OnInstanceLoaded();
|
||||
|
||||
Debug.Log($"[ScriptSingleton] {typeof(T).Name} 인스턴스를 생성하고 싱글톤으로 등록했습니다.");
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 사용자 정의 초기화 훅. 인스턴스가 로드된 뒤 1회 호출됩니다.
|
||||
/// </summary>
|
||||
protected virtual void OnInstanceLoaded() { }
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
|
||||
private static void RegisterQuitHandler()
|
||||
{
|
||||
Application.quitting -= OnApplicationQuitting;
|
||||
Application.quitting += OnApplicationQuitting;
|
||||
}
|
||||
|
||||
private static void OnApplicationQuitting()
|
||||
{
|
||||
_isQuitting = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Address Key를 해석합니다. 요구사항에 따라 타입명(네임스페이스 제외) 그대로를 사용합니다.
|
||||
/// </summary>
|
||||
private static string ResolveAddressKey()
|
||||
{
|
||||
return typeof(T).Name;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
2
Assets/_DDD/_Scripts/Utilities/ScriptSingleton.cs.meta
Normal file
2
Assets/_DDD/_Scripts/Utilities/ScriptSingleton.cs.meta
Normal file
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0544b64d4ef0a744dbd9ee6bcf4ecc00
|
Loading…
Reference in New Issue
Block a user