ui 생성 로직 변경

This commit is contained in:
NTG 2025-08-21 16:25:27 +09:00
parent 0dc138ea2f
commit ac49d77573
52 changed files with 967 additions and 1333 deletions

View File

@ -134,4 +134,5 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 41f0ee0aabb2f954d918caa8d484f646, type: 3} m_Script: {fileID: 11500000, guid: 41f0ee0aabb2f954d918caa8d484f646, type: 3}
m_Name: m_Name:
m_EditorClassIdentifier: m_EditorClassIdentifier:
<UiType>k__BackingField: 4
_enableBlockImage: 1 _enableBlockImage: 1

View File

@ -319,5 +319,6 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 828648aab79941544bf8ceb7b25b586c, type: 3} m_Script: {fileID: 11500000, guid: 828648aab79941544bf8ceb7b25b586c, type: 3}
m_Name: m_Name:
m_EditorClassIdentifier: m_EditorClassIdentifier:
<UiType>k__BackingField: 4
_enableBlockImage: 0 _enableBlockImage: 0
_messageText: {fileID: 1263817835881307751} _messageText: {fileID: 1263817835881307751}

View File

@ -422,6 +422,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: eccb2d58803b65f4e82f22153315d3c6, type: 3} m_Script: {fileID: 11500000, guid: eccb2d58803b65f4e82f22153315d3c6, type: 3}
m_Name: m_Name:
m_EditorClassIdentifier: m_EditorClassIdentifier:
<UiType>k__BackingField: 2
_enableBlockImage: 0 _enableBlockImage: 0
_filledImage: {fileID: 1182510989530764005} _filledImage: {fileID: 1182510989530764005}
_textLabel: {fileID: 5874059589008679693} _textLabel: {fileID: 5874059589008679693}

View File

@ -651,7 +651,7 @@ PrefabInstance:
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 6952779389930089995, guid: 4f2bf029cb06b084ba41defc8fc76731, type: 3} - target: {fileID: 6952779389930089995, guid: 4f2bf029cb06b084ba41defc8fc76731, type: 3}
propertyPath: m_Name propertyPath: m_Name
value: Hud value: RestaurantHud
objectReference: {fileID: 0} objectReference: {fileID: 0}
m_RemovedComponents: [] m_RemovedComponents: []
m_RemovedGameObjects: m_RemovedGameObjects:
@ -663,8 +663,30 @@ PrefabInstance:
- targetCorrespondingSourceObject: {fileID: 8967231042952671610, guid: 4f2bf029cb06b084ba41defc8fc76731, type: 3} - targetCorrespondingSourceObject: {fileID: 8967231042952671610, guid: 4f2bf029cb06b084ba41defc8fc76731, type: 3}
insertIndex: -1 insertIndex: -1
addedObject: {fileID: 8229589654595845064} addedObject: {fileID: 8229589654595845064}
m_AddedComponents: [] m_AddedComponents:
- targetCorrespondingSourceObject: {fileID: 6952779389930089995, guid: 4f2bf029cb06b084ba41defc8fc76731, type: 3}
insertIndex: -1
addedObject: {fileID: 3263769952751147662}
m_SourcePrefab: {fileID: 100100000, guid: 4f2bf029cb06b084ba41defc8fc76731, type: 3} m_SourcePrefab: {fileID: 100100000, guid: 4f2bf029cb06b084ba41defc8fc76731, type: 3}
--- !u!1 &3080325846008693413 stripped
GameObject:
m_CorrespondingSourceObject: {fileID: 6952779389930089995, guid: 4f2bf029cb06b084ba41defc8fc76731, type: 3}
m_PrefabInstance: {fileID: 5387070474184109230}
m_PrefabAsset: {fileID: 0}
--- !u!114 &3263769952751147662
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3080325846008693413}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 7521841876f5b054aa4a0b0081ff8425, type: 3}
m_Name:
m_EditorClassIdentifier:
<UiType>k__BackingField: 1
_enableBlockImage: 0
--- !u!224 &3940853162783645140 stripped --- !u!224 &3940853162783645140 stripped
RectTransform: RectTransform:
m_CorrespondingSourceObject: {fileID: 8967231042952671610, guid: 4f2bf029cb06b084ba41defc8fc76731, type: 3} m_CorrespondingSourceObject: {fileID: 8967231042952671610, guid: 4f2bf029cb06b084ba41defc8fc76731, type: 3}

View File

@ -811,7 +811,9 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 7b3eb65dc42aeb84eba2397a9603e94d, type: 3} m_Script: {fileID: 11500000, guid: 7b3eb65dc42aeb84eba2397a9603e94d, type: 3}
m_Name: m_Name:
m_EditorClassIdentifier: m_EditorClassIdentifier:
<UiType>k__BackingField: 3
_enableBlockImage: 1 _enableBlockImage: 1
InputActionMaps: 3
_uiActionsInputBinding: {fileID: 11400000, guid: 99d3d87bd43df65488e757c43a308f36, type: 2} _uiActionsInputBinding: {fileID: 11400000, guid: 99d3d87bd43df65488e757c43a308f36, type: 2}
_messageLabel: {fileID: 3495127426411772216} _messageLabel: {fileID: 3495127426411772216}
_messageLabelLocalizeStringEvent: {fileID: 7334955628972040157} _messageLabelLocalizeStringEvent: {fileID: 7334955628972040157}

View File

@ -6338,6 +6338,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 46c8396c996c804449b383960b44e812, type: 3} m_Script: {fileID: 11500000, guid: 46c8396c996c804449b383960b44e812, type: 3}
m_Name: m_Name:
m_EditorClassIdentifier: m_EditorClassIdentifier:
<UiType>k__BackingField: 3
_enableBlockImage: 0 _enableBlockImage: 0
InputActionMaps: 3 InputActionMaps: 3
_uiActionsInputBinding: {fileID: 11400000, guid: 8073fcaf56fc7c34e996d0d47044f146, type: 2} _uiActionsInputBinding: {fileID: 11400000, guid: 8073fcaf56fc7c34e996d0d47044f146, type: 2}
@ -6362,6 +6363,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 80dee5e1862248aab26236036049e5fc, type: 3} m_Script: {fileID: 11500000, guid: 80dee5e1862248aab26236036049e5fc, type: 3}
m_Name: m_Name:
m_EditorClassIdentifier: m_EditorClassIdentifier:
_holdCompleteTime: 0
--- !u!1001 &4530765275021007961 --- !u!1001 &4530765275021007961
PrefabInstance: PrefabInstance:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0

Binary file not shown.

BIN
Assets/_DDD/_Addressables/So/GameData/UiData.asset (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: dd182535820ec034b9d5a0315f93fa26 guid: d6d7638e05d740944a77a01f60b331c4
NativeFormatImporter: NativeFormatImporter:
externalObjects: {} externalObjects: {}
mainObjectFileID: 11400000 mainObjectFileID: 11400000

Binary file not shown.

View File

@ -10,7 +10,7 @@ GameObject:
m_Component: m_Component:
- component: {fileID: 4347279445921954555} - component: {fileID: 4347279445921954555}
m_Layer: 5 m_Layer: 5
m_Name: PopupUis m_Name: PopupUiRoot
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
m_NavMeshLayer: 0 m_NavMeshLayer: 0
@ -38,6 +38,115 @@ RectTransform:
m_AnchoredPosition: {x: 0, y: 0} m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0} m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5} m_Pivot: {x: 0.5, y: 0.5}
--- !u!1 &1111901944139047714
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 6076800063500988294}
m_Layer: 5
m_Name: HudRoot
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &6076800063500988294
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1111901944139047714}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 1
m_Children:
- {fileID: 7916164406893547064}
m_Father: {fileID: 5760169274063006291}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!1 &4130064528218324885
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 6174235207617529232}
m_Layer: 5
m_Name: CommonUiRoot
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &6174235207617529232
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4130064528218324885}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 1
m_Children:
- {fileID: 4191602006120035298}
- {fileID: 4862733150375458244}
m_Father: {fileID: 5760169274063006291}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!1 &4851448928365776117
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 3090446084811068791}
m_Layer: 5
m_Name: InteractionUiRoot
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &3090446084811068791
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4851448928365776117}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 1
m_Children:
- {fileID: 496411955364970508}
m_Father: {fileID: 5760169274063006291}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!1 &6838253471355869082 --- !u!1 &6838253471355869082
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@ -73,11 +182,10 @@ RectTransform:
m_LocalScale: {x: 0, y: 0, z: 0} m_LocalScale: {x: 0, y: 0, z: 0}
m_ConstrainProportionsScale: 1 m_ConstrainProportionsScale: 1
m_Children: m_Children:
- {fileID: 7916164406893547064} - {fileID: 6076800063500988294}
- {fileID: 496411955364970508} - {fileID: 3090446084811068791}
- {fileID: 4347279445921954555} - {fileID: 4347279445921954555}
- {fileID: 4191602006120035298} - {fileID: 6174235207617529232}
- {fileID: 4862733150375458244}
m_Father: {fileID: 0} m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0} m_AnchorMin: {x: 0, y: 0}
@ -219,13 +327,10 @@ MonoBehaviour:
m_Name: m_Name:
m_EditorClassIdentifier: m_EditorClassIdentifier:
_persistent: 1 _persistent: 1
_popupUiState: _hudRoot: {fileID: 6076800063500988294}
m_AssetGUID: dd182535820ec034b9d5a0315f93fa26 _interactionUiRoot: {fileID: 3090446084811068791}
m_SubObjectName:
m_SubObjectType:
m_SubObjectGUID:
m_EditorAssetChanged: 1
_popupUiRoot: {fileID: 4347279445921954555} _popupUiRoot: {fileID: 4347279445921954555}
_commonUiRoot: {fileID: 6174235207617529232}
--- !u!1001 &291049386471777514 --- !u!1001 &291049386471777514
PrefabInstance: PrefabInstance:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@ -274,7 +379,7 @@ PrefabInstance:
serializedVersion: 2 serializedVersion: 2
m_Modification: m_Modification:
serializedVersion: 3 serializedVersion: 3
m_TransformParent: {fileID: 5760169274063006291} m_TransformParent: {fileID: 6076800063500988294}
m_Modifications: m_Modifications:
- target: {fileID: 1353387648420519096, guid: 86a58b93c36851e4787861c23023b094, type: 3} - target: {fileID: 1353387648420519096, guid: 86a58b93c36851e4787861c23023b094, type: 3}
propertyPath: m_AnchorMax.y propertyPath: m_AnchorMax.y
@ -302,7 +407,7 @@ PrefabInstance:
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 3080325846008693413, guid: 86a58b93c36851e4787861c23023b094, type: 3} - target: {fileID: 3080325846008693413, guid: 86a58b93c36851e4787861c23023b094, type: 3}
propertyPath: m_Name propertyPath: m_Name
value: Hud value: RestaurantHud
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 4664972416674662382, guid: 86a58b93c36851e4787861c23023b094, type: 3} - target: {fileID: 4664972416674662382, guid: 86a58b93c36851e4787861c23023b094, type: 3}
propertyPath: m_Pivot.x propertyPath: m_Pivot.x
@ -354,15 +459,15 @@ PrefabInstance:
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 4664972416674662382, guid: 86a58b93c36851e4787861c23023b094, type: 3} - target: {fileID: 4664972416674662382, guid: 86a58b93c36851e4787861c23023b094, type: 3}
propertyPath: m_LocalRotation.x propertyPath: m_LocalRotation.x
value: 0 value: -0
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 4664972416674662382, guid: 86a58b93c36851e4787861c23023b094, type: 3} - target: {fileID: 4664972416674662382, guid: 86a58b93c36851e4787861c23023b094, type: 3}
propertyPath: m_LocalRotation.y propertyPath: m_LocalRotation.y
value: 0 value: -0
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 4664972416674662382, guid: 86a58b93c36851e4787861c23023b094, type: 3} - target: {fileID: 4664972416674662382, guid: 86a58b93c36851e4787861c23023b094, type: 3}
propertyPath: m_LocalRotation.z propertyPath: m_LocalRotation.z
value: 0 value: -0
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 4664972416674662382, guid: 86a58b93c36851e4787861c23023b094, type: 3} - target: {fileID: 4664972416674662382, guid: 86a58b93c36851e4787861c23023b094, type: 3}
propertyPath: m_AnchoredPosition.x propertyPath: m_AnchoredPosition.x
@ -1811,7 +1916,7 @@ PrefabInstance:
serializedVersion: 2 serializedVersion: 2
m_Modification: m_Modification:
serializedVersion: 3 serializedVersion: 3
m_TransformParent: {fileID: 5760169274063006291} m_TransformParent: {fileID: 6174235207617529232}
m_Modifications: m_Modifications:
- target: {fileID: 1926278333613504521, guid: f84d9014b084dbf46b1c4d44fe5b63c3, type: 3} - target: {fileID: 1926278333613504521, guid: f84d9014b084dbf46b1c4d44fe5b63c3, type: 3}
propertyPath: m_Name propertyPath: m_Name
@ -1867,15 +1972,15 @@ PrefabInstance:
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 8122602890985372994, guid: f84d9014b084dbf46b1c4d44fe5b63c3, type: 3} - target: {fileID: 8122602890985372994, guid: f84d9014b084dbf46b1c4d44fe5b63c3, type: 3}
propertyPath: m_LocalRotation.x propertyPath: m_LocalRotation.x
value: 0 value: -0
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 8122602890985372994, guid: f84d9014b084dbf46b1c4d44fe5b63c3, type: 3} - target: {fileID: 8122602890985372994, guid: f84d9014b084dbf46b1c4d44fe5b63c3, type: 3}
propertyPath: m_LocalRotation.y propertyPath: m_LocalRotation.y
value: 0 value: -0
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 8122602890985372994, guid: f84d9014b084dbf46b1c4d44fe5b63c3, type: 3} - target: {fileID: 8122602890985372994, guid: f84d9014b084dbf46b1c4d44fe5b63c3, type: 3}
propertyPath: m_LocalRotation.z propertyPath: m_LocalRotation.z
value: 0 value: -0
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 8122602890985372994, guid: f84d9014b084dbf46b1c4d44fe5b63c3, type: 3} - target: {fileID: 8122602890985372994, guid: f84d9014b084dbf46b1c4d44fe5b63c3, type: 3}
propertyPath: m_AnchoredPosition.x propertyPath: m_AnchoredPosition.x
@ -2079,7 +2184,7 @@ PrefabInstance:
serializedVersion: 2 serializedVersion: 2
m_Modification: m_Modification:
serializedVersion: 3 serializedVersion: 3
m_TransformParent: {fileID: 5760169274063006291} m_TransformParent: {fileID: 6174235207617529232}
m_Modifications: m_Modifications:
- target: {fileID: 2726783183655360567, guid: 085fe4e2983c9ed4780abfb56bf5c322, type: 3} - target: {fileID: 2726783183655360567, guid: 085fe4e2983c9ed4780abfb56bf5c322, type: 3}
propertyPath: m_Pivot.x propertyPath: m_Pivot.x
@ -2131,15 +2236,15 @@ PrefabInstance:
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 2726783183655360567, guid: 085fe4e2983c9ed4780abfb56bf5c322, type: 3} - target: {fileID: 2726783183655360567, guid: 085fe4e2983c9ed4780abfb56bf5c322, type: 3}
propertyPath: m_LocalRotation.x propertyPath: m_LocalRotation.x
value: 0 value: -0
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 2726783183655360567, guid: 085fe4e2983c9ed4780abfb56bf5c322, type: 3} - target: {fileID: 2726783183655360567, guid: 085fe4e2983c9ed4780abfb56bf5c322, type: 3}
propertyPath: m_LocalRotation.y propertyPath: m_LocalRotation.y
value: 0 value: -0
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 2726783183655360567, guid: 085fe4e2983c9ed4780abfb56bf5c322, type: 3} - target: {fileID: 2726783183655360567, guid: 085fe4e2983c9ed4780abfb56bf5c322, type: 3}
propertyPath: m_LocalRotation.z propertyPath: m_LocalRotation.z
value: 0 value: -0
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 2726783183655360567, guid: 085fe4e2983c9ed4780abfb56bf5c322, type: 3} - target: {fileID: 2726783183655360567, guid: 085fe4e2983c9ed4780abfb56bf5c322, type: 3}
propertyPath: m_AnchoredPosition.x propertyPath: m_AnchoredPosition.x
@ -2181,7 +2286,7 @@ PrefabInstance:
serializedVersion: 2 serializedVersion: 2
m_Modification: m_Modification:
serializedVersion: 3 serializedVersion: 3
m_TransformParent: {fileID: 5760169274063006291} m_TransformParent: {fileID: 3090446084811068791}
m_Modifications: m_Modifications:
- target: {fileID: 507999590019405259, guid: 67b28d928cd16794eba49dade35d395d, type: 3} - target: {fileID: 507999590019405259, guid: 67b28d928cd16794eba49dade35d395d, type: 3}
propertyPath: m_Name propertyPath: m_Name
@ -2313,15 +2418,15 @@ PrefabInstance:
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 7858233056384553088, guid: 67b28d928cd16794eba49dade35d395d, type: 3} - target: {fileID: 7858233056384553088, guid: 67b28d928cd16794eba49dade35d395d, type: 3}
propertyPath: m_LocalRotation.x propertyPath: m_LocalRotation.x
value: 0 value: -0
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 7858233056384553088, guid: 67b28d928cd16794eba49dade35d395d, type: 3} - target: {fileID: 7858233056384553088, guid: 67b28d928cd16794eba49dade35d395d, type: 3}
propertyPath: m_LocalRotation.y propertyPath: m_LocalRotation.y
value: 0 value: -0
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 7858233056384553088, guid: 67b28d928cd16794eba49dade35d395d, type: 3} - target: {fileID: 7858233056384553088, guid: 67b28d928cd16794eba49dade35d395d, type: 3}
propertyPath: m_LocalRotation.z propertyPath: m_LocalRotation.z
value: 0 value: -0
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 7858233056384553088, guid: 67b28d928cd16794eba49dade35d395d, type: 3} - target: {fileID: 7858233056384553088, guid: 67b28d928cd16794eba49dade35d395d, type: 3}
propertyPath: m_AnchoredPosition.x propertyPath: m_AnchoredPosition.x

Binary file not shown.

View File

@ -8,8 +8,10 @@ namespace DDD
public class GameData : ScriptSingleton<GameData> public class GameData : ScriptSingleton<GameData>
{ {
[SerializeField] private AssetReference _gameLocalizationData; [SerializeField] private AssetReference _gameLocalizationData;
[SerializeField] private AssetReference _uiData;
public GameLocalizationData LocalizationData { get; private set; } public GameLocalizationData LocalizationData { get; private set; }
public UiData UiData { get; private set; }
private bool _isLoaded; private bool _isLoaded;
@ -21,10 +23,16 @@ public async Task LoadData()
} }
var gameLocalizationDataHandle = _gameLocalizationData.LoadAssetAsync<GameLocalizationData>(); var gameLocalizationDataHandle = _gameLocalizationData.LoadAssetAsync<GameLocalizationData>();
var popupUiDataHandle = _uiData.LoadAssetAsync<UiData>();
await gameLocalizationDataHandle.Task; await gameLocalizationDataHandle.Task;
await popupUiDataHandle.Task;
LocalizationData = gameLocalizationDataHandle.Result; LocalizationData = gameLocalizationDataHandle.Result;
UiData = popupUiDataHandle.Result;
Debug.Assert(LocalizationData != null, "GameLocalizationData is null"); Debug.Assert(LocalizationData != null, "GameLocalizationData is null");
Debug.Assert(UiData != null, "UiData is null");
_isLoaded = true; _isLoaded = true;
} }

View File

@ -0,0 +1,12 @@
using System.Collections.Generic;
using Sirenix.OdinInspector;
using UnityEngine;
namespace DDD
{
[CreateAssetMenu(fileName = "UiData", menuName = "GameData/UiData")]
public class UiData : SerializedScriptableObject
{
public Dictionary<GameFlowState, List<BaseUi>> FlowToUiMapping = new();
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a0663996283946b0a51684c1047c24a1
timeCreated: 1755751458

View File

@ -28,7 +28,7 @@ public void PreInit()
public Task Init() public Task Init()
{ {
return Task.CompletedTask; ; return Task.CompletedTask;
} }
public async void PostInit() public async void PostInit()

View File

@ -1,17 +1,14 @@
using UnityEngine;
using UnityEngine.AddressableAssets;
namespace DDD namespace DDD
{ {
public class GameState : ScriptSingleton<GameState> public class GameState : ScriptSingleton<GameState>
{ {
[SerializeField] private AssetReference _gameLevelState;
public GameLevelState LevelState { get; private set; } public GameLevelState LevelState { get; private set; }
public UiState UiState { get; private set; }
private void OnEnable() private void OnEnable()
{ {
LevelState = CreateInstance<GameLevelState>(); LevelState = CreateInstance<GameLevelState>();
UiState = CreateInstance<UiState>();
} }
} }
} }

View File

@ -2,14 +2,24 @@
using System.ComponentModel; using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using TMPro;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using DDD.MVVM;
namespace DDD namespace DDD
{ {
public enum UiType
{
None = 0,
Hud,
Interaction,
Popup,
Common
}
public abstract class BaseUi : MonoBehaviour public abstract class BaseUi : MonoBehaviour
{ {
[field: SerializeField] public UiType UiType { get; set; }
[SerializeField] protected bool _enableBlockImage; [SerializeField] protected bool _enableBlockImage;
protected CanvasGroup _canvasGroup; protected CanvasGroup _canvasGroup;
@ -27,7 +37,6 @@ protected virtual void Awake()
_blockImage = transform.Find(CommonConstants.BlockImage)?.gameObject; _blockImage = transform.Find(CommonConstants.BlockImage)?.gameObject;
_bindingContext = new BindingContext(); _bindingContext = new BindingContext();
SetupAutoBindings();
SetupBindings(); SetupBindings();
} }
@ -38,7 +47,6 @@ protected virtual void OnEnable()
protected virtual void Start() protected virtual void Start()
{ {
TryRegister();
ClosePanel(); ClosePanel();
} }
@ -58,8 +66,20 @@ protected virtual void OnDestroy()
_bindingContext?.Dispose(); _bindingContext?.Dispose();
} }
protected virtual void TryRegister() { } public virtual void CreateInitialize()
protected virtual void TryUnregister() { } {
TryRegister();
}
protected virtual void TryRegister()
{
UiManager.Instance.UiState.RegisterUI(this);
}
protected virtual void TryUnregister()
{
UiManager.Instance.UiState.UnregisterUI(this);
}
// BaseUi 메서드들을 직접 구현 // BaseUi 메서드들을 직접 구현
public virtual void OpenPanel() public virtual void OpenPanel()
@ -93,74 +113,6 @@ public virtual void SetUiInteractable(bool active)
public bool IsOpenPanel() => _panel && _panel.activeInHierarchy; public bool IsOpenPanel() => _panel && _panel.activeInHierarchy;
/// <summary>
/// Attribute 기반 자동 바인딩 설정
/// </summary>
private void SetupAutoBindings()
{
var fields = GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public)
.Where(f => f.GetCustomAttribute<BindToAttribute>() != null);
foreach (var field in fields)
{
var bindAttribute = field.GetCustomAttribute<BindToAttribute>();
SetupBinding(field, bindAttribute);
}
// 컬렉션 바인딩 설정
var collectionFields = GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public)
.Where(f => f.GetCustomAttribute<BindCollectionAttribute>() != null);
foreach (var field in collectionFields)
{
var bindAttribute = field.GetCustomAttribute<BindCollectionAttribute>();
SetupCollectionBinding(field, bindAttribute);
}
}
/// <summary>
/// 개별 필드의 바인딩 설정
/// </summary>
private void SetupBinding(FieldInfo field, BindToAttribute bindAttribute)
{
var target = field.GetValue(this);
IValueConverter converter = null;
if (bindAttribute.ConverterType != null)
{
converter = Activator.CreateInstance(bindAttribute.ConverterType) as IValueConverter;
}
// UI 컴포넌트 타입별 바인딩 타겟 생성
IBindingTarget bindingTarget = target switch
{
Text text => new TextBindingTarget(text, bindAttribute.PropertyPath),
Image image => new ImageBindingTarget(image, bindAttribute.PropertyPath),
GameObject go => new ActiveBindingTarget(go, bindAttribute.PropertyPath),
Slider slider => new SliderBindingTarget(slider, bindAttribute.PropertyPath),
_ => null
};
if (bindingTarget != null)
{
_bindingContext.Bind(bindAttribute.PropertyPath, bindingTarget, converter);
}
}
/// <summary>
/// 컬렉션 바인딩 설정
/// </summary>
private void SetupCollectionBinding(FieldInfo field, BindCollectionAttribute bindAttribute)
{
var target = field.GetValue(this);
if (target is Transform parent)
{
// 컬렉션 바인딩 로직 (필요시 확장)
Debug.Log($"Collection binding for {bindAttribute.PropertyPath} is set up on {parent.name}");
}
}
/// <summary> /// <summary>
/// 추가 바인딩 설정 - 하위 클래스에서 구현 /// 추가 바인딩 설정 - 하위 클래스에서 구현
/// </summary> /// </summary>
@ -181,30 +133,5 @@ protected virtual void HandleCustomPropertyChanged(string propertyName)
{ {
// 하위 클래스에서 구현 // 하위 클래스에서 구현
} }
// 수동 바인딩 헬퍼 메서드들
protected void BindText(Text text, string propertyPath, IValueConverter converter = null)
{
var target = new TextBindingTarget(text, propertyPath);
_bindingContext?.Bind(propertyPath, target, converter);
}
protected void BindImage(Image image, string propertyPath, IValueConverter converter = null)
{
var target = new ImageBindingTarget(image, propertyPath);
_bindingContext?.Bind(propertyPath, target, converter);
}
protected void BindActive(GameObject gameObject, string propertyPath, IValueConverter converter = null)
{
var target = new ActiveBindingTarget(gameObject, propertyPath);
_bindingContext?.Bind(propertyPath, target, converter);
}
protected void BindSlider(Slider slider, string propertyPath, IValueConverter converter = null)
{
var target = new SliderBindingTarget(slider, propertyPath);
_bindingContext?.Bind(propertyPath, target, converter);
}
} }
} }

View File

@ -1,6 +1,5 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using DG.Tweening; using DG.Tweening;
using UnityEngine;
namespace DDD namespace DDD
{ {
@ -15,6 +14,8 @@ protected override void Awake()
protected override void TryRegister() protected override void TryRegister()
{ {
base.TryRegister();
EventBus.Register<FadeInEvent>(this); EventBus.Register<FadeInEvent>(this);
EventBus.Register<FadeOutEvent>(this); EventBus.Register<FadeOutEvent>(this);
} }

View File

@ -20,7 +20,7 @@ protected override void Awake()
_canvasGroup.alpha = 0; _canvasGroup.alpha = 0;
_messageText.text = null; _messageText.text = null;
} }
protected override void TryRegister() protected override void TryRegister()
{ {
base.TryRegister(); base.TryRegister();

View File

@ -24,7 +24,7 @@ protected override void Awake()
_filledImage.fillAmount = 0f; _filledImage.fillAmount = 0f;
} }
protected override void TryRegister() protected override void TryRegister()
{ {
base.TryRegister(); base.TryRegister();

View File

@ -1,7 +1,7 @@
using System.Linq; using System.Linq;
using UnityEngine; using UnityEngine;
namespace DDD.MVVM namespace DDD
{ {
/// <summary> /// <summary>
/// 불린 값을 반전시키는 컨버터 /// 불린 값을 반전시키는 컨버터

View File

@ -1,4 +1,4 @@
namespace DDD.MVVM namespace DDD
{ {
/// <summary> /// <summary>
/// 값 변환기 인터페이스 /// 값 변환기 인터페이스

View File

@ -1,5 +1,4 @@
using System.Linq; using System.Linq;
using DDD.MVVM;
using UnityEngine; using UnityEngine;
namespace DDD namespace DDD
@ -11,41 +10,31 @@ namespace DDD
public class RestaurantManagementViewModel : SimpleViewModel, IEventHandler<TodayMenuRemovedEvent> public class RestaurantManagementViewModel : SimpleViewModel, IEventHandler<TodayMenuRemovedEvent>
{ {
// 홀드 진행 상태 관리 // 홀드 진행 상태 관리
[SerializeField] private float _holdCompleteTime = 1f;
private bool _isHolding; private bool _isHolding;
private float _elapsedTime;
private float _holdCompleteTime = 1f; private float _holdProgress;
public float HoldProgress
{
get => _holdProgress;
private set
{
if (SetField(ref _holdProgress, value))
{
OnPropertyChanged(nameof(NormalizedHoldProgress));
}
}
}
/// <summary>
/// 홀드 진행률을 0~1 범위로 변환한 값
/// </summary>
public float NormalizedHoldProgress => _holdCompleteTime <= 0f ? 0f : Mathf.Clamp01(_holdProgress / _holdCompleteTime);
// 탭 상태 관리 // 탭 상태 관리
private SectionButtonType _currentSection = SectionButtonType.Menu; private SectionButtonType _currentSection = SectionButtonType.Menu;
private InventoryCategoryType _currentCategory = InventoryCategoryType.Food; private InventoryCategoryType _currentCategory = InventoryCategoryType.Food;
/// <summary>
/// 현재 홀드 상태
/// </summary>
public bool IsHolding
{
get => _isHolding;
private set => SetField(ref _isHolding, value);
}
/// <summary>
/// 홀드 진행 시간 (0.0 ~ 1.0)
/// </summary>
public float HoldProgress
{
get => _elapsedTime;
private set => SetField(ref _elapsedTime, value);
}
/// <summary>
/// 홀드 완료에 필요한 시간
/// </summary>
public float HoldCompleteTime
{
get => _holdCompleteTime;
set => SetField(ref _holdCompleteTime, value);
}
/// <summary> /// <summary>
/// 현재 선택된 섹션 /// 현재 선택된 섹션
/// </summary> /// </summary>
@ -67,13 +56,7 @@ public InventoryCategoryType CurrentCategory
/// <summary> /// <summary>
/// 배치 완료 가능 여부 (체크리스트 완료 상태) /// 배치 완료 가능 여부 (체크리스트 완료 상태)
/// </summary> /// </summary>
public bool CanCompleteBatch => public bool CanCompleteBatch => RestaurantState.Instance.ManagementState.GetChecklistStates().All(state => state);
RestaurantState.Instance.ManagementState.GetChecklistStates().All(state => state);
/// <summary>
/// 홀드 진행률을 0~1 범위로 변환한 값
/// </summary>
public float NormalizedHoldProgress => HoldCompleteTime <= 0f ? 1f : Mathf.Clamp01(HoldProgress / HoldCompleteTime);
public override void Initialize() public override void Initialize()
{ {
@ -103,9 +86,9 @@ private void UnregisterEvents()
/// </summary> /// </summary>
public void UpdateHoldProgress() public void UpdateHoldProgress()
{ {
if (!IsHolding) return; if (_isHolding == false) return;
if (HoldCompleteTime <= 0f) if (_holdCompleteTime <= 0f)
{ {
ProcessCompleteBatch(); ProcessCompleteBatch();
return; return;
@ -114,13 +97,10 @@ public void UpdateHoldProgress()
var deltaTime = Time.deltaTime; var deltaTime = Time.deltaTime;
HoldProgress += deltaTime; HoldProgress += deltaTime;
if (HoldProgress >= HoldCompleteTime) if (HoldProgress >= _holdCompleteTime)
{ {
ProcessCompleteBatch(); ProcessCompleteBatch();
} }
// UI 업데이트를 위한 정규화된 진행률 알림
OnPropertyChanged(nameof(NormalizedHoldProgress));
} }
/// <summary> /// <summary>
@ -128,9 +108,8 @@ public void UpdateHoldProgress()
/// </summary> /// </summary>
public void StartHold() public void StartHold()
{ {
IsHolding = true; _isHolding = true;
HoldProgress = 0f; HoldProgress = 0f;
OnPropertyChanged(nameof(NormalizedHoldProgress));
} }
/// <summary> /// <summary>
@ -143,9 +122,8 @@ public void CancelHold()
private void ResetHoldState() private void ResetHoldState()
{ {
IsHolding = false; _isHolding = false;
HoldProgress = 0f; HoldProgress = 0f;
OnPropertyChanged(nameof(NormalizedHoldProgress));
} }
/// <summary> /// <summary>

View File

@ -1,4 +1,4 @@
namespace DDD.MVVM namespace DDD
{ {
/// <summary> /// <summary>
/// 서비스 계층의 기본 인터페이스 /// 서비스 계층의 기본 인터페이스

View File

@ -2,7 +2,7 @@
using System.Linq; using System.Linq;
using UnityEngine; using UnityEngine;
namespace DDD.MVVM namespace DDD
{ {
/// <summary> /// <summary>
/// 인벤토리 관련 비즈니스 로직을 담당하는 서비스 /// 인벤토리 관련 비즈니스 로직을 담당하는 서비스

View File

@ -1,114 +0,0 @@
using System;
namespace DDD.MVVM
{
/// <summary>
/// UI 요소를 ViewModel 속성에 바인딩하기 위한 Attribute
/// Inspector에서 바인딩 정보를 시각적으로 확인할 수 있도록 지원
/// </summary>
[System.AttributeUsage(System.AttributeTargets.Field)]
public class BindToAttribute : System.Attribute
{
/// <summary>
/// 바인딩할 ViewModel 속성의 경로 (nameof 사용 권장)
/// </summary>
public string PropertyPath { get; }
/// <summary>
/// 값 변환기 타입 (선택사항)
/// </summary>
public System.Type ConverterType { get; }
/// <summary>
/// 바인딩 Attribute 생성자
/// </summary>
/// <param name="propertyPath">바인딩할 속성 경로 (nameof 사용 권장)</param>
/// <param name="converterType">값 변환기 타입 (선택사항)</param>
public BindToAttribute(string propertyPath, System.Type converterType = null)
{
PropertyPath = propertyPath;
ConverterType = converterType;
}
}
// /// <summary>
// /// 타입 안전한 바인딩 Attribute (제네릭 버전)
// /// 특정 ViewModel 타입에 대한 바인딩을 명시적으로 지정
// /// </summary>
// /// <typeparam name="TViewModel">바인딩할 ViewModel 타입</typeparam>
// [System.AttributeUsage(System.AttributeTargets.Field)]
// public class BindToAttribute<TViewModel> : System.Attribute where TViewModel : class
// {
// /// <summary>
// /// 바인딩할 ViewModel 속성의 경로
// /// </summary>
// public string PropertyPath { get; }
//
// /// <summary>
// /// 값 변환기 타입 (선택사항)
// /// </summary>
// public System.Type ConverterType { get; }
//
// /// <summary>
// /// 타입 안전한 바인딩 Attribute 생성자
// /// </summary>
// /// <param name="propertyPath">바인딩할 속성 경로</param>
// /// <param name="converterType">값 변환기 타입 (선택사항)</param>
// public BindToAttribute(string propertyPath, System.Type converterType = null)
// {
// PropertyPath = propertyPath;
// ConverterType = converterType;
// }
// }
/// <summary>
/// 컬렉션 바인딩을 위한 Attribute
/// 동적으로 생성되는 UI 요소들을 컬렉션에 바인딩
/// </summary>
[System.AttributeUsage(System.AttributeTargets.Field)]
public class BindCollectionAttribute : System.Attribute
{
/// <summary>
/// 바인딩할 컬렉션 속성의 경로
/// </summary>
public string PropertyPath { get; }
/// <summary>
/// 아이템 프리팹의 필드명 또는 속성명
/// </summary>
public string ItemPrefabReference { get; }
/// <summary>
/// 컬렉션 바인딩 Attribute 생성자
/// </summary>
/// <param name="propertyPath">바인딩할 컬렉션 속성 경로</param>
/// <param name="itemPrefabReference">아이템 프리팹 참조</param>
public BindCollectionAttribute(string propertyPath, string itemPrefabReference = null)
{
PropertyPath = propertyPath;
ItemPrefabReference = itemPrefabReference;
}
}
/// <summary>
/// 커맨드 바인딩을 위한 Attribute
/// 버튼 클릭 등의 이벤트를 ViewModel 메서드에 바인딩
/// </summary>
[System.AttributeUsage(System.AttributeTargets.Field)]
public class BindCommandAttribute : System.Attribute
{
/// <summary>
/// 바인딩할 ViewModel 메서드 이름
/// </summary>
public string MethodName { get; }
/// <summary>
/// 커맨드 바인딩 Attribute 생성자
/// </summary>
/// <param name="methodName">바인딩할 메서드 이름</param>
public BindCommandAttribute(string methodName)
{
MethodName = methodName;
}
}
}

View File

@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 279b3238907a3564f842594af646eab7

View File

@ -2,113 +2,12 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Reflection; using System.Reflection;
using TMPro;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
namespace DDD.MVVM namespace DDD
{ {
/// <summary>
/// 바인딩 타겟 인터페이스
/// UI 요소와 ViewModel 속성을 연결하는 역할
/// </summary>
public interface IBindingTarget
{
/// <summary>
/// 바인딩된 속성의 경로
/// </summary>
string PropertyPath { get; }
/// <summary>
/// UI 요소의 값을 업데이트
/// </summary>
/// <param name="value">새로운 값</param>
void UpdateValue(object value);
}
/// <summary>
/// Text 컴포넌트에 대한 바인딩 타겟
/// </summary>
public class TextBindingTarget : IBindingTarget
{
private readonly Text _text;
public string PropertyPath { get; }
public TextBindingTarget(Text text, string propertyPath)
{
_text = text;
PropertyPath = propertyPath;
}
public void UpdateValue(object value)
{
if (_text != null)
_text.text = value?.ToString() ?? string.Empty;
}
}
/// <summary>
/// Image 컴포넌트에 대한 바인딩 타겟
/// </summary>
public class ImageBindingTarget : IBindingTarget
{
private readonly Image _image;
public string PropertyPath { get; }
public ImageBindingTarget(Image image, string propertyPath)
{
_image = image;
PropertyPath = propertyPath;
}
public void UpdateValue(object value)
{
if (_image != null && value is Sprite sprite)
_image.sprite = sprite;
}
}
/// <summary>
/// GameObject의 활성화 상태에 대한 바인딩 타겟
/// </summary>
public class ActiveBindingTarget : IBindingTarget
{
private readonly GameObject _gameObject;
public string PropertyPath { get; }
public ActiveBindingTarget(GameObject gameObject, string propertyPath)
{
_gameObject = gameObject;
PropertyPath = propertyPath;
}
public void UpdateValue(object value)
{
if (_gameObject != null)
_gameObject.SetActive(value is bool active && active);
}
}
/// <summary>
/// Slider 컴포넌트에 대한 바인딩 타겟
/// </summary>
public class SliderBindingTarget : IBindingTarget
{
private readonly Slider _slider;
public string PropertyPath { get; }
public SliderBindingTarget(Slider slider, string propertyPath)
{
_slider = slider;
PropertyPath = propertyPath;
}
public void UpdateValue(object value)
{
if (_slider != null && value is float floatValue)
_slider.value = floatValue;
}
}
/// <summary> /// <summary>
/// 바인딩 컨텍스트 - ViewModel과 View 간의 데이터 바인딩을 관리 /// 바인딩 컨텍스트 - ViewModel과 View 간의 데이터 바인딩을 관리
/// </summary> /// </summary>

View File

@ -0,0 +1,39 @@
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace DDD
{
public static class BindingHelper
{
public static void BindText(BindingContext context, TextMeshProUGUI text, string propertyPath, IValueConverter converter = null)
{
var target = new TextBindingTarget(text, propertyPath);
context?.Bind(propertyPath, target, converter);
}
public static void BindImage(BindingContext context, Image image, string propertyPath, IValueConverter converter = null)
{
var target = new ImageBindingTarget(image, propertyPath);
context?.Bind(propertyPath, target, converter);
}
public static void BindImageFilled(BindingContext context, Image image, string propertyPath, IValueConverter converter = null)
{
var target = new ImageFilledBindingTarget(image, propertyPath);
context?.Bind(propertyPath, target, converter);
}
public static void BindActive(BindingContext context, GameObject gameObject, string propertyPath, IValueConverter converter = null)
{
var target = new ActiveBindingTarget(gameObject, propertyPath);
context?.Bind(propertyPath, target, converter);
}
public static void BindSlider(BindingContext context, Slider slider, string propertyPath, IValueConverter converter = null)
{
var target = new SliderBindingTarget(slider, propertyPath);
context?.Bind(propertyPath, target, converter);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a76b583779de40c0b4b3a922c4efb82d
timeCreated: 1755747839

View File

@ -0,0 +1,118 @@
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace DDD
{
public interface IBindingTarget
{
/// <summary>
/// 바인딩된 속성의 경로
/// </summary>
string PropertyPath { get; }
/// <summary>
/// UI 요소의 값을 업데이트
/// </summary>
/// <param name="value">새로운 값</param>
void UpdateValue(object value);
}
public class TextBindingTarget : IBindingTarget
{
private readonly TextMeshProUGUI _text;
public string PropertyPath { get; }
public TextBindingTarget(TextMeshProUGUI text, string propertyPath)
{
_text = text;
PropertyPath = propertyPath;
}
public void UpdateValue(object value)
{
if (_text != null)
{
_text.text = value?.ToString() ?? string.Empty;
}
}
}
public class ImageBindingTarget : IBindingTarget
{
private readonly Image _image;
public string PropertyPath { get; }
public ImageBindingTarget(Image image, string propertyPath)
{
_image = image;
PropertyPath = propertyPath;
}
public void UpdateValue(object value)
{
if (_image != null && value is Sprite sprite)
{
_image.sprite = sprite;
}
}
}
public class ImageFilledBindingTarget : IBindingTarget
{
private readonly Image _image;
public string PropertyPath { get; }
public ImageFilledBindingTarget(Image image, string propertyPath)
{
_image = image;
PropertyPath = propertyPath;
}
public void UpdateValue(object value)
{
if (_image != null && value is float floatValue)
{
_image.fillAmount = Mathf.Clamp01(floatValue); // 0-1 범위로 제한
}
}
}
public class ActiveBindingTarget : IBindingTarget
{
private readonly GameObject _gameObject;
public string PropertyPath { get; }
public ActiveBindingTarget(GameObject go, string propertyPath)
{
_gameObject = go;
PropertyPath = propertyPath;
}
public void UpdateValue(object value)
{
if (_gameObject != null)
{
_gameObject.SetActive(value is true);
}
}
}
public class SliderBindingTarget : IBindingTarget
{
private readonly Slider _slider;
public string PropertyPath { get; }
public SliderBindingTarget(Slider slider, string propertyPath)
{
_slider = slider;
PropertyPath = propertyPath;
}
public void UpdateValue(object value)
{
if (_slider != null && value is float floatValue)
_slider.value = floatValue;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: cd44087c256644b9815bfdc23ac4b29f
timeCreated: 1755749119

View File

@ -1,24 +0,0 @@
namespace DDD.MVVM
{
/// <summary>
/// 입력 처리 단계를 나타내는 열거형
/// 매직 스트링을 제거하고 타입 안전성을 제공
/// </summary>
public enum InputPhaseType
{
/// <summary>
/// 입력이 시작됨
/// </summary>
Started,
/// <summary>
/// 입력이 수행됨
/// </summary>
Performed,
/// <summary>
/// 입력이 취소됨
/// </summary>
Canceled
}
}

View File

@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 5c9b66b101f99e1458e01b9e0653935f

View File

@ -10,8 +10,11 @@ public abstract class SimpleViewModel : MonoBehaviour, INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged; public event PropertyChangedEventHandler PropertyChanged;
protected virtual void Awake() { } protected virtual void Awake() { }
protected virtual void OnEnable() { }
protected virtual void Start() { } protected virtual void Start() { }
protected virtual void OnDisable() { }
protected virtual void OnDestroy() { } protected virtual void OnDestroy() { }
public virtual void Initialize() { } public virtual void Initialize() { }
public virtual void Cleanup() { } public virtual void Cleanup() { }
@ -35,6 +38,7 @@ protected virtual void OnPropertyChanged([CallerMemberName] string propertyName
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null) protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{ {
if (EqualityComparer<T>.Default.Equals(field, value)) return false; if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value; field = value;
OnPropertyChanged(propertyName); OnPropertyChanged(propertyName);
return true; return true;
@ -64,24 +68,11 @@ protected void EndUpdate()
if (_pendingNotifications.Count > 0) if (_pendingNotifications.Count > 0)
{ {
foreach (var prop in _pendingNotifications) foreach (var prop in _pendingNotifications)
{
OnPropertyChanged(prop); OnPropertyChanged(prop);
}
_pendingNotifications.Clear(); _pendingNotifications.Clear();
} }
} }
/// <summary>
/// PropertyChanged 이벤트 발생 (배치 업데이트 고려)
/// </summary>
protected virtual void OnPropertyChangedInternal([CallerMemberName] string propertyName = null)
{
if (_isUpdating)
{
_pendingNotifications.Add(propertyName);
}
else
{
OnPropertyChanged(propertyName);
}
}
} }
} }

View File

@ -1,9 +1,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Linq; using System.Linq;
using UnityEngine; using UnityEngine;
namespace DDD.MVVM namespace DDD
{ {
/// <summary> /// <summary>
/// 인벤토리 UI의 ViewModel /// 인벤토리 UI의 ViewModel
@ -12,8 +11,7 @@ namespace DDD.MVVM
public class InventoryViewModel : SimpleViewModel, IEventHandler<InventoryChangedEvent>, public class InventoryViewModel : SimpleViewModel, IEventHandler<InventoryChangedEvent>,
IEventHandler<TodayMenuAddedEvent>, IEventHandler<TodayMenuRemovedEvent> IEventHandler<TodayMenuAddedEvent>, IEventHandler<TodayMenuRemovedEvent>
{ {
[Header("Services")] private InventoryService _inventoryService;
[SerializeField] private InventoryService _inventoryService;
// Private fields for properties // Private fields for properties
private InventoryCategoryType _currentCategory = InventoryCategoryType.Food; private InventoryCategoryType _currentCategory = InventoryCategoryType.Food;
@ -33,8 +31,6 @@ public InventoryCategoryType CurrentCategory
if (SetField(ref _currentCategory, value)) if (SetField(ref _currentCategory, value))
{ {
UpdateVisibleItems(); UpdateVisibleItems();
// 연관된 계산된 속성들도 알림
OnPropertyChanged(nameof(CategoryDisplayText));
} }
} }
} }
@ -81,21 +77,6 @@ public ItemViewModel SelectedItem
set => SetField(ref _selectedItem, value); set => SetField(ref _selectedItem, value);
} }
// Computed Properties (계산된 속성들)
/// <summary>
/// 카테고리 표시 텍스트 (한국어)
/// </summary>
public string CategoryDisplayText => CurrentCategory switch
{
InventoryCategoryType.Food => "음식",
InventoryCategoryType.Drink => "음료",
InventoryCategoryType.Ingredient => "재료",
InventoryCategoryType.Cookware => "조리도구",
InventoryCategoryType.Special => "특수",
_ => "전체"
};
/// <summary> /// <summary>
/// 보이는 아이템들 중 실제 보유한 아이템이 있는지 확인 /// 보이는 아이템들 중 실제 보유한 아이템이 있는지 확인
/// </summary> /// </summary>

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 36adabeb3767cf64684116798ff0ef30
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 76b62bf64be94ab4bb1e4b610da29fa4
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,210 +0,0 @@
using System;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEngine.UI;
namespace DDD.MVVM
{
/// <summary>
/// 자동 바인딩을 지원하는 View 기본 클래스
/// Attribute를 통해 설정된 바인딩을 자동으로 처리
/// </summary>
/// <typeparam name="TViewModel">바인딩할 ViewModel 타입</typeparam>
public abstract class AutoBindView<TViewModel> : MonoBehaviour where TViewModel : SimpleViewModel
{
[SerializeField] protected TViewModel _viewModel;
protected BindingContext _bindingContext;
/// <summary>
/// ViewModel 인스턴스
/// </summary>
public TViewModel ViewModel => _viewModel;
protected virtual void Awake()
{
if (_viewModel == null)
_viewModel = GetComponent<TViewModel>();
_bindingContext = new BindingContext();
SetupAutoBindings();
}
protected virtual void OnEnable()
{
if (_viewModel != null && _bindingContext != null)
{
_bindingContext.SetDataContext(_viewModel);
_viewModel.PropertyChanged += OnViewModelPropertyChanged;
}
}
protected virtual void OnDisable()
{
if (_viewModel != null)
{
_viewModel.PropertyChanged -= OnViewModelPropertyChanged;
}
}
protected virtual void OnDestroy()
{
_bindingContext?.Dispose();
}
/// <summary>
/// Attribute 기반 자동 바인딩 설정
/// </summary>
private void SetupAutoBindings()
{
var fields = GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public)
.Where(f => f.GetCustomAttribute<BindToAttribute>() != null);
foreach (var field in fields)
{
var bindAttribute = field.GetCustomAttribute<BindToAttribute>();
SetupBinding(field, bindAttribute);
}
// 컬렉션 바인딩 설정
var collectionFields = GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public)
.Where(f => f.GetCustomAttribute<BindCollectionAttribute>() != null);
foreach (var field in collectionFields)
{
var bindAttribute = field.GetCustomAttribute<BindCollectionAttribute>();
SetupCollectionBinding(field, bindAttribute);
}
}
/// <summary>
/// 개별 필드의 바인딩 설정
/// </summary>
/// <param name="field">바인딩할 필드</param>
/// <param name="bindAttribute">바인딩 Attribute</param>
private void SetupBinding(FieldInfo field, BindToAttribute bindAttribute)
{
var target = field.GetValue(this);
IValueConverter converter = null;
if (bindAttribute.ConverterType != null)
{
converter = Activator.CreateInstance(bindAttribute.ConverterType) as IValueConverter;
}
// UI 컴포넌트 타입별 바인딩 타겟 생성
IBindingTarget bindingTarget = target switch
{
Text text => new TextBindingTarget(text, bindAttribute.PropertyPath),
Image image => new ImageBindingTarget(image, bindAttribute.PropertyPath),
GameObject gameObject => new ActiveBindingTarget(gameObject, bindAttribute.PropertyPath),
Slider slider => new SliderBindingTarget(slider, bindAttribute.PropertyPath),
_ => null
};
if (bindingTarget != null)
{
_bindingContext.Bind(bindAttribute.PropertyPath, bindingTarget, converter);
}
}
/// <summary>
/// 컬렉션 바인딩 설정
/// </summary>
/// <param name="field">바인딩할 필드</param>
/// <param name="bindAttribute">바인딩 Attribute</param>
private void SetupCollectionBinding(FieldInfo field, BindCollectionAttribute bindAttribute)
{
var target = field.GetValue(this);
if (target is Transform parent)
{
// 컬렉션 바인딩은 별도 구현이 필요한 복잡한 기능으로
// 현재는 기본 구조만 제공
Debug.Log($"Collection binding for {bindAttribute.PropertyPath} is set up on {parent.name}");
}
}
/// <summary>
/// ViewModel 속성 변경 이벤트 핸들러
/// 추가적인 커스텀 로직이 필요한 경우 오버라이드
/// </summary>
/// <param name="sender">이벤트 발신자</param>
/// <param name="e">속성 변경 정보</param>
protected virtual void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
{
// 자동 바인딩으로 처리되지 않는 특별한 속성들의 커스텀 처리
HandleCustomPropertyChanged(e.PropertyName);
}
/// <summary>
/// 커스텀 속성 변경 처리 (하위 클래스에서 오버라이드)
/// </summary>
/// <param name="propertyName">변경된 속성 이름</param>
protected virtual void HandleCustomPropertyChanged(string propertyName)
{
// 하위 클래스에서 구현
}
/// <summary>
/// 수동 바인딩 헬퍼 메서드들
/// Attribute 사용이 어려운 경우 코드로 바인딩 설정
/// </summary>
protected void BindText(Text text, string propertyPath, IValueConverter converter = null)
{
var target = new TextBindingTarget(text, propertyPath);
_bindingContext?.Bind(propertyPath, target, converter);
}
protected void BindImage(Image image, string propertyPath, IValueConverter converter = null)
{
var target = new ImageBindingTarget(image, propertyPath);
_bindingContext?.Bind(propertyPath, target, converter);
}
protected void BindActive(GameObject gameObject, string propertyPath, IValueConverter converter = null)
{
var target = new ActiveBindingTarget(gameObject, propertyPath);
_bindingContext?.Bind(propertyPath, target, converter);
}
protected void BindSlider(Slider slider, string propertyPath, IValueConverter converter = null)
{
var target = new SliderBindingTarget(slider, propertyPath);
_bindingContext?.Bind(propertyPath, target, converter);
}
/// <summary>
/// ViewModel 메서드 호출 헬퍼
/// UI 이벤트에서 ViewModel 메서드를 쉽게 호출
/// </summary>
/// <param name="methodName">호출할 메서드 이름</param>
/// <param name="parameters">메서드 매개변수</param>
protected void InvokeViewModelMethod(string methodName, params object[] parameters)
{
if (_viewModel == null) return;
var method = _viewModel.GetType().GetMethod(methodName);
method?.Invoke(_viewModel, parameters);
}
/// <summary>
/// ViewModel 속성 직접 설정 헬퍼
/// </summary>
/// <param name="propertyName">속성 이름</param>
/// <param name="value">설정할 값</param>
protected void SetViewModelProperty(string propertyName, object value)
{
if (_viewModel == null) return;
var property = _viewModel.GetType().GetProperty(propertyName);
if (property != null && property.CanWrite)
{
property.SetValue(_viewModel, value);
}
}
}
}

View File

@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 738101122cf3fb74e99b244165797ab8

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: abb1dd67b48daeb4f968a2641cf7b4a3
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -5,7 +5,7 @@ namespace DDD
{ {
public abstract class BasePopupUi : BaseUi public abstract class BasePopupUi : BaseUi
{ {
public bool IsTopPopup => UiManager.Instance.PopupUiState.IsTopPopup(this); public bool IsTopPopup => UiManager.Instance.UiState.IsTopPopup(this);
public InputActionMaps InputActionMaps; public InputActionMaps InputActionMaps;
protected override void Awake() protected override void Awake()
@ -29,10 +29,10 @@ protected override void Update()
if (IsOpenPanel() == false) return; if (IsOpenPanel() == false) return;
var currentSelectedGameObject = EventSystem.current.currentSelectedGameObject; var currentSelectedGameObject = EventSystem.current.currentSelectedGameObject;
if (currentSelectedGameObject == null || currentSelectedGameObject.activeInHierarchy == false) if (!currentSelectedGameObject || currentSelectedGameObject.activeInHierarchy == false)
{ {
var initialSelected = GetInitialSelected(); var initialSelected = GetInitialSelected();
if (initialSelected != null) if (initialSelected)
{ {
EventSystem.current.SetSelectedGameObject(initialSelected); EventSystem.current.SetSelectedGameObject(initialSelected);
} }
@ -43,14 +43,14 @@ protected override void TryRegister()
{ {
base.TryRegister(); base.TryRegister();
UiManager.Instance.PopupUiState?.RegisterPopupUI(this); UiManager.Instance.UiState.RegisterPopupUI(this);
} }
protected override void TryUnregister() protected override void TryUnregister()
{ {
base.TryUnregister(); base.TryUnregister();
UiManager.Instance?.PopupUiState?.UnregisterPopupUI(this); UiManager.Instance?.UiState?.UnregisterPopupUI(this);
} }
public virtual void Open(OpenPopupUiEvent evt) public virtual void Open(OpenPopupUiEvent evt)

View File

@ -6,31 +6,47 @@
namespace DDD namespace DDD
{ {
[CreateAssetMenu(fileName = "PopupUiState", menuName = "GameUi/PopupUiState")] public class UiState : SerializedScriptableObject, IEventHandler<OpenPopupUiEvent>, IEventHandler<ClosePopupUiEvent>
public class PopupUiState : SerializedScriptableObject, IEventHandler<OpenPopupUiEvent>, IEventHandler<ClosePopupUiEvent>
{ {
public Dictionary<GameFlowState, List<BasePopupUi>> FlowToPopupUiMapping = new(); private readonly Dictionary<Type, BaseUi> _uis = new();
private readonly Dictionary<Type, BasePopupUi> _popupUis = new();
private readonly Stack<BasePopupUi> _popupUiStack = new();
private InputActionMaps _previousActionMap = InputActionMaps.None;
private UiData _uiData => GameData.Instance.UiData;
public void OnEnable()
{
EventBus.Register<OpenPopupUiEvent>(this);
EventBus.Register<ClosePopupUiEvent>(this);
[Title("디버그")] _uis.Clear();
[ReadOnly, ShowInInspector] private readonly Dictionary<Type, BasePopupUi> _popupUis = new(); _popupUis.Clear();
[ReadOnly, ShowInInspector] private readonly Stack<BasePopupUi> _popupUiStack = new(); _popupUiStack.Clear();
[ReadOnly, SerializeField] private InputActionMaps _previousActionMap = InputActionMaps.None; }
private void OnDisable() private void OnDisable()
{ {
EventBus.Unregister<OpenPopupUiEvent>(this); EventBus.Unregister<OpenPopupUiEvent>(this);
EventBus.Unregister<ClosePopupUiEvent>(this); EventBus.Unregister<ClosePopupUiEvent>(this);
} }
public void Initialize() public void RegisterUI(BaseUi ui)
{ {
EventBus.Register<OpenPopupUiEvent>(this); var type = ui.GetType();
EventBus.Register<ClosePopupUiEvent>(this); _uis.TryAdd(type, ui);
_popupUis.Clear();
_popupUiStack.Clear();
} }
public void UnregisterUI(BaseUi ui)
{
var type = ui.GetType();
if (_uis.TryGetValue(type, out var registered) && registered == ui)
{
_uis.Remove(type);
}
}
public void RegisterPopupUI(BasePopupUi ui) public void RegisterPopupUI(BasePopupUi ui)
{ {
var type = ui.GetType(); var type = ui.GetType();
@ -85,51 +101,26 @@ public void Invoke(ClosePopupUiEvent evt)
} }
} }
} }
public void CreatePopup(BasePopupUi popup, Transform parent)
{
if (_popupUis.TryGetValue(popup.GetType(), out var registered) && registered == popup) return;
var instance = Instantiate(popup, parent);
instance.name = popup.name;
}
public void DestroyPopup(BasePopupUi popup)
{
if (_popupUis.TryGetValue(popup.GetType(), out var registered) == false || registered != popup) return;
Destroy(popup.gameObject);
}
public List<BasePopupUi> GetMatchingPopupUis(GameFlowState flowState)
{
return FlowToPopupUiMapping
.Where(keyValuePair => (keyValuePair.Key & flowState) != 0)
.SelectMany(keyValuePair => keyValuePair.Value)
.ToList();
}
public bool HasMatchingPopupUis(GameFlowState flowState) public bool HasMatchingPopupUis(GameFlowState flowState)
{ {
return FlowToPopupUiMapping.Any(keyValuePair => (keyValuePair.Key & flowState) != 0); return _uiData.FlowToUiMapping.Any(keyValuePair => (keyValuePair.Key & flowState) != 0);
} }
public void CreateMatchingPopupUis(GameFlowState flowState, Transform parent) public void CreateUi(BaseUi ui, Transform parent)
{ {
var matchingPopupUis = GetMatchingPopupUis(flowState); if (_uis.TryGetValue(ui.GetType(), out var registered) && registered == ui) return;
foreach (var popupUi in matchingPopupUis)
{ var instance = Instantiate(ui, parent);
CreatePopup(popupUi, parent); instance.name = ui.name;
} instance.CreateInitialize();
} }
public void DestroyMatchingPopupUis(GameFlowState flowState) public void DestroyUi(BaseUi ui)
{ {
var matchingPopupUis = GetMatchingPopupUis(flowState); if (_uis.TryGetValue(ui.GetType(), out var registered) && registered == ui) return;
foreach (var popupUi in matchingPopupUis)
{ Destroy(ui.gameObject);
DestroyPopup(popupUi);
}
} }
public bool IsTopPopup(BasePopupUi popup) public bool IsTopPopup(BasePopupUi popup)

View File

@ -2,14 +2,9 @@
using UnityEngine.UI; using UnityEngine.UI;
using UnityEngine.EventSystems; using UnityEngine.EventSystems;
using UnityEngine.InputSystem; using UnityEngine.InputSystem;
using DDD.MVVM;
namespace DDD namespace DDD
{ {
/// <summary>
/// MVVM 패턴을 적용한 새로운 레스토랑 관리 UI
/// 기존 RestaurantManagementUi의 기능을 ViewModel과 분리하여 구현
/// </summary>
[RequireComponent(typeof(RestaurantManagementViewModel))] [RequireComponent(typeof(RestaurantManagementViewModel))]
public class RestaurantManagementUi : PopupUi<RestaurantUiActions, RestaurantManagementViewModel> public class RestaurantManagementUi : PopupUi<RestaurantUiActions, RestaurantManagementViewModel>
{ {
@ -26,26 +21,25 @@ public class RestaurantManagementUi : PopupUi<RestaurantUiActions, RestaurantMan
[SerializeField] private TabGroupUi _cookwareCategoryTabs; [SerializeField] private TabGroupUi _cookwareCategoryTabs;
[Header("Hold Progress UI")] [Header("Hold Progress UI")]
[SerializeField, BindTo(nameof(RestaurantManagementViewModel.NormalizedHoldProgress))] [SerializeField] private Image _completeBatchFilledImage;
private Image _completeBatchFilledImage;
protected override void Awake()
{
base.Awake();
SetupViewModelEvents();
}
protected override void Update() protected override void Update()
{ {
base.Update(); base.Update();
if (_viewModel != null && _viewModel.IsHolding) if (_viewModel)
{ {
_viewModel.UpdateHoldProgress(); _viewModel.UpdateHoldProgress();
} }
} }
protected override void TryRegister()
{
base.TryRegister();
SetupViewModelEvents();
}
public override void Open(OpenPopupUiEvent evt) public override void Open(OpenPopupUiEvent evt)
{ {
base.Open(evt); base.Open(evt);
@ -73,8 +67,7 @@ protected override GameObject GetInitialSelected()
protected override void SetupBindings() protected override void SetupBindings()
{ {
// Attribute 기반 자동 바인딩이 처리됨 BindingHelper.BindImageFilled(_bindingContext, _completeBatchFilledImage, nameof(RestaurantManagementViewModel.NormalizedHoldProgress));
// 추가적인 수동 바인딩이 필요한 경우 여기에 구현
} }
protected override void HandleCustomPropertyChanged(string propertyName) protected override void HandleCustomPropertyChanged(string propertyName)
@ -260,7 +253,7 @@ protected override bool OnInputCanceled(RestaurantUiActions actionEnum, InputAct
{ {
var isHandled = base.OnInputCanceled(actionEnum, context); var isHandled = base.OnInputCanceled(actionEnum, context);
if (isHandled && _viewModel != null) if (isHandled && _viewModel)
{ {
switch (actionEnum) switch (actionEnum)
{ {

View File

@ -0,0 +1,7 @@
namespace DDD
{
public class RestaurantHud : BaseUi
{
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 7521841876f5b054aa4a0b0081ff8425

View File

@ -1,390 +1,402 @@
using System; // using System;
using UnityEngine; // using UnityEngine;
using UnityEngine.UI; // using UnityEngine.UI;
using UnityEngine.EventSystems; // using UnityEngine.EventSystems;
using UnityEngine.InputSystem; // using UnityEngine.InputSystem;
//
namespace DDD // namespace DDD
{ // {
public enum ButtonState // public enum ButtonState
{ // {
Normal, // Normal,
Highlighted, // Highlighted,
Pressed, // Pressed,
Selected, // Selected,
Disabled // Disabled
} // }
//
public class UiButton : MonoBehaviour, IInteractableUi, IPointerEnterHandler, IPointerExitHandler, // public enum ButtonType
IPointerDownHandler, IPointerUpHandler, ISelectHandler, IDeselectHandler, ISubmitHandler // {
{ // None = 0,
[Header("Button Components")] // Toggle
[SerializeField] private Button _button; // }
[SerializeField] private Selectable _selectable; //
// // TODO : ButtonType == None
[Header("State Synchronization")] // // normal, selected(마우스 pointerEnter, eventsystem selected), pressed (마우스 pointerDown, 키보드 외부 입력처리), disabled
[SerializeField] private bool _synchronizeStates = true; // // Highlighted가 사실상 selected로 통합, 실제로 마우스가 가리키는 오브젝트가 eventsystem의 selected가 됨
[SerializeField] private bool _handleKeyboardInput = true; // // ButtonType == Toggle
[SerializeField] private bool _handleGamepadInput = true; // // normal, highlighted, selected(계속 눌려있는 상태, 추후에 ToggleGroup 클래스에서 관리 - 다른 토글이 눌리기 전까지 풀리지 않음), pressed(눌리면 selected 고정), disabled
//
[Header("Visual Feedback")] // public class UiButton : MonoBehaviour, IInteractableUi, IPointerEnterHandler, IPointerExitHandler,
[SerializeField] private Animator _animator; // IPointerDownHandler, IPointerUpHandler, ISelectHandler, IDeselectHandler, ISubmitHandler
[SerializeField] private Image _targetGraphic; // {
// [Header("Button Components")]
[Header("Toggle Functionality")] // [SerializeField] private Button _button;
[SerializeField] private bool _useToggle = false; // [SerializeField] private Selectable _selectable;
//
// State tracking // [Header("State Synchronization")]
private bool _isPressed; // [SerializeField] private bool _synchronizeStates = true;
private bool _isHighlighted; // [SerializeField] private bool _handleKeyboardInput = true;
private bool _isSelected; // [SerializeField] private bool _handleGamepadInput = true;
private bool _wasSelectedByKeyboard; //
private bool _isToggled = false; // [Header("Visual Feedback")]
// [SerializeField] private Animator _animator;
// Events // [SerializeField] private Image _targetGraphic;
public event Action OnClicked; //
public event Action OnStateChanged; // [Header("Toggle Functionality")]
// [SerializeField] private ButtonType _buttonType;
// Animation parameter hashes (if using Animator) //
private readonly int _normalHash = Animator.StringToHash("Normal"); // // State tracking
private readonly int _highlightedHash = Animator.StringToHash("Highlighted"); // private bool _isPressed;
private readonly int _pressedHash = Animator.StringToHash("Pressed"); // private bool _isHighlighted;
private readonly int _selectedHash = Animator.StringToHash("Selected"); // private bool _isSelected;
private readonly int _disabledHash = Animator.StringToHash("Disabled"); // private bool _wasSelectedByKeyboard;
// private bool _isToggled = false;
private void Awake() //
{ // // Events
InitializeComponents(); // public event Action OnClicked;
} // public event Action OnStateChanged;
//
private void OnEnable() // // Animation parameter hashes (if using Animator)
{ // private readonly int _normalHash = Animator.StringToHash("Normal");
if (_button != null) // private readonly int _highlightedHash = Animator.StringToHash("Highlighted");
{ // private readonly int _pressedHash = Animator.StringToHash("Pressed");
_button.onClick.AddListener(HandleButtonClick); // private readonly int _selectedHash = Animator.StringToHash("Selected");
} // private readonly int _disabledHash = Animator.StringToHash("Disabled");
//
UpdateVisualState(); // private void Awake()
} // {
// InitializeComponents();
private void OnDisable() // }
{ //
if (_button != null) // private void OnEnable()
{ // {
_button.onClick.RemoveListener(HandleButtonClick); // if (_button != null)
} // {
} // _button.onClick.AddListener(HandleButtonClick);
// }
private void Update() //
{ // UpdateVisualState();
HandleInputUpdate(); // }
} //
// private void OnDisable()
private void InitializeComponents() // {
{ // if (_button != null)
// Get Button component if not assigned // {
if (_button == null) // _button.onClick.RemoveListener(HandleButtonClick);
{ // }
_button = GetComponent<Button>(); // }
} //
// private void Update()
// Get Selectable component (Button inherits from Selectable) // {
if (_selectable == null) // HandleInputUpdate();
{ // }
_selectable = _button; //
} // private void InitializeComponents()
// {
// Get target graphic from button if not assigned // // Get Button component if not assigned
if (_targetGraphic == null && _button != null) // if (_button == null)
{ // {
_targetGraphic = _button.targetGraphic as Image; // _button = GetComponent<Button>();
} // }
//
// Get Animator if not assigned // // Get Selectable component (Button inherits from Selectable)
if (_animator == null) // if (_selectable == null)
{ // {
_animator = GetComponent<Animator>(); // _selectable = _button;
} // }
} //
// // Get target graphic from button if not assigned
private void HandleInputUpdate() // if (_targetGraphic == null && _button != null)
{ // {
if (!_handleKeyboardInput && !_handleGamepadInput) return; // _targetGraphic = _button.targetGraphic as Image;
if (_selectable == null || !_selectable.interactable) return; // }
//
// Handle keyboard/gamepad input when this button is selected // // Get Animator if not assigned
if (EventSystem.current != null && EventSystem.current.currentSelectedGameObject == gameObject) // if (_animator == null)
{ // {
//HandleSelectedInput(); // _animator = GetComponent<Animator>();
} // }
} // }
//
private void HandleSelectedInput() // private void HandleInputUpdate()
{ // {
var keyboard = Keyboard.current; // if (!_handleKeyboardInput && !_handleGamepadInput) return;
var gamepad = Gamepad.current; // if (_selectable == null || !_selectable.interactable) return;
//
// Handle keyboard input // // Handle keyboard/gamepad input when this button is selected
if (_handleKeyboardInput && keyboard != null) // if (EventSystem.current != null && EventSystem.current.currentSelectedGameObject == gameObject)
{ // {
if (keyboard.enterKey.wasPressedThisFrame || keyboard.spaceKey.wasPressedThisFrame) // //HandleSelectedInput();
{ // }
HandleButtonClick(); // }
} //
} // private void HandleSelectedInput()
// {
// Handle gamepad input // var keyboard = Keyboard.current;
if (_handleGamepadInput && gamepad != null) // var gamepad = Gamepad.current;
{ //
if (gamepad.buttonSouth.wasPressedThisFrame) // A button on Xbox controller // // Handle keyboard input
{ // if (_handleKeyboardInput && keyboard != null)
HandleButtonClick(); // {
} // if (keyboard.enterKey.wasPressedThisFrame || keyboard.spaceKey.wasPressedThisFrame)
} // {
} // HandleButtonClick();
// }
private void HandleButtonClick() // }
{ //
if (_selectable != null && !_selectable.interactable) return; // // Handle gamepad input
// if (_handleGamepadInput && gamepad != null)
if (_useToggle) // {
{ // if (gamepad.buttonSouth.wasPressedThisFrame) // A button on Xbox controller
_isToggled = !_isToggled; // {
// HandleButtonClick();
if (_isToggled) // }
{ // }
// When toggled on, maintain pressed state and make non-interactable // }
_isPressed = true; //
_selectable.interactable = false; // private void HandleButtonClick()
} // {
else // if (_selectable != null && !_selectable.interactable) return;
{ //
// When toggled off, restore normal behavior // if (_buttonType == ButtonType.Toggle)
_isPressed = false; // {
_selectable.interactable = true; // _isToggled = !_isToggled;
} //
// if (_isToggled)
UpdateVisualState(); // {
} // // When toggled on, maintain pressed state and make non-interactable
// _isPressed = true;
OnClicked?.Invoke(); // _selectable.interactable = false;
} // }
// else
private void UpdateVisualState() // {
{ // // When toggled off, restore normal behavior
if (!_synchronizeStates) return; // _isPressed = false;
// _selectable.interactable = true;
var currentState = GetCurrentState(); // }
print(currentState); //
ApplyVisualState(currentState); // UpdateVisualState();
OnStateChanged?.Invoke(); // }
} //
// OnClicked?.Invoke();
private ButtonState GetCurrentState() // }
{ //
if (_selectable == null || (!_selectable.interactable && !_isToggled)) // private void UpdateVisualState()
return ButtonState.Disabled; // {
// if (!_synchronizeStates) return;
// Toggle mode: when toggled on, maintain pressed state even if not interactable //
if (_useToggle && _isToggled) // var currentState = GetCurrentState();
return ButtonState.Pressed; // print(currentState);
// ApplyVisualState(currentState);
if (_isPressed) // OnStateChanged?.Invoke();
return ButtonState.Pressed; // }
//
if (_isSelected) // private ButtonState GetCurrentState()
return ButtonState.Selected; // {
// if (_selectable == null || (!_selectable.interactable && !_isToggled))
if (_isHighlighted) // return ButtonState.Disabled;
return ButtonState.Highlighted; //
// // Toggle mode: when toggled on, maintain pressed state even if not interactable
return ButtonState.Normal; // if (_useToggle && _isToggled)
} // return ButtonState.Pressed;
//
private void ApplyVisualState(ButtonState state) // if (_isPressed)
{ // return ButtonState.Pressed;
// Apply animator state if available //
if (_animator != null && _animator.runtimeAnimatorController != null) // if (_isSelected)
{ // return ButtonState.Selected;
ApplyAnimatorState(state); //
} // if (_isHighlighted)
// return ButtonState.Highlighted;
// Apply color tint if using Button's color block //
if (_button != null && _targetGraphic != null) // return ButtonState.Normal;
{ // }
ApplyColorState(state); //
} // private void ApplyVisualState(ButtonState state)
} // {
// // Apply animator state if available
private void ApplyAnimatorState(ButtonState state) // if (_animator != null && _animator.runtimeAnimatorController != null)
{ // {
switch (state) // ApplyAnimatorState(state);
{ // }
case ButtonState.Normal: //
_animator.SetTrigger(_normalHash); // // Apply color tint if using Button's color block
break; // if (_button != null && _targetGraphic != null)
case ButtonState.Highlighted: // {
_animator.SetTrigger(_highlightedHash); // ApplyColorState(state);
break; // }
case ButtonState.Pressed: // }
_animator.SetTrigger(_pressedHash); //
break; // private void ApplyAnimatorState(ButtonState state)
case ButtonState.Selected: // {
_animator.SetTrigger(_selectedHash); // switch (state)
break; // {
case ButtonState.Disabled: // case ButtonState.Normal:
_animator.SetTrigger(_disabledHash); // _animator.SetTrigger(_normalHash);
break; // break;
} // case ButtonState.Highlighted:
} // _animator.SetTrigger(_highlightedHash);
// break;
private void ApplyColorState(ButtonState state) // case ButtonState.Pressed:
{ // _animator.SetTrigger(_pressedHash);
var colors = _button.colors; // break;
Color targetColor; // case ButtonState.Selected:
// _animator.SetTrigger(_selectedHash);
switch (state) // break;
{ // case ButtonState.Disabled:
case ButtonState.Normal: // _animator.SetTrigger(_disabledHash);
targetColor = colors.normalColor; // break;
break; // }
case ButtonState.Highlighted: // }
targetColor = colors.highlightedColor; //
break; // private void ApplyColorState(ButtonState state)
case ButtonState.Pressed: // {
targetColor = colors.pressedColor; // var colors = _button.colors;
break; // Color targetColor;
case ButtonState.Selected: //
targetColor = colors.selectedColor; // switch (state)
break; // {
case ButtonState.Disabled: // case ButtonState.Normal:
targetColor = colors.disabledColor; // targetColor = colors.normalColor;
break; // break;
default: // case ButtonState.Highlighted:
targetColor = colors.normalColor; // targetColor = colors.highlightedColor;
break; // break;
} // case ButtonState.Pressed:
// targetColor = colors.pressedColor;
_targetGraphic.color = targetColor; // break;
} // case ButtonState.Selected:
// targetColor = colors.selectedColor;
// IInteractableUi implementation // break;
public void OnInteract() // case ButtonState.Disabled:
{ // targetColor = colors.disabledColor;
if (_selectable != null && _selectable.interactable) // break;
{ // default:
// This method is called for programmatic interaction // targetColor = colors.normalColor;
HandleButtonClick(); // break;
} // }
} //
// _targetGraphic.color = targetColor;
// Pointer event handlers // }
public void OnPointerEnter(PointerEventData eventData) //
{ // // IInteractableUi implementation
_isHighlighted = true; // public void OnInteract()
UpdateVisualState(); // {
} // if (_selectable != null && _selectable.interactable)
// {
public void OnPointerExit(PointerEventData eventData) // // This method is called for programmatic interaction
{ // HandleButtonClick();
_isHighlighted = false; // }
UpdateVisualState(); // }
} //
// // Pointer event handlers
public void OnPointerDown(PointerEventData eventData) // public void OnPointerEnter(PointerEventData eventData)
{ // {
if (eventData.button == PointerEventData.InputButton.Left) // _isHighlighted = true;
{ // UpdateVisualState();
if (_isSelected) // }
{ //
_isPressed = true; // public void OnPointerExit(PointerEventData eventData)
UpdateVisualState(); // {
return; // _isHighlighted = false;
} // UpdateVisualState();
_isSelected = true; // }
UpdateVisualState(); //
} // public void OnPointerDown(PointerEventData eventData)
} // {
// if (eventData.button == PointerEventData.InputButton.Left)
public void OnPointerUp(PointerEventData eventData) // {
{ // if (_isSelected)
if (eventData.button == PointerEventData.InputButton.Left) // {
{ // _isPressed = true;
// Don't reset pressed state in toggle mode when toggled on // UpdateVisualState();
if (_isPressed && !(_useToggle && _isToggled)) // return;
{ // }
_isPressed = false; // _isSelected = true;
} // UpdateVisualState();
_isSelected = false; // }
UpdateVisualState(); // }
} //
} // public void OnPointerUp(PointerEventData eventData)
// {
// Selection event handlers (for keyboard/gamepad navigation) // if (eventData.button == PointerEventData.InputButton.Left)
public void OnSelect(BaseEventData eventData) // {
{ // // Don't reset pressed state in toggle mode when toggled on
_isSelected = true; // if (_isPressed && !(_useToggle && _isToggled))
UpdateVisualState(); // {
} // _isPressed = false;
// }
public void OnDeselect(BaseEventData eventData) // _isSelected = false;
{ // UpdateVisualState();
_isSelected = false; // }
UpdateVisualState(); // }
} //
// // Selection event handlers (for keyboard/gamepad navigation)
// Submit handler (for keyboard/gamepad activation) // public void OnSelect(BaseEventData eventData)
public void OnSubmit(BaseEventData eventData) // {
{ // _isSelected = true;
HandleButtonClick(); // UpdateVisualState();
} // }
//
// Public API // public void OnDeselect(BaseEventData eventData)
public bool IsInteractable => _selectable != null && _selectable.interactable; // {
// _isSelected = false;
public void SetInteractable(bool interactable) // UpdateVisualState();
{ // }
if (_selectable != null) //
{ // // Submit handler (for keyboard/gamepad activation)
_selectable.interactable = interactable; // public void OnSubmit(BaseEventData eventData)
UpdateVisualState(); // {
} // HandleButtonClick();
} // }
//
public void ForceUpdateState() // // Public API
{ // public bool IsInteractable => _selectable != null && _selectable.interactable;
UpdateVisualState(); //
} // public void SetInteractable(bool interactable)
// {
// Toggle functionality API // if (_selectable != null)
public bool UseToggle // {
{ // _selectable.interactable = interactable;
get => _useToggle; // UpdateVisualState();
set => _useToggle = value; // }
} // }
//
public bool IsToggled => _isToggled; // public void ForceUpdateState()
// {
public void SetToggleState(bool toggled) // UpdateVisualState();
{ // }
if (!_useToggle) return; //
// // Toggle functionality API
_isToggled = toggled; // public bool UseToggle
// {
if (_isToggled) // get => _useToggle;
{ // set => _useToggle = value;
_isPressed = true; // }
if (_selectable != null) //
_selectable.interactable = false; // public bool IsToggled => _isToggled;
} //
else // public void SetToggleState(bool toggled)
{ // {
_isPressed = false; // if (!_useToggle) return;
if (_selectable != null) //
_selectable.interactable = true; // _isToggled = toggled;
} //
// if (_isToggled)
UpdateVisualState(); // {
} // _isPressed = true;
} // if (_selectable != null)
} // _selectable.interactable = false;
// }
// else
// {
// _isPressed = false;
// if (_selectable != null)
// _selectable.interactable = true;
// }
//
// UpdateVisualState();
// }
// }
// }

View File

@ -1,133 +0,0 @@
using UnityEngine;
using UnityEngine.UI;
namespace DDD
{
/// <summary>
/// Simple test script to verify UiButton functionality.
/// Attach this to a GameObject with UiButton component to test.
/// </summary>
public class UiButtonTest : MonoBehaviour
{
[SerializeField] private UiButton _uiButton;
[SerializeField] private Text _statusText;
private int _clickCount = 0;
private void Start()
{
// Get UiButton if not assigned
if (_uiButton == null)
{
_uiButton = GetComponent<UiButton>();
}
if (_uiButton != null)
{
// Subscribe to events
_uiButton.OnClicked += HandleButtonClicked;
_uiButton.OnStateChanged += HandleStateChanged;
Debug.Log("[DEBUG_LOG] UiButton test initialized");
}
else
{
Debug.LogError("[DEBUG_LOG] UiButton component not found!");
}
UpdateStatusText();
}
private void OnDestroy()
{
if (_uiButton != null)
{
_uiButton.OnClicked -= HandleButtonClicked;
_uiButton.OnStateChanged -= HandleStateChanged;
}
}
private void HandleButtonClicked()
{
_clickCount++;
Debug.Log($"[DEBUG_LOG] UiButton clicked! Count: {_clickCount}");
UpdateStatusText();
}
private void HandleStateChanged()
{
Debug.Log($"[DEBUG_LOG] UiButton state changed. Interactable: {_uiButton.IsInteractable}");
}
private void UpdateStatusText()
{
if (_statusText != null)
{
_statusText.text = $"Clicks: {_clickCount}\nInteractable: {(_uiButton?.IsInteractable ?? false)}";
}
}
// Test methods that can be called from inspector or other scripts
[ContextMenu("Toggle Interactable")]
public void ToggleInteractable()
{
if (_uiButton != null)
{
_uiButton.SetInteractable(!_uiButton.IsInteractable);
Debug.Log($"[DEBUG_LOG] Button interactable set to: {_uiButton.IsInteractable}");
UpdateStatusText();
}
}
[ContextMenu("Force Update State")]
public void ForceUpdateState()
{
if (_uiButton != null)
{
_uiButton.ForceUpdateState();
Debug.Log("[DEBUG_LOG] Button state forcefully updated");
}
}
[ContextMenu("Reset Click Count")]
public void ResetClickCount()
{
_clickCount = 0;
Debug.Log("[DEBUG_LOG] Click count reset");
UpdateStatusText();
}
[ContextMenu("Toggle Use Toggle Mode")]
public void ToggleUseToggleMode()
{
if (_uiButton != null)
{
_uiButton.UseToggle = !_uiButton.UseToggle;
Debug.Log($"[DEBUG_LOG] Toggle mode set to: {_uiButton.UseToggle}");
UpdateStatusText();
}
}
[ContextMenu("Set Toggle State On")]
public void SetToggleStateOn()
{
if (_uiButton != null)
{
_uiButton.SetToggleState(true);
Debug.Log($"[DEBUG_LOG] Toggle state set to ON. Toggled: {_uiButton.IsToggled}");
UpdateStatusText();
}
}
[ContextMenu("Set Toggle State Off")]
public void SetToggleStateOff()
{
if (_uiButton != null)
{
_uiButton.SetToggleState(false);
Debug.Log($"[DEBUG_LOG] Toggle state set to OFF. Toggled: {_uiButton.IsToggled}");
UpdateStatusText();
}
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: cc3aec81f35244d39191739b3bd208d2
timeCreated: 1755681789

View File

@ -1,15 +1,18 @@
using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using UnityEngine; using UnityEngine;
using UnityEngine.AddressableAssets;
namespace DDD namespace DDD
{ {
public class UiManager : Singleton<UiManager>, IManager, IGameFlowHandler public class UiManager : Singleton<UiManager>, IManager, IGameFlowHandler
{ {
[SerializeField] private AssetReference _popupUiState; [SerializeField] private Transform _hudRoot;
[SerializeField] private Transform _interactionUiRoot;
[SerializeField] private Transform _popupUiRoot; [SerializeField] private Transform _popupUiRoot;
[SerializeField] private Transform _commonUiRoot;
public PopupUiState PopupUiState { get; private set; } public UiData UiData => GameData.Instance.UiData;
public UiState UiState => GameState.Instance.UiState;
private void OnDestroy() private void OnDestroy()
{ {
@ -18,17 +21,28 @@ private void OnDestroy()
public void PreInit() public void PreInit()
{ {
ClearAll();
GameFlowManager.Instance.FlowHandlers.Add(this); GameFlowManager.Instance.FlowHandlers.Add(this);
foreach (Transform child in _popupUiRoot)
{
Destroy(child.gameObject);
}
} }
public async Task Init() public Task Init()
{ {
await LoadData(); var flowToUiMapping = UiData.FlowToUiMapping;
foreach (var flowToUis in flowToUiMapping)
{
if (flowToUis.Key == GameFlowState.All)
{
foreach (var ui in flowToUis.Value)
{
var uiType = ui.UiType;
var root = GetUiRoot(uiType);
UiState.CreateUi(ui, root);
}
}
}
return Task.CompletedTask;
} }
public void PostInit() public void PostInit()
@ -36,27 +50,62 @@ public void PostInit()
} }
private void ClearObjects(Transform root)
{
foreach (Transform child in root)
{
Destroy(child.gameObject);
}
}
private void ClearAll()
{
ClearObjects(_hudRoot);
ClearObjects(_interactionUiRoot);
ClearObjects(_popupUiRoot);
ClearObjects(_commonUiRoot);
}
public Task OnReadyNewFlow(GameFlowState newFlowState) public Task OnReadyNewFlow(GameFlowState newFlowState)
{ {
PopupUiState.CreateMatchingPopupUis(newFlowState, _popupUiRoot); var flowToUiMapping = UiData.FlowToUiMapping;
foreach (var flowToUis in flowToUiMapping)
{
if ((flowToUis.Key & newFlowState) != 0)
{
foreach (var ui in flowToUis.Value)
{
var uiType = ui.UiType;
var root = GetUiRoot(uiType);
UiState.CreateUi(ui, root);
}
}
else
{
foreach (var ui in flowToUis.Value)
{
UiState.DestroyUi(ui);
}
}
}
return Task.CompletedTask; return Task.CompletedTask;
} }
public Task OnExitCurrentFlow(GameFlowState exitingFlowState) public Task OnExitCurrentFlow(GameFlowState exitingFlowState)
{ {
PopupUiState.DestroyMatchingPopupUis(exitingFlowState);
return Task.CompletedTask; return Task.CompletedTask;
} }
private async Task LoadData() public Transform GetUiRoot(UiType uiType)
{ {
var handle = _popupUiState.LoadAssetAsync<PopupUiState>(); return uiType switch
await handle.Task; {
PopupUiState = handle.Result; UiType.Hud => _hudRoot,
UiType.Interaction => _interactionUiRoot,
Debug.Assert(PopupUiState != null, "PopupUiState is null"); UiType.Popup => _popupUiRoot,
UiType.Common => _commonUiRoot,
PopupUiState.Initialize(); _ => throw new Exception("UiType 설정 오류")
};
} }
} }
} }