Merge remote-tracking branch 'origin/develop' into develop
# Conflicts: # Assets/AddressableAssetsData/AssetGroups/Default Local Group.asset
This commit is contained in:
commit
574ca3e9d3
7
.aiignore
Normal file
7
.aiignore
Normal file
@ -0,0 +1,7 @@
|
||||
# An .aiignore file follows the same syntax as a .gitignore file.
|
||||
# .gitignore documentation: https://git-scm.com/docs/gitignore
|
||||
# Junie will ask for explicit approval before view or edit the file or file within a directory listed in .aiignore.
|
||||
# Only files contents is protected, Junie is still allowed to view file names even if they are listed in .aiignore.
|
||||
# Be aware that the files you included in .aiignore can still be accessed by Junie in two cases:
|
||||
# - If Brave Mode is turned on.
|
||||
# - If a command has been added to the Allowlist — Junie will not ask for confirmation, even if it accesses - files and folders listed in .aiignore.
|
51
.junie/guidelines.md
Normal file
51
.junie/guidelines.md
Normal file
@ -0,0 +1,51 @@
|
||||
# 프로젝트 가이드라인 — ProjectDDD (Unity)
|
||||
|
||||
이 저장소는 주로 Unity 에디터를 통해 편집 및 실행됩니다. 코드는 Unity의 .NET/Mono 환경을 대상으로 하며, 컴파일은 일반적인 `dotnet` CLI 빌드가 아니라 대개 Unity가 주도합니다.
|
||||
|
||||
## 저장소 레이아웃(상위 수준)
|
||||
- `Assets/` – 게임 콘텐츠와 스크립트. 대부분의 수정은 여기서 이루어집니다.
|
||||
- `Packages/` – Unity 패키지 매니페스트와 임베디드 패키지.
|
||||
- `ProjectSettings/` 및 `UserSettings/` – Unity 설정. 지시가 없는 한 수동으로 수정하지 마십시오.
|
||||
- `Library/`, `Temp/`, `obj/`, `Logs/` – Unity/IDE가 생성. 이 디렉터리는 수정하거나 커밋하지 마십시오.
|
||||
- `ServerData/`, `Docs/` – 프로젝트별 데이터와 문서가 있다면 여기에 있습니다.
|
||||
- 다수의 `*.csproj` 파일 – Rider/IDE 지원을 위한 자동 생성 파일; 특별히 필요하지 않는 한 수동 수정 금지.
|
||||
|
||||
## 이 프로젝트에서 Junie의 작업 방식
|
||||
- 문제를 직접 해결하는 최소하고도 목표 지향적인 코드 변경을 선호합니다.
|
||||
- Unity가 생성한 폴더들(`Library/`, `Temp/`, `obj/`, `Logs/`)이나 패키지 캐시 내용은 수정하지 않습니다.
|
||||
- 새로운 스크립트는 `Assets/` 아래 적절한 도메인 폴더에 배치합니다. `_DDD/_Scripts` 하위의 기존 폴더 규칙을 유지합니다.
|
||||
- `.csproj` 파일은 수정하지 않습니다. Unity가 재생성합니다.
|
||||
- Addressables를 다룰 때는 그룹 설정을 일관되게 유지하고 의도치 않은 대용량 데이터 변경을 커밋하지 않습니다.
|
||||
|
||||
## 빌드, 실행, 테스트
|
||||
- 기본 실행 환경은 Unity Editor/Player입니다. 독립 실행형 단위 테스트가 없을 수 있습니다.
|
||||
- 작업이 명시적으로 요구하지 않는 한 .NET 테스트 러너나 CLI 빌드를 시도하지 말고, 컴파일은 Unity에 맡기십시오.
|
||||
- 코드를 추가할 때는 Unity에서 컴파일 가능한지 확인하십시오(런타임 코드에서 에디터 전용 API 직접 호출 금지, 네임스페이스 올바름, 사용할 수 없는 API 사용 금지).
|
||||
- 본 문서와 같은 순수 문서/설정 작업에서는 빌드가 필요 없습니다.
|
||||
|
||||
## 코딩 및 스타일 가이드(C#/Unity)
|
||||
- 표준 C# 컨벤션을 따르십시오: 공용 타입과 멤버는 PascalCase, 지역 변수와 매개변수는 camelCase.
|
||||
- private 직렬화 필드는 `[SerializeField] private Type _fieldName;` 형태를 선호하고, 필요 시 프로퍼티로 노출하십시오.
|
||||
- Unity API 호출은 메인 스레드에서 수행하십시오. `Update`/`FixedUpdate` 내부의 과도한 할당을 피하십시오.
|
||||
- `async`/`await`는 신중하게 사용하십시오. 대부분의 엔진 API 호출에는 Unity 메인 스레드 동기화가 필요합니다.
|
||||
- 거대한 모놀리식 구조보다 작은, 역할에 집중된 컴포넌트 구성을 선호합니다. 프로젝트의 DDD 경계를 준수하십시오.
|
||||
- null 체크와 가드 절을 추가하고, 개발 빌드에서는 명확한 메시지와 함께 빠르게 실패하도록 하십시오.
|
||||
|
||||
## Addressables 및 리소스
|
||||
- `Addressables.LoadAssetAsync<T>`로 로드하고, 사용 후 핸들을 해제하여 누수를 방지하십시오.
|
||||
- 일반 게임 플레이 코드에서 Addressable 그룹을 프로그래밍적으로 수정하지 마십시오. 그룹/라벨 변경은 명시적으로 필요하지 않는 한 에디터에서 수행하십시오.
|
||||
|
||||
## 수정 금지(명시적 지시가 없는 한)
|
||||
- `Library/`, `Temp/`, `obj/`, `Logs/`, 그리고 `Library/PackageCache/` 아래의 Unity 패키지 캐시.
|
||||
- 루트의 자동 생성 `*.csproj` 파일.
|
||||
- 작업과 무관한 대용량 바이너리 에셋.
|
||||
|
||||
## 자동화 작업의 완료 정의
|
||||
- 이슈를 완전히 충족하는 최소 변경을 제공합니다.
|
||||
- 각 응답에 계획, 진행 상황, 다음 단계를 포함한 `<UPDATE>`를 포함합니다.
|
||||
- 코드가 변경되었다면, 에지 케이스를 고려하고 런타임 경로에서 에디터 전용 API 사용을 피하십시오.
|
||||
- 제출 전에 의도치 않은 파일(특히 생성 디렉터리와 Addressable 그룹)이 수정되지 않았는지 확인하십시오.
|
||||
|
||||
## 깃
|
||||
- 깃 커밋 시, 한국어로 커밋 메시지를 작성합니다.
|
||||
- 깃 커밋 메시지는 간결하고 핵심 정보만 포함해야 합니다.
|
@ -2,7 +2,8 @@
|
||||
"name": "Febucci.TextAnimator.Demo.Runtime",
|
||||
"references": [
|
||||
"Unity.TextMeshPro",
|
||||
"Febucci.TextAnimator.Runtime"
|
||||
"Febucci.TextAnimator.Runtime",
|
||||
"Febucci.Attributes.Runtime"
|
||||
],
|
||||
"optionalUnityReferences": [],
|
||||
"includePlatforms": [],
|
||||
|
@ -2,7 +2,8 @@
|
||||
"name": "Febucci.TextAnimator.Editor",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"GUID:1e113d3b5d77bc04eab508251483e8ff"
|
||||
"GUID:1e113d3b5d77bc04eab508251483e8ff",
|
||||
"GUID:448b0b55421917e4784a8f2f7449081f"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
|
@ -3,7 +3,8 @@
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"GUID:1e113d3b5d77bc04eab508251483e8ff",
|
||||
"GUID:6055be8ebefd69e48b49212b09b47b2f"
|
||||
"GUID:6055be8ebefd69e48b49212b09b47b2f",
|
||||
"GUID:448b0b55421917e4784a8f2f7449081f"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
|
@ -812,6 +812,7 @@ MonoBehaviour:
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
_enableBlockImage: 1
|
||||
_uiActionsInputBinding: {fileID: 11400000, guid: 99d3d87bd43df65488e757c43a308f36, type: 2}
|
||||
_messageLabel: {fileID: 3495127426411772216}
|
||||
_messageLabelLocalizeStringEvent: {fileID: 7334955628972040157}
|
||||
_cancelButton: {fileID: 3014273876221658359}
|
||||
|
@ -1091,6 +1091,10 @@ PrefabInstance:
|
||||
propertyPath: m_Name
|
||||
value: CookwareTabButton
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1035128454163554855, guid: b8766f1471289d74bbcdc2f5ad979e8b, type: 3}
|
||||
propertyPath: m_Navigation.m_Mode
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2720195383270857804, guid: b8766f1471289d74bbcdc2f5ad979e8b, type: 3}
|
||||
propertyPath: m_text
|
||||
value: "\uC7A5\uBE44"
|
||||
@ -4858,6 +4862,10 @@ PrefabInstance:
|
||||
propertyPath: m_Name
|
||||
value: MenuTabButton
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1035128454163554855, guid: b8766f1471289d74bbcdc2f5ad979e8b, type: 3}
|
||||
propertyPath: m_Navigation.m_Mode
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2720195383270857804, guid: b8766f1471289d74bbcdc2f5ad979e8b, type: 3}
|
||||
propertyPath: m_text
|
||||
value: "\uBA54\uB274"
|
||||
@ -6328,6 +6336,7 @@ MonoBehaviour:
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
_enableBlockImage: 1
|
||||
_uiActionsInputBinding: {fileID: 11400000, guid: 8073fcaf56fc7c34e996d0d47044f146, type: 2}
|
||||
_checklistView: {fileID: 7075966153492927588}
|
||||
_inventoryView: {fileID: 3570087040626823091}
|
||||
_itemDetailView: {fileID: 7657801840785021781}
|
||||
@ -8348,6 +8357,10 @@ PrefabInstance:
|
||||
propertyPath: m_Interactable
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1035128454163554855, guid: b8766f1471289d74bbcdc2f5ad979e8b, type: 3}
|
||||
propertyPath: m_Navigation.m_Mode
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2720195383270857804, guid: b8766f1471289d74bbcdc2f5ad979e8b, type: 3}
|
||||
propertyPath: m_text
|
||||
value: "\uC810\uC6D0"
|
||||
|
BIN
Assets/_DDD/_Addressables/So/InputBindingSo/ConfirmUi_RestaurantUiActions_InputBindingSo.asset
(Stored with Git LFS)
BIN
Assets/_DDD/_Addressables/So/InputBindingSo/ConfirmUi_RestaurantUiActions_InputBindingSo.asset
(Stored with Git LFS)
Binary file not shown.
Binary file not shown.
BIN
Assets/_DDD/_Addressables/So/UiActionInputBinding/ConfirmUi_InputBindingSo.asset
(Stored with Git LFS)
Normal file
BIN
Assets/_DDD/_Addressables/So/UiActionInputBinding/ConfirmUi_InputBindingSo.asset
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Assets/_DDD/_Addressables/So/UiActionInputBinding/RestaurantManagementUi_InputBindingSo.asset
(Stored with Git LFS)
Normal file
BIN
Assets/_DDD/_Addressables/So/UiActionInputBinding/RestaurantManagementUi_InputBindingSo.asset
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -76,9 +76,9 @@ public static async Task<List<T>> LoadAssetsByLabel<T>(string label) where T : U
|
||||
return new List<T>();
|
||||
}
|
||||
|
||||
public static async Task<SceneInstance> LoadScene(AssetReference assetReference, LoadSceneMode mode = LoadSceneMode.Additive)
|
||||
public static async Task<SceneInstance> LoadScene(AssetReference assetReference, LoadSceneMode mode = LoadSceneMode.Additive, bool activateOnLoad = true)
|
||||
{
|
||||
var handle = Addressables.LoadSceneAsync(assetReference, mode);
|
||||
var handle = Addressables.LoadSceneAsync(assetReference, mode, activateOnLoad);
|
||||
await handle.Task;
|
||||
|
||||
if (handle.Status == AsyncOperationStatus.Succeeded)
|
||||
|
@ -203,6 +203,13 @@ public static void CreateAtlas(string path, string destPath)
|
||||
|
||||
if (objects.Count == 0) return;
|
||||
|
||||
// Validate destination path extension
|
||||
if (!destPath.EndsWith(ExtenstionConstants.SpriteAtlasExtenstionLower, System.StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Debug.LogWarning($"[SpriteAtlas] destPath must end with .spriteatlas : {destPath}");
|
||||
return;
|
||||
}
|
||||
|
||||
Utils.MakeFolderFromFilePath(destPath);
|
||||
var atlas = new SpriteAtlasAsset();
|
||||
|
||||
@ -216,9 +223,17 @@ public static void CreateAtlas(string path, string destPath)
|
||||
atlas.Add(objects.ToArray());
|
||||
|
||||
SpriteAtlasAsset.Save(atlas, destPath);
|
||||
AssetDatabase.Refresh();
|
||||
|
||||
var sai = (SpriteAtlasImporter)AssetImporter.GetAtPath(destPath);
|
||||
// Ensure importer is created/applied synchronously before accessing it
|
||||
AssetDatabase.ImportAsset(destPath, ImportAssetOptions.ForceUpdate | ImportAssetOptions.ForceSynchronousImport);
|
||||
|
||||
var sai = AssetImporter.GetAtPath(destPath) as SpriteAtlasImporter;
|
||||
if (sai == null)
|
||||
{
|
||||
Debug.LogWarning($"[SpriteAtlas] Importer not ready for '{destPath}'. Skipping settings this pass.");
|
||||
return;
|
||||
}
|
||||
|
||||
sai.packingSettings = new SpriteAtlasPackingSettings
|
||||
{
|
||||
enableRotation = false,
|
||||
@ -235,8 +250,11 @@ public static void CreateAtlas(string path, string destPath)
|
||||
generateMipMaps = false
|
||||
};
|
||||
|
||||
// 저장 후 설정 반영을 위해 동기 임포트, 그리고 즉시 패킹 수행
|
||||
// Persist settings and reimport synchronously to apply them
|
||||
AssetDatabase.WriteImportSettingsIfDirty(destPath);
|
||||
AssetDatabase.ImportAsset(destPath, ImportAssetOptions.ForceUpdate | ImportAssetOptions.ForceSynchronousImport);
|
||||
|
||||
// Finally, pack the atlas for the active build target
|
||||
var packedAtlas = AssetDatabase.LoadAssetAtPath<SpriteAtlas>(destPath);
|
||||
if (packedAtlas != null)
|
||||
{
|
||||
@ -253,10 +271,22 @@ public static void CreateSingleAtlas(string path, string destPath)
|
||||
AssetDatabase.DeleteAsset(destPath);
|
||||
}
|
||||
|
||||
// Validate destination path extension
|
||||
if (!destPath.EndsWith(ExtenstionConstants.SpriteAtlasExtenstionLower, System.StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Debug.LogWarning($"[SpriteAtlas] destPath must end with .spriteatlas : {destPath}");
|
||||
return;
|
||||
}
|
||||
|
||||
Utils.MakeFolderFromFilePath(destPath);
|
||||
var atlas = new SpriteAtlasAsset();
|
||||
|
||||
var sprite = AssetDatabase.LoadAssetAtPath<Sprite>(path);
|
||||
if (sprite == null)
|
||||
{
|
||||
Debug.LogWarning($"[SpriteAtlas] Source sprite not found at '{path}'. Skipping atlas: '{destPath}'");
|
||||
return;
|
||||
}
|
||||
atlas.Add(new Object[] { sprite });
|
||||
|
||||
var spriteAtlasComponents = new List<IPostProcessorSpriteAtlas>();
|
||||
@ -267,9 +297,17 @@ public static void CreateSingleAtlas(string path, string destPath)
|
||||
}
|
||||
|
||||
SpriteAtlasAsset.Save(atlas, destPath);
|
||||
AssetDatabase.Refresh();
|
||||
|
||||
var sai = (SpriteAtlasImporter)AssetImporter.GetAtPath(destPath);
|
||||
// Ensure importer is created/applied synchronously before accessing it
|
||||
AssetDatabase.ImportAsset(destPath, ImportAssetOptions.ForceUpdate | ImportAssetOptions.ForceSynchronousImport);
|
||||
|
||||
var sai = AssetImporter.GetAtPath(destPath) as SpriteAtlasImporter;
|
||||
if (sai == null)
|
||||
{
|
||||
Debug.LogWarning($"[SpriteAtlas] Importer not ready for '{destPath}'. Skipping settings this pass.");
|
||||
return;
|
||||
}
|
||||
|
||||
sai.packingSettings = new SpriteAtlasPackingSettings
|
||||
{
|
||||
enableRotation = false,
|
||||
@ -286,8 +324,11 @@ public static void CreateSingleAtlas(string path, string destPath)
|
||||
generateMipMaps = false
|
||||
};
|
||||
|
||||
// 저장 후 설정 반영을 위해 동기 임포트, 그리고 즉시 패킹 수행
|
||||
// Persist settings and reimport synchronously to apply them
|
||||
AssetDatabase.WriteImportSettingsIfDirty(destPath);
|
||||
AssetDatabase.ImportAsset(destPath, ImportAssetOptions.ForceUpdate | ImportAssetOptions.ForceSynchronousImport);
|
||||
|
||||
// Finally, pack the atlas for the active build target
|
||||
var packedAtlas = AssetDatabase.LoadAssetAtPath<SpriteAtlas>(destPath);
|
||||
if (packedAtlas != null)
|
||||
{
|
||||
|
@ -15,6 +15,17 @@ private void Awake()
|
||||
_cinemachineCamera = GetComponent<CinemachineCamera>();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
CameraManager.Instance.RegisterCamera(this);
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
var cameraManager = CameraManager.Instance;
|
||||
cameraManager?.UnRegisterCamera(this);
|
||||
}
|
||||
|
||||
public int GetPriority() => _cinemachineCamera.Priority;
|
||||
public void SetPriority(int newPriority) => _cinemachineCamera.Priority = newPriority;
|
||||
public void SetFollowTarget(Transform target) => _cinemachineCamera.Follow = target;
|
||||
|
@ -2,7 +2,6 @@
|
||||
using System.Threading.Tasks;
|
||||
using Sirenix.OdinInspector;
|
||||
using Unity.Cinemachine;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DDD
|
||||
{
|
||||
@ -44,12 +43,7 @@ public Task Init()
|
||||
|
||||
public void PostInit()
|
||||
{
|
||||
var cameraGameObjects = FindObjectsByType<CameraGameObject>(FindObjectsInactive.Include, FindObjectsSortMode.None);
|
||||
foreach (var cameraGameObject in cameraGameObjects)
|
||||
{
|
||||
RegisterCamera(cameraGameObject);
|
||||
}
|
||||
_initializationTask.SetResult(true);
|
||||
|
||||
}
|
||||
|
||||
public void RegisterCamera(CameraGameObject cameraGameObject)
|
||||
@ -72,16 +66,9 @@ public void SwitchCamera(CameraType cameraType, CinemachineBlendDefinition.Style
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<CameraGameObject> GetCameraGameObject(CameraType cameraType)
|
||||
public CameraGameObject GetCameraGameObject(CameraType cameraType)
|
||||
{
|
||||
await _initializationTask.Task;
|
||||
|
||||
if (_cameraGameObjects.TryGetValue(cameraType, out var cameraGameObject))
|
||||
{
|
||||
return cameraGameObject;
|
||||
}
|
||||
|
||||
return null;
|
||||
return _cameraGameObjects.GetValueOrDefault(cameraType);
|
||||
}
|
||||
}
|
||||
}
|
@ -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))
|
||||
{
|
||||
|
@ -69,6 +69,34 @@ public void PostInit()
|
||||
|
||||
}
|
||||
|
||||
public async Task PreloadAll()
|
||||
{
|
||||
var flowToSceneMapping = GameFlowManager.Instance.GameFlowSceneMappingSo.FlowToSceneMapping;
|
||||
|
||||
foreach (var flowAssetPair in flowToSceneMapping)
|
||||
{
|
||||
if (_loadedSceneDatas.ContainsKey(flowAssetPair.Key)) continue;
|
||||
|
||||
var runtimeKey = GetRuntimeKey(flowAssetPair.Value);
|
||||
if (_assetKeyToSceneData.TryGetValue(runtimeKey, out var existing))
|
||||
{
|
||||
_loadedSceneDatas[flowAssetPair.Key] = existing;
|
||||
continue;
|
||||
}
|
||||
|
||||
var instance = await AssetManager.LoadScene(flowAssetPair.Value, activateOnLoad:false);
|
||||
if (!instance.Scene.IsValid())
|
||||
{
|
||||
Debug.LogError($"[SceneManager] {flowAssetPair.Key}의 씬 로딩 실패");
|
||||
continue;
|
||||
}
|
||||
|
||||
var data = new SceneData(instance.Scene, instance);
|
||||
_loadedSceneDatas[flowAssetPair.Key] = data;
|
||||
_assetKeyToSceneData[runtimeKey] = data;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task PreloadScene(GameFlowState gameFlowState)
|
||||
{
|
||||
if (_loadedSceneDatas.ContainsKey(gameFlowState)) return;
|
||||
@ -93,41 +121,10 @@ public async Task PreloadScene(GameFlowState gameFlowState)
|
||||
return;
|
||||
}
|
||||
|
||||
DeactivateScene(loadedInstance.Scene);
|
||||
|
||||
var data = new SceneData(loadedInstance.Scene, loadedInstance);
|
||||
_loadedSceneDatas[gameFlowState] = data;
|
||||
_assetKeyToSceneData[runtimeKey] = data;
|
||||
}
|
||||
|
||||
public async Task PreloadAll()
|
||||
{
|
||||
var flowToSceneMapping = GameFlowManager.Instance.GameFlowSceneMappingSo.FlowToSceneMapping;
|
||||
|
||||
foreach (var flowAssetPair in flowToSceneMapping)
|
||||
{
|
||||
if (_loadedSceneDatas.ContainsKey(flowAssetPair.Key)) continue;
|
||||
|
||||
var runtimeKey = GetRuntimeKey(flowAssetPair.Value);
|
||||
if (_assetKeyToSceneData.TryGetValue(runtimeKey, out var existing))
|
||||
{
|
||||
_loadedSceneDatas[flowAssetPair.Key] = existing;
|
||||
continue;
|
||||
}
|
||||
|
||||
var instance = await AssetManager.LoadScene(flowAssetPair.Value);
|
||||
if (!instance.Scene.IsValid())
|
||||
{
|
||||
Debug.LogError($"[SceneManager] {flowAssetPair.Key}의 씬 로딩 실패");
|
||||
continue;
|
||||
}
|
||||
|
||||
DeactivateScene(instance.Scene);
|
||||
var data = new SceneData(instance.Scene, instance);
|
||||
_loadedSceneDatas[flowAssetPair.Key] = data;
|
||||
_assetKeyToSceneData[runtimeKey] = data;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ActivateScene(GameFlowState newFlowState)
|
||||
{
|
||||
@ -148,19 +145,10 @@ public async Task ActivateScene(GameFlowState newFlowState)
|
||||
{
|
||||
await handler.OnBeforeSceneActivate();
|
||||
}
|
||||
|
||||
foreach (var root in sceneData.Scene.GetRootGameObjects())
|
||||
|
||||
if (sceneData.SceneInstance.HasValue)
|
||||
{
|
||||
root.SetActive(true);
|
||||
}
|
||||
|
||||
if (sceneData.Scene.IsValid())
|
||||
{
|
||||
UnityEngine.SceneManagement.SceneManager.SetActiveScene(sceneData.Scene);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"[SceneManager] {newFlowState}의 Scene이 유효하지 않습니다.");
|
||||
await sceneData.SceneInstance.Value.ActivateAsync();
|
||||
}
|
||||
|
||||
foreach (var handler in _sceneTransitionHandlerSo.Handlers.Where(handler => handler != null))
|
||||
@ -169,22 +157,6 @@ public async Task ActivateScene(GameFlowState newFlowState)
|
||||
}
|
||||
}
|
||||
|
||||
public void DeactivateScene(GameFlowState gameFlowState)
|
||||
{
|
||||
if (_loadedSceneDatas.TryGetValue(gameFlowState, out var sceneData))
|
||||
{
|
||||
DeactivateScene(sceneData.Scene);
|
||||
}
|
||||
}
|
||||
|
||||
private void DeactivateScene(Scene scene)
|
||||
{
|
||||
foreach (var rootObject in scene.GetRootGameObjects())
|
||||
{
|
||||
rootObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task UnloadScene(GameFlowState gameFlowState)
|
||||
{
|
||||
if (_loadedSceneDatas.TryGetValue(gameFlowState, out var sceneData))
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
namespace DDD
|
||||
{
|
||||
public class GameState : ScriptableObject
|
||||
public class GameState : ScriptSingleton<GameState>
|
||||
{
|
||||
[SerializeField] private AssetReference _gameLevelState;
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
@ -20,30 +21,23 @@ public static IEnumerable<T> GetFlags<T>(this T input) where T : Enum
|
||||
|
||||
public abstract class PopupUi<T> : BasePopupUi where T : Enum
|
||||
{
|
||||
protected BaseUiActionsInputBindingSo<T> _baseUiActionsInputBindingSo;
|
||||
[SerializeField, Required] protected BaseUiActionsInputBinding<T> _uiActionsInputBinding;
|
||||
protected readonly List<(InputAction action, Action<InputAction.CallbackContext> handler)> _registeredHandlers = new();
|
||||
public override InputActionMaps InputActionMaps => _baseUiActionsInputBindingSo.InputActionMaps;
|
||||
public override InputActionMaps InputActionMaps => _uiActionsInputBinding.InputActionMaps;
|
||||
|
||||
private bool _isTopPopup => UiManager.Instance.PopupUiState.IsTopPopup(this);
|
||||
|
||||
private const string InputBindingSo = "InputBindingSo";
|
||||
|
||||
protected override async void TryRegister()
|
||||
protected override void TryRegister()
|
||||
{
|
||||
base.TryRegister();
|
||||
|
||||
UiManager.Instance?.PopupUiState?.RegisterPopupUI(this);
|
||||
|
||||
string addressableKey = $"{GetType().Name}_{typeof(T).Name}_{InputBindingSo}";
|
||||
_baseUiActionsInputBindingSo = await AssetManager.LoadAsset<BaseUiActionsInputBindingSo<T>>(addressableKey);
|
||||
|
||||
Debug.Assert(_baseUiActionsInputBindingSo != null, $"{GetType().Name} class InputBindingSo not found: {addressableKey}");
|
||||
UiManager.Instance.PopupUiState.RegisterPopupUI(this);
|
||||
|
||||
foreach (var actionEnum in _baseUiActionsInputBindingSo.BindingActions.GetFlags())
|
||||
foreach (var actionEnum in _uiActionsInputBinding.BindingActions.GetFlags())
|
||||
{
|
||||
if (actionEnum.Equals(default(T))) continue;
|
||||
|
||||
var inputAction = InputManager.Instance.GetAction(_baseUiActionsInputBindingSo.InputActionMaps, actionEnum.ToString());
|
||||
var inputAction = InputManager.Instance.GetAction(_uiActionsInputBinding.InputActionMaps, actionEnum.ToString());
|
||||
if (inputAction == null) continue;
|
||||
|
||||
var startedHandler = new Action<InputAction.CallbackContext>(context =>
|
||||
@ -96,7 +90,7 @@ public override void Open(OpenPopupUiEvent evt)
|
||||
|
||||
if (UiManager.Instance.PopupUiState.IsTopPopup(this))
|
||||
{
|
||||
InputManager.Instance.SwitchCurrentActionMap(_baseUiActionsInputBindingSo.InputActionMaps);
|
||||
InputManager.Instance.SwitchCurrentActionMap(_uiActionsInputBinding.InputActionMaps);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DDD
|
||||
{
|
||||
public class BaseUiActionsInputBinding<T> : ScriptableObject where T : Enum
|
||||
{
|
||||
public InputActionMaps InputActionMaps;
|
||||
|
||||
[EnumToggleButtons]
|
||||
public T BindingActions;
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
using System;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DDD
|
||||
{
|
||||
public class BaseUiActionsInputBindingSo<T> : ScriptableObject where T : Enum
|
||||
{
|
||||
public InputActionMaps InputActionMaps;
|
||||
|
||||
[EnumToggleButtons]
|
||||
public T BindingActions;
|
||||
|
||||
[ReadOnly, LabelText("Addressable Key")]
|
||||
public string AddressableKey => $"{typeof(T).Name}_InputBindingSo";
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace DDD
|
||||
{
|
||||
[CreateAssetMenu(fileName = "_UiActionsInputBinding", menuName = "Ui/RestaurantActions_InputBindingSo")]
|
||||
public class RestaurantActionsInputBinding : BaseUiActionsInputBinding<RestaurantActions> { }
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace DDD
|
||||
{
|
||||
[CreateAssetMenu(fileName = "_RestaurantActions_InputBindingSo", menuName = "Ui/RestaurantActions_InputBindingSo")]
|
||||
public class RestaurantActionsInputBindingSo : BaseUiActionsInputBindingSo<RestaurantActions> { }
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace DDD
|
||||
{
|
||||
[CreateAssetMenu(fileName = "_UiActionsInputBinding", menuName = "Ui/RestaurantUiActions_InputBindingSo")]
|
||||
public class RestaurantUiActionsInputBinding : BaseUiActionsInputBinding<RestaurantUiActions> { }
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace DDD
|
||||
{
|
||||
[CreateAssetMenu(fileName = "_RestaurantUiActions_InputBindingSo", menuName = "Ui/RestaurantUiActions_InputBindingSo")]
|
||||
public class RestaurantUiActionsInputBindingSo : BaseUiActionsInputBindingSo<RestaurantUiActions> { }
|
||||
}
|
@ -1,22 +1,20 @@
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace DDD
|
||||
{
|
||||
public class RestaurantPlayerCharacter : RestaurantCharacter
|
||||
{
|
||||
protected override void Awake()
|
||||
protected override async void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
_ = Initialize();
|
||||
PlayerManager.Instance.RegisterPlayer(gameObject);
|
||||
}
|
||||
|
||||
private async Task Initialize()
|
||||
protected override void Start()
|
||||
{
|
||||
PlayerManager.Instance.RegisterPlayer(gameObject);
|
||||
var cameraObject = await CameraManager.Instance.GetCameraGameObject(CameraType.RestaurantBaseCamera);
|
||||
cameraObject?.SetFollowAndLookAtTarget(transform);
|
||||
base.Start();
|
||||
|
||||
var cameraObject = CameraManager.Instance.GetCameraGameObject(CameraType.RestaurantBaseCamera);
|
||||
cameraObject.SetFollowAndLookAtTarget(transform);
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
return Task.CompletedTask;
|
||||
_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
|
@ -2,7 +2,8 @@
|
||||
"name": "DrawingPackageToolsEditor",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"GUID:f4059aaf6c60a4a58a177a2609feb769"
|
||||
"GUID:f4059aaf6c60a4a58a177a2609feb769",
|
||||
"GUID:de4e6084e6d474788bb8c799d6b461eb"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
|
@ -7,7 +7,8 @@
|
||||
"GUID:f4059aaf6c60a4a58a177a2609feb769",
|
||||
"GUID:de4e6084e6d474788bb8c799d6b461eb",
|
||||
"GUID:734d92eba21c94caba915361bd5ac177",
|
||||
"GUID:e0cd26848372d4e5c891c569017e11f1"
|
||||
"GUID:e0cd26848372d4e5c891c569017e11f1",
|
||||
"GUID:db11b4b5d7520bc479416b48c98206cb"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
|
@ -2,7 +2,8 @@
|
||||
"name": "AstarPackageToolsEditor",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"GUID:f4059aaf6c60a4a58a177a2609feb769"
|
||||
"GUID:f4059aaf6c60a4a58a177a2609feb769",
|
||||
"GUID:de4e6084e6d474788bb8c799d6b461eb"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
|
Loading…
Reference in New Issue
Block a user