Pool 적용 버그 잡는 중

This commit is contained in:
logonkhi
2025-06-27 17:50:23 +09:00
parent 935979a696
commit 750d38153d
32 changed files with 1977 additions and 218 deletions

View File

@@ -10,9 +10,6 @@ GameObject:
m_Component:
- component: {fileID: 302300799186228596}
- component: {fileID: 8488195170614820883}
- component: {fileID: 3820258601269832329}
- component: {fileID: 363746616583149630}
- component: {fileID: 5360656424770899642}
m_Layer: 0
m_Name: AGV
m_TagString: Untagged
@@ -32,7 +29,8 @@ Transform:
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Children:
- {fileID: 8708419218786706813}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &8488195170614820883
@@ -47,24 +45,72 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: bc083cb3e97c87446bc1a7db7b4ef9e4, type: 3}
m_Name:
m_EditorClassIdentifier:
moveSpeed: 1
rotationSpeed: 1
modelObject: {fileID: 7493524444357289953}
moveSpeed: 0.5
rotationSpeed: 0.5
teleportDistanceThreshold: 5
--- !u!33 &3820258601269832329
--- !u!1 &6358428858938227828
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 8708419218786706813}
- component: {fileID: 7493524444357289953}
- component: {fileID: 6887510220552103127}
- component: {fileID: 9132754914965147154}
- component: {fileID: 6486094733119401088}
m_Layer: 0
m_Name: Cube
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &8708419218786706813
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6358428858938227828}
serializedVersion: 2
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0.5, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 302300799186228596}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &7493524444357289953
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6358428858938227828}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 369f10656ae555b4983fa3147fc56818, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!33 &6887510220552103127
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1373625414374435398}
m_GameObject: {fileID: 6358428858938227828}
m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
--- !u!23 &363746616583149630
--- !u!23 &9132754914965147154
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1373625414374435398}
m_GameObject: {fileID: 6358428858938227828}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
@@ -103,13 +149,13 @@ MeshRenderer:
m_SortingLayer: 0
m_SortingOrder: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!65 &5360656424770899642
--- !u!65 &6486094733119401088
BoxCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1373625414374435398}
m_GameObject: {fileID: 6358428858938227828}
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
@@ -118,7 +164,7 @@ BoxCollider:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_IsTrigger: 1
m_IsTrigger: 0
m_ProvidesContacts: 0
m_Enabled: 1
serializedVersion: 3

View File

@@ -0,0 +1,415 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &494093931030574861
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 5765213305061083199}
- component: {fileID: 4826901156942800586}
- component: {fileID: 8650239058674280921}
m_Layer: 5
m_Name: Text (TMP)
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &5765213305061083199
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 494093931030574861}
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: 0
m_Children: []
m_Father: {fileID: 2463437008278947656}
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!222 &4826901156942800586
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 494093931030574861}
m_CullTransparentMesh: 1
--- !u!114 &8650239058674280921
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 494093931030574861}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text: 0
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 08cebd004d97ca742ac80400f37f4eed, type: 2}
m_sharedMaterial: {fileID: 4860575619018115804, guid: 08cebd004d97ca742ac80400f37f4eed, type: 2}
m_fontSharedMaterials: []
m_fontMaterial: {fileID: 0}
m_fontMaterials: []
m_fontColor32:
serializedVersion: 2
rgba: 4278190080
m_fontColor: {r: 0, g: 0, b: 0, a: 1}
m_enableVertexGradient: 0
m_colorMode: 3
m_fontColorGradient:
topLeft: {r: 1, g: 1, b: 1, a: 1}
topRight: {r: 1, g: 1, b: 1, a: 1}
bottomLeft: {r: 1, g: 1, b: 1, a: 1}
bottomRight: {r: 1, g: 1, b: 1, a: 1}
m_fontColorGradientPreset: {fileID: 0}
m_spriteAsset: {fileID: 0}
m_tintAllSprites: 0
m_StyleSheet: {fileID: 0}
m_TextStyleHashCode: -1183493901
m_overrideHtmlColors: 0
m_faceColor:
serializedVersion: 2
rgba: 4294967295
m_fontSize: 36
m_fontSizeBase: 36
m_fontWeight: 400
m_enableAutoSizing: 0
m_fontSizeMin: 18
m_fontSizeMax: 72
m_fontStyle: 0
m_HorizontalAlignment: 2
m_VerticalAlignment: 512
m_textAlignment: 65535
m_characterSpacing: 0
m_wordSpacing: 0
m_lineSpacing: 0
m_lineSpacingMax: 0
m_paragraphSpacing: 0
m_charWidthMaxAdj: 0
m_TextWrappingMode: 1
m_wordWrappingRatios: 0.4
m_overflowMode: 0
m_linkedTextComponent: {fileID: 0}
parentLinkedComponent: {fileID: 0}
m_enableKerning: 0
m_ActiveFontFeatures: 6e72656b
m_enableExtraPadding: 0
checkPaddingRequired: 0
m_isRichText: 1
m_EmojiFallbackSupport: 1
m_parseCtrlCharacters: 1
m_isOrthographic: 1
m_isCullingEnabled: 0
m_horizontalMapping: 0
m_verticalMapping: 0
m_uvLineOffset: 0
m_geometrySortingOrder: 0
m_IsTextObjectScaleStatic: 0
m_VertexBufferAutoSizeReduction: 0
m_useMaxVisibleDescender: 1
m_pageToDisplay: 1
m_margin: {x: 0, y: 0, z: 0, w: 0}
m_isUsingLegacyAnimationComponent: 0
m_isVolumetricText: 0
m_hasFontAssetChanged: 0
m_baseMaterial: {fileID: 0}
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
--- !u!1 &952545100964675415
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 8574585598928791195}
m_Layer: 5
m_Name: expandedView
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &8574585598928791195
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 952545100964675415}
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: 0
m_Children: []
m_Father: {fileID: 2463437008278947656}
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 &1828762385695490929
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 2463437008278947656}
- component: {fileID: 5380681345511526047}
m_Layer: 5
m_Name: Alarm
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &2463437008278947656
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1828762385695490929}
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: 0
m_Children:
- {fileID: 5007029136827874888}
- {fileID: 5765213305061083199}
- {fileID: 8574585598928791195}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 100, y: 100}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &5380681345511526047
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1828762385695490929}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 773fa02b59601044b8be752f78f63e55, type: 3}
m_Name:
m_EditorClassIdentifier:
clusterView: {fileID: 4918696741701580718}
alarmCountText: {fileID: 8650239058674280921}
expandedView: {fileID: 952545100964675415}
singleAlarmIconPrefab: {fileID: 7314511769243682470, guid: e9acc8c9a93a2b5409fc01661660b217, type: 3}
--- !u!1 &4918696741701580718
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 5007029136827874888}
m_Layer: 5
m_Name: clusterView
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &5007029136827874888
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4918696741701580718}
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: 0
m_Children:
- {fileID: 7229327262419467602}
- {fileID: 1591538567731418999}
m_Father: {fileID: 2463437008278947656}
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 &7297564150823027611
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 7229327262419467602}
- component: {fileID: 7287393582081843390}
- component: {fileID: 4544043674251718843}
m_Layer: 5
m_Name: shadow
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &7229327262419467602
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7297564150823027611}
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: 0
m_Children: []
m_Father: {fileID: 5007029136827874888}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 5, y: -5}
m_SizeDelta: {x: 10, y: 10}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &7287393582081843390
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7297564150823027611}
m_CullTransparentMesh: 1
--- !u!114 &4544043674251718843
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7297564150823027611}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 0, g: 0, b: 0, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 887145076, guid: 4cf3568ca3f55f64cb11447d139d7a3d, type: 3}
m_Type: 1
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!1 &7656214594090597472
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1591538567731418999}
- component: {fileID: 8641121927952400765}
- component: {fileID: 4970931623191621822}
m_Layer: 5
m_Name: bg
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &1591538567731418999
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7656214594090597472}
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: 0
m_Children: []
m_Father: {fileID: 5007029136827874888}
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!222 &8641121927952400765
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7656214594090597472}
m_CullTransparentMesh: 1
--- !u!114 &4970931623191621822
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7656214594090597472}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: -895992892, guid: 73d757b5d1b754245969af12daf01e78, type: 3}
m_Type: 1
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: e8b88e69e607ee448806427e91440a8e
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,340 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &1326045257776503954
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 8602533637707951236}
- component: {fileID: 5770335694761070239}
- component: {fileID: 2018211268787215362}
m_Layer: 5
m_Name: Text (TMP)
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &8602533637707951236
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1326045257776503954}
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: 0
m_Children: []
m_Father: {fileID: 3374630250525829090}
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: -20, y: -20}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &5770335694761070239
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1326045257776503954}
m_CullTransparentMesh: 1
--- !u!114 &2018211268787215362
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1326045257776503954}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text: TR
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 08cebd004d97ca742ac80400f37f4eed, type: 2}
m_sharedMaterial: {fileID: 4860575619018115804, guid: 08cebd004d97ca742ac80400f37f4eed, type: 2}
m_fontSharedMaterials: []
m_fontMaterial: {fileID: 0}
m_fontMaterials: []
m_fontColor32:
serializedVersion: 2
rgba: 4278190080
m_fontColor: {r: 0, g: 0, b: 0, a: 1}
m_enableVertexGradient: 0
m_colorMode: 3
m_fontColorGradient:
topLeft: {r: 1, g: 1, b: 1, a: 1}
topRight: {r: 1, g: 1, b: 1, a: 1}
bottomLeft: {r: 1, g: 1, b: 1, a: 1}
bottomRight: {r: 1, g: 1, b: 1, a: 1}
m_fontColorGradientPreset: {fileID: 0}
m_spriteAsset: {fileID: 0}
m_tintAllSprites: 0
m_StyleSheet: {fileID: 0}
m_TextStyleHashCode: -1183493901
m_overrideHtmlColors: 0
m_faceColor:
serializedVersion: 2
rgba: 4294967295
m_fontSize: 12
m_fontSizeBase: 12
m_fontWeight: 400
m_enableAutoSizing: 0
m_fontSizeMin: 18
m_fontSizeMax: 72
m_fontStyle: 0
m_HorizontalAlignment: 1
m_VerticalAlignment: 256
m_textAlignment: 65535
m_characterSpacing: 0
m_wordSpacing: 0
m_lineSpacing: 0
m_lineSpacingMax: 0
m_paragraphSpacing: 0
m_charWidthMaxAdj: 0
m_TextWrappingMode: 1
m_wordWrappingRatios: 0.4
m_overflowMode: 0
m_linkedTextComponent: {fileID: 0}
parentLinkedComponent: {fileID: 0}
m_enableKerning: 0
m_ActiveFontFeatures: 6e72656b
m_enableExtraPadding: 0
checkPaddingRequired: 0
m_isRichText: 1
m_EmojiFallbackSupport: 1
m_parseCtrlCharacters: 1
m_isOrthographic: 1
m_isCullingEnabled: 0
m_horizontalMapping: 0
m_verticalMapping: 0
m_uvLineOffset: 0
m_geometrySortingOrder: 0
m_IsTextObjectScaleStatic: 0
m_VertexBufferAutoSizeReduction: 0
m_useMaxVisibleDescender: 1
m_pageToDisplay: 1
m_margin: {x: 0, y: 0, z: 0, w: 0}
m_isUsingLegacyAnimationComponent: 0
m_isVolumetricText: 0
m_hasFontAssetChanged: 0
m_baseMaterial: {fileID: 0}
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
--- !u!1 &3798696889335606665
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 6765948511015989509}
- component: {fileID: 2825211744860677609}
- component: {fileID: 7215137539608397115}
m_Layer: 5
m_Name: shadow
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &6765948511015989509
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3798696889335606665}
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: 0
m_Children: []
m_Father: {fileID: 3374630250525829090}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 5, y: -5}
m_SizeDelta: {x: 10, y: 10}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &2825211744860677609
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3798696889335606665}
m_CullTransparentMesh: 1
--- !u!114 &7215137539608397115
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3798696889335606665}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 0, g: 0, b: 0, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 887145076, guid: 4cf3568ca3f55f64cb11447d139d7a3d, type: 3}
m_Type: 1
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!1 &7314511769243682470
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 3374630250525829090}
- component: {fileID: 5545478218830425297}
m_Layer: 5
m_Name: AlarmIcon
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &3374630250525829090
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7314511769243682470}
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: 0
m_Children:
- {fileID: 6765948511015989509}
- {fileID: 5399931818953919527}
- {fileID: 8602533637707951236}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 100, y: 150}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &5545478218830425297
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7314511769243682470}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: cd395cde180b3094a91d87699139bbcf, type: 3}
m_Name:
m_EditorClassIdentifier:
text: {fileID: 2018211268787215362}
--- !u!1 &8817008079317718099
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 5399931818953919527}
- component: {fileID: 1290445261252787132}
- component: {fileID: 2283905710529375295}
m_Layer: 5
m_Name: bg
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &5399931818953919527
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8817008079317718099}
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: 0
m_Children: []
m_Father: {fileID: 3374630250525829090}
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!222 &1290445261252787132
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8817008079317718099}
m_CullTransparentMesh: 1
--- !u!114 &2283905710529375295
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8817008079317718099}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: -895992892, guid: 73d757b5d1b754245969af12daf01e78, type: 3}
m_Type: 1
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: e9acc8c9a93a2b5409fc01661660b217
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -208,6 +208,127 @@ MonoBehaviour:
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!1 &1844102171940980497
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1010396666164740167}
- component: {fileID: 380188952969839968}
- component: {fileID: 170663812404971233}
- component: {fileID: 8791400826399403740}
m_Layer: 5
m_Name: CopyButton
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &1010396666164740167
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1844102171940980497}
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: 0
m_Children:
- {fileID: 5059140281209684741}
m_Father: {fileID: 4802890858156259540}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 1, y: 1}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: -20, y: -2}
m_SizeDelta: {x: 30, y: 15}
m_Pivot: {x: 1, y: 1}
--- !u!222 &380188952969839968
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1844102171940980497}
m_CullTransparentMesh: 1
--- !u!114 &170663812404971233
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1844102171940980497}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 0.26415092, g: 0.26415092, b: 0.26415092, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: -27720893, guid: e5829cbc100001646956a9c3ed4e33c5, type: 3}
m_Type: 1
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!114 &8791400826399403740
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1844102171940980497}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Navigation:
m_Mode: 3
m_WrapAround: 0
m_SelectOnUp: {fileID: 0}
m_SelectOnDown: {fileID: 0}
m_SelectOnLeft: {fileID: 0}
m_SelectOnRight: {fileID: 0}
m_Transition: 1
m_Colors:
m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
m_ColorMultiplier: 1
m_FadeDuration: 0.1
m_SpriteState:
m_HighlightedSprite: {fileID: 0}
m_PressedSprite: {fileID: 0}
m_SelectedSprite: {fileID: 0}
m_DisabledSprite: {fileID: 0}
m_AnimationTriggers:
m_NormalTrigger: Normal
m_HighlightedTrigger: Highlighted
m_PressedTrigger: Pressed
m_SelectedTrigger: Selected
m_DisabledTrigger: Disabled
m_Interactable: 1
m_TargetGraphic: {fileID: 170663812404971233}
m_OnClick:
m_PersistentCalls:
m_Calls: []
--- !u!1 &4884066667293879814
GameObject:
m_ObjectHideFlags: 0
@@ -316,6 +437,7 @@ RectTransform:
- {fileID: 7383335062191980062}
- {fileID: 2993317773174661490}
- {fileID: 6308980257678838100}
- {fileID: 1010396666164740167}
- {fileID: 6685372230643413407}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
@@ -346,6 +468,7 @@ MonoBehaviour:
m_EditorClassIdentifier:
text: {fileID: 4202672738064507978}
closeButton: {fileID: 5925304901667948221}
copyButton: {fileID: 8791400826399403740}
screenOffset: {x: 10, y: 10}
menuBarHeight: 70
--- !u!1 &7346391167643616437
@@ -484,3 +607,139 @@ MonoBehaviour:
m_hasFontAssetChanged: 0
m_baseMaterial: {fileID: 0}
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
--- !u!1 &7999202329282239406
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 5059140281209684741}
- component: {fileID: 8801292156144245076}
- component: {fileID: 6403130881974049321}
m_Layer: 5
m_Name: Text (TMP)
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &5059140281209684741
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7999202329282239406}
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: 0
m_Children: []
m_Father: {fileID: 1010396666164740167}
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!222 &8801292156144245076
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7999202329282239406}
m_CullTransparentMesh: 1
--- !u!114 &6403130881974049321
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7999202329282239406}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text: Copy
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 08cebd004d97ca742ac80400f37f4eed, type: 2}
m_sharedMaterial: {fileID: 4860575619018115804, guid: 08cebd004d97ca742ac80400f37f4eed, type: 2}
m_fontSharedMaterials: []
m_fontMaterial: {fileID: 0}
m_fontMaterials: []
m_fontColor32:
serializedVersion: 2
rgba: 4294967295
m_fontColor: {r: 1, g: 1, b: 1, a: 1}
m_enableVertexGradient: 0
m_colorMode: 3
m_fontColorGradient:
topLeft: {r: 1, g: 1, b: 1, a: 1}
topRight: {r: 1, g: 1, b: 1, a: 1}
bottomLeft: {r: 1, g: 1, b: 1, a: 1}
bottomRight: {r: 1, g: 1, b: 1, a: 1}
m_fontColorGradientPreset: {fileID: 0}
m_spriteAsset: {fileID: 0}
m_tintAllSprites: 0
m_StyleSheet: {fileID: 0}
m_TextStyleHashCode: -1183493901
m_overrideHtmlColors: 0
m_faceColor:
serializedVersion: 2
rgba: 4294967295
m_fontSize: 8
m_fontSizeBase: 8
m_fontWeight: 400
m_enableAutoSizing: 0
m_fontSizeMin: 18
m_fontSizeMax: 72
m_fontStyle: 0
m_HorizontalAlignment: 2
m_VerticalAlignment: 512
m_textAlignment: 65535
m_characterSpacing: 0
m_wordSpacing: 0
m_lineSpacing: 0
m_lineSpacingMax: 0
m_paragraphSpacing: 0
m_charWidthMaxAdj: 0
m_TextWrappingMode: 1
m_wordWrappingRatios: 0.4
m_overflowMode: 0
m_linkedTextComponent: {fileID: 0}
parentLinkedComponent: {fileID: 0}
m_enableKerning: 0
m_ActiveFontFeatures: 6e72656b
m_enableExtraPadding: 0
checkPaddingRequired: 0
m_isRichText: 1
m_EmojiFallbackSupport: 1
m_parseCtrlCharacters: 1
m_isOrthographic: 1
m_isCullingEnabled: 0
m_horizontalMapping: 0
m_verticalMapping: 0
m_uvLineOffset: 0
m_geometrySortingOrder: 0
m_IsTextObjectScaleStatic: 0
m_VertexBufferAutoSizeReduction: 0
m_useMaxVisibleDescender: 1
m_pageToDisplay: 1
m_margin: {x: 0, y: 0, z: 0, w: 0}
m_isUsingLegacyAnimationComponent: 0
m_isVolumetricText: 0
m_hasFontAssetChanged: 0
m_baseMaterial: {fileID: 0}
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}

View File

@@ -926,6 +926,51 @@ RectTransform:
m_CorrespondingSourceObject: {fileID: 8849628700159893901, guid: 5a23b2bd2bd04c045878e1a06b3b9aa2, type: 3}
m_PrefabInstance: {fileID: 769109585}
m_PrefabAsset: {fileID: 0}
--- !u!1 &784978156
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 784978157}
- component: {fileID: 784978158}
m_Layer: 0
m_Name: AlarmManager
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &784978157
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 784978156}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 429.2735, y: 488.27496, z: 49.768143}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &784978158
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 784978156}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 6765c5d969530e44cbe4fc91d5e52ca1, type: 3}
m_Name:
m_EditorClassIdentifier:
alarmUIPrefab: {fileID: 1828762385695490929, guid: e8b88e69e607ee448806427e91440a8e, type: 3}
--- !u!1 &832575517
GameObject:
m_ObjectHideFlags: 0
@@ -980,6 +1025,115 @@ RectTransform:
m_CorrespondingSourceObject: {fileID: 5064510836022735693, guid: 27ddee6261f49584c8634ba7c5f4ae46, type: 3}
m_PrefabInstance: {fileID: 8261569461642068635}
m_PrefabAsset: {fileID: 0}
--- !u!1 &1091201604
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1091201608}
- component: {fileID: 1091201607}
- component: {fileID: 1091201606}
- component: {fileID: 1091201605}
m_Layer: 0
m_Name: Plane
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!64 &1091201605
MeshCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1091201604}
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_IsTrigger: 0
m_ProvidesContacts: 0
m_Enabled: 1
serializedVersion: 5
m_Convex: 0
m_CookingOptions: 30
m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
--- !u!23 &1091201606
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1091201604}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!33 &1091201607
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1091201604}
m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
--- !u!4 &1091201608
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1091201604}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 1000, y: 0, z: 1000}
m_LocalScale: {x: 300, y: 1, z: 300}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1101428664
GameObject:
m_ObjectHideFlags: 0
@@ -2069,8 +2223,10 @@ SceneRoots:
- {fileID: 832575519}
- {fileID: 632541407}
- {fileID: 27812499}
- {fileID: 784978157}
- {fileID: 2030316712}
- {fileID: 483439351}
- {fileID: 495653798}
- {fileID: 88083293}
- {fileID: 1840728471}
- {fileID: 1091201608}

View File

@@ -52,6 +52,7 @@ namespace SampleProject
mqttPipeLine = new MQTTPipeLine("localhost", 1883);
mqttPipeLine.AddTopic("AGV");
//mqttPipeLine.AddTopic("ALARM");
mqttPipeLine.Execute();
//10초 후 정지

View File

@@ -1,6 +1,7 @@
using System;
using UnityEngine;
using UVC.Core;
using UVC.Factory.Alarm;
using UVC.UI.Tooltip;
namespace SampleProject
@@ -21,6 +22,17 @@ namespace SampleProject
AppMain.Instance.Initialized += OnAppInitialized;
}
/// <summary>
/// AGV 관리자가 생성될 때 발생하는 이벤트를 처리합니다.
/// </summary>
/// <remarks>이 메서드는 AGV 관리자 생성과 관련된 필요한 초기화 또는 설정 작업을 수행하기 위한 것입니다.
/// 내부적으로 호출되며 외부 코드에서 직접 사용하도록 의도된 것이 아닙니다.
///</remarks>
internal void OnAGVManagerCreated()
{
AlarmManager.Instance.Run();
}
private void OnAppInitialized()
{
if (Initialized != null)

View File

@@ -3,6 +3,7 @@ using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using UnityEngine;
namespace UVC.Data
{
@@ -11,12 +12,21 @@ namespace UVC.Data
/// </summary>
public class DataArray : List<DataObject>, IDataObject
{
private bool isInPool = false;
/// <summary>
/// 이 객체가 객체 풀에 있는지 여부를 나타냅니다.
/// 중복 반환을 방지하기 위해 DataArrayPool에서 내부적으로 사용됩니다.
/// </summary>
internal bool IsInPool { get; set; } = false;
internal bool IsInPool { get => isInPool;
set
{
isInPool = value;
foreach (var item in this)
{
item.IsInPool = value; // 내부 DataObject도 풀에 있다고 표시합니다.
}
}
}
// 추가 된 항목 목록
protected List<DataObject> addedList = new List<DataObject>();
@@ -77,6 +87,26 @@ namespace UVC.Data
}
}
public DataArray(System.IO.Stream jsonStream)
{
if (jsonStream == null)
throw new ArgumentNullException(nameof(jsonStream));
// 스트림 처리 최적화를 위해 청크 단위로 읽을 수 있지만,
// 현재는 Newtonsoft.Json의 기본 역직렬화 사용
using (var reader = new Newtonsoft.Json.JsonTextReader(new System.IO.StreamReader(jsonStream)))
{
// 청크 읽기 설정 - 메모리 사용량 최적화
reader.SupportMultipleContent = true;
var serializer = new Newtonsoft.Json.JsonSerializer();
var sourceObject = serializer.Deserialize<JArray>(reader);
// 수정된 코드: 생성자를 호출하는 대신 JArray 메서드를 사용
if (sourceObject != null) FromJArray(sourceObject);
}
}
/// <summary>
/// JArray로부터 DataArray를 생성하는 생성자
/// </summary>
@@ -191,8 +221,7 @@ namespace UVC.Data
// 기존 변경 추적 목록을 초기화합니다.
ClearTrackedChanges();
// 성능 향상을 위해 ID를 키로 사용하는 사전을 생성합니다.
var thisDict = this.ToDictionary(item => item.Id);
var otherDict = otherArray.ToDictionary(item => item.Id);
@@ -208,7 +237,7 @@ namespace UVC.Data
// 추가된 항목 확인 (다른 배열에는 있지만 현재 배열에는 없는 항목)
foreach (var id in otherIds.Where(id => !thisIds.Contains(id)))
{
addedList.Add(otherDict[id]);
addedList.Add(otherDict[id].Copy(fromPool: false));
}
// 수정된 항목 확인 (양쪽 모두에 있지만 내용이 다른 항목)
@@ -230,6 +259,12 @@ namespace UVC.Data
// [성능 개선] RemoveAll과 HashSet을 사용하여 제거 작업의 효율성을 높입니다.
if (removedList.Count > 0)
{
// 제거될 객체들을 먼저 풀에 반환합니다.
foreach (var item in removedList)
{
if (item.IsInPool) item.ReturnToPool();
}
// 그 다음 리스트에서 제거합니다.
var removedItemIds = new HashSet<string>(removedList.Select(i => i.Id));
this.RemoveAll(item => removedItemIds.Contains(item.Id));
}
@@ -289,11 +324,10 @@ namespace UVC.Data
/// </summary>
public new void Clear()
{
if (Count > 0)
{
ReturnToDataObjectPool();
base.Clear();
}
// 내부 리스트와 변경 추적 리스트를 모두 비웁니다.
ReturnToDataObjectPool();
base.Clear();
ClearTrackedChanges();
}
/// <summary>
@@ -301,16 +335,12 @@ namespace UVC.Data
/// </summary>
public void Reset()
{
// 포함된 DataObject들을 먼저 풀에 반환합니다.
ReturnToDataObjectPool();
// 내부 리스트와 변경 추적 리스트를 모두 비웁니다.
base.Clear();
ClearTrackedChanges();
Clear();
}
public void ReturnToPool()
{
// 포함된 DataObject들을 먼저 풀에 반환합니다.
DataArrayPool.Return(this);
}
@@ -322,7 +352,7 @@ namespace UVC.Data
{
foreach (var item in this)
{
DataObjectPool.Return(item);
item.ReturnToPool();
}
}
@@ -340,37 +370,53 @@ namespace UVC.Data
/// 동일한 상태와 값을 가진 현재 데이터 객체의 새 인스턴스를 생성합니다.
/// </summary>
/// <remarks>복제된 객체는 원본 객체와 독립적이므로, 한 객체를 변경해도 다른 객체에는 영향을 미치지 않습니다.
///</remarks>
/// </remarks>
/// <param name="fromPool">객체 풀에서 복제할지 여부를 지정합니다. 기본값은 true입니다.</param>
/// <returns>현재 객체의 복사본인 새 <see cref="IDataObject"/> 인스턴스를 반환합니다.</returns>
public IDataObject Clone()
public IDataObject Clone(bool fromPool = true)
{
return Copy(fromPool);
}
/// <summary>
/// 현재 <see cref="DataArray"/> 인스턴스의 요소 및 관련 상태를 포함한 깊은 복사본을 생성합니다.
///
/// </summary>
/// <remarks>이 메서드는 현재 배열에 있는 모든 <see cref="DataObject"/> 요소의 깊은 복사본을 포함하는 새로운 <see cref="DataArray"/> 인스턴스를 반환합니다.
/// 복사된 인스턴스는 내부 목록에서 추적하는 추가, 제거 또는 수정된 요소를 포함하여 원본 인스턴스의 상태도 복제합니다.
/// </remarks>
/// <param name="fromPool">객체 풀에서 복제할지 여부를 지정합니다. 기본값은 true입니다.</param>
/// <returns>현재 인스턴스의 깊은 복사본인 새로운 <see cref="DataArray"/> 인스턴스를 반환합니다.</returns>
public DataArray Copy(bool fromPool = true)
{
// 풀에서 새 DataArray 인스턴스를 가져옵니다.
var clone = DataArrayPool.Get();
clone.FromCapacity(this.Count);
DataArray clone;
if(fromPool) clone = DataArrayPool.Get();
else clone = new DataArray();
// 배열의 모든 DataObject를 순회하며 각각을 복제합니다.
foreach (var item in this)
{
// DataObject의 Clone 메서드를 호출하여 깊은 복사를 수행하고,
// base.Add를 사용해 추적 로직 없이 직접 추가합니다.
if (item.Clone() is DataObject clonedItem)
DataObject clonedItem = item.Copy(fromPool);
clone.Add(clonedItem);
if (addedList.Contains(item))
{
clone.Add(clonedItem);
if (addedList.Contains(item))
{
clone.addedList.Add(clonedItem);
}
else if (removedList.Contains(item))
{
clone.removedList.Add(clonedItem);
}
else if (modifiedList.Contains(item))
{
clone.modifiedList.Add(clonedItem);
}
clone.addedList.Add(clonedItem);
}
else if (removedList.Contains(item))
{
clone.removedList.Add(clonedItem);
}
else if (modifiedList.Contains(item))
{
clone.modifiedList.Add(clonedItem);
}
}
return clone;
}

View File

@@ -10,7 +10,7 @@ namespace UVC.Data
public static class DataArrayPool
{
private static readonly ConcurrentQueue<DataArray> _pool = new ConcurrentQueue<DataArray>();
private static int _maxPoolSize = 500; // DataArray는 DataObject보다 수가 적을 것이므로 기본값 조정
private static int _maxPoolSize = 100; // DataArray는 DataObject보다 수가 적을 것이므로 기본값 조정
// --- 통계용 필드 ---
private static int _inUseCount = 0;
@@ -18,15 +18,16 @@ namespace UVC.Data
private static int _poolMisses = 0;
private static readonly object _statsLock = new object();
/// <summary>
/// 풀의 최대 크기를 설정합니다.
/// </summary>
/// <param name="size">설정할 최대 크기</param>
public static void SetMaxPoolSize(int size)
static DataArrayPool()
{
_maxPoolSize = size > 0 ? size : 500;
// maxPoolSize만큼의 DataObject 인스턴스를 미리 생성하여 풀에 추가합니다.
for (int i = 0; i < _maxPoolSize; i++)
{
_pool.Enqueue(new DataArray() { IsInPool = true });
}
}
/// <summary>
/// 풀에서 DataArray 객체를 가져옵니다. 풀이 비어있으면 새로 생성합니다.
/// </summary>
@@ -50,17 +51,16 @@ namespace UVC.Data
{
int oldSize = _maxPoolSize;
_maxPoolSize += 100; // 증가량 조정
Debug.Log($"DataArrayPool size automatically increased from {oldSize} to {_maxPoolSize}. Peak usage: {_peakUsage}");
//Debug.Log($"DataArrayPool size automatically increased from {oldSize} to {_maxPoolSize}. Peak usage: {_peakUsage}");
}
}
}
if(_peakUsage % 100 == 0) Debug.Log($"DataArrayPool stats: {GetStats()}");
//if(_peakUsage % 10 == 0) Debug.Log($"DataArrayPool stats: {GetStats()}");
if (fromPool)
{
array.IsInPool = false;
array.Reset();
}
return fromPool ? array : new DataArray();
}
@@ -93,7 +93,7 @@ namespace UVC.Data
/// <returns>풀 통계 (최대 사용량, 현재 사용량, 풀 비어있을 때 생성 횟수, 현재 풀 크기)</returns>
public static string GetStats()
{
return $"Peak Usage: {_peakUsage}, In Use: {_inUseCount}, Misses: {_poolMisses}, Pooled: {_pool.Count}, Max Size: {_maxPoolSize}";
return $"최대 사용량: {_peakUsage}, 현재 사용량: {_inUseCount}, 풀 비어있을 때 생성 횟수: {_poolMisses}, 현재 풀 크기: {_pool.Count}, Max Size: {_maxPoolSize}";
}
/// <summary>

View File

@@ -49,12 +49,30 @@ namespace UVC.Data
/// </example>
public class DataObject : OrderedDictionary<string, object?>, IDataObject
{
private bool isInPool = false;
/// <summary>
/// 이 객체가 객체 풀에 있는지 여부를 나타냅니다.
/// 중복 반환을 방지하기 위해 DataObjectPool에서 내부적으로 사용됩니다.
/// </summary>
internal bool IsInPool { get; set; } = false;
internal bool IsInPool
{
get => isInPool;
set
{
isInPool = value;
foreach (var item in this)
{
if(item.Value is DataObject dataObject)
{
dataObject.isInPool = value; // 내부 DataObject도 풀에 있다고 표시합니다.
}
else if(item.Value is DataArray dataArray)
{
dataArray.IsInPool = value; // 내부 DataArray도 풀에 있다고 표시합니다.
}
}
}
}
/// <summary>
/// 객체의 고유 식별자를 나타내는 속성입니다. DataArray에서 데이터를 식별하는 데 사용됩니다.
@@ -104,6 +122,30 @@ namespace UVC.Data
/// </summary>
public DataObject() { }
public DataObject(string jsonString): this(JObject.Parse(jsonString))
{
}
public DataObject(System.IO.Stream jsonStream)
{
if (jsonStream == null)
throw new ArgumentNullException(nameof(jsonStream));
// 스트림 처리 최적화를 위해 청크 단위로 읽을 수 있지만,
// 현재는 Newtonsoft.Json의 기본 역직렬화 사용
using (var reader = new Newtonsoft.Json.JsonTextReader(new System.IO.StreamReader(jsonStream)))
{
// 청크 읽기 설정 - 메모리 사용량 최적화
reader.SupportMultipleContent = true;
var serializer = new Newtonsoft.Json.JsonSerializer();
var sourceObject = serializer.Deserialize<JObject>(reader);
// 수정된 코드: 생성자를 호출하는 대신 FromJObject 메서드를 사용
if(sourceObject != null) FromJObject(sourceObject);
}
}
/// <summary>
/// JObject로부터 데이터 객체를 생성합니다.
/// </summary>
@@ -270,6 +312,16 @@ namespace UVC.Data
if (hasChanged)
{
// 기존에 풀링 가능한 객체가 있었다면 풀에 반환합니다.
if (oldValue is DataObject oldDataObject)
{
if(oldDataObject.IsInPool) oldDataObject.ReturnToPool();
}
else if (oldValue is DataArray oldDataArray)
{
if (oldDataArray.IsInPool) oldDataArray.ReturnToPool();
}
// 기본 딕셔너리에 값을 설정합니다.
base[key] = value;
@@ -289,7 +341,7 @@ namespace UVC.Data
/// <param name="propertyName">속성 이름</param>
/// <param name="defaultValue">속성이 없거나 변환할 수 없는 경우 반환할 기본값</param>
/// <returns>변환된 정수 값 또는 기본값</returns>
public int GetInt(string propertyName, int defaultValue = 0)
public int? GetInt(string propertyName, int? defaultValue = null)
{
if (TryGetValue(propertyName, out object? value) && value != null)
{
@@ -321,7 +373,7 @@ namespace UVC.Data
/// <param name="propertyName">속성 이름</param>
/// <param name="defaultValue">속성이 없거나 변환할 수 없는 경우 반환할 기본값</param>
/// <returns>변환된 불리언 값 또는 기본값</returns>
public bool GetBool(string propertyName, bool defaultValue = false)
public bool? GetBool(string propertyName, bool? defaultValue = false)
{
if (TryGetValue(propertyName, out object? value) && value != null)
{
@@ -338,7 +390,7 @@ namespace UVC.Data
/// <param name="propertyName">속성 이름</param>
/// <param name="defaultValue">속성이 없거나 변환할 수 없는 경우 반환할 기본값</param>
/// <returns>변환된 부동 소수점 값 또는 기본값</returns>
public float GetFloat(string propertyName, float defaultValue = 0f)
public float? GetFloat(string propertyName, float? defaultValue = null)
{
if (TryGetValue(propertyName, out object? value) && value != null)
{
@@ -355,7 +407,7 @@ namespace UVC.Data
/// <param name="propertyName">속성 이름</param>
/// <param name="defaultValue">속성이 없거나 변환할 수 없는 경우 반환할 기본값</param>
/// <returns>변환된 더블 값 또는 기본값</returns>
public double GetDouble(string propertyName, double defaultValue = 0.0)
public double? GetDouble(string propertyName, double? defaultValue = null)
{
if (TryGetValue(propertyName, out object? value) && value != null)
{
@@ -372,7 +424,7 @@ namespace UVC.Data
/// <param name="propertyName">속성 이름</param>
/// <param name="defaultValue">속성이 없거나 변환할 수 없는 경우 반환할 기본값</param>
/// <returns>변환된 long 값 또는 기본값</returns>
public long GetLong(string propertyName, long defaultValue = 0L)
public long? GetLong(string propertyName, long? defaultValue = null)
{
if (TryGetValue(propertyName, out object? value) && value != null)
{
@@ -407,7 +459,7 @@ namespace UVC.Data
/// <param name="propertyName">속성 이름</param>
/// <param name="defaultValue">속성이 없거나 변환할 수 없는 경우 반환할 기본값</param>
/// <returns>변환된 열거형 값 또는 기본값</returns>
public T GetEnum<T>(string propertyName, T defaultValue = default) where T : Enum
public T? GetEnum<T>(string propertyName, T? defaultValue = default) where T : Enum
{
if (TryGetValue(propertyName, out object? value) && value != null)
{
@@ -572,9 +624,22 @@ namespace UVC.Data
/// <returns>제거 성공 여부</returns>
public new bool Remove(string key)
{
// 제거하기 전에 이전 값을 가져옵니다.
TryGetValue(key, out object? oldValue);
bool result = base.Remove(key);
if (result)
{
// 제거된 객체가 풀링 가능한 경우 풀에 반환합니다.
if (oldValue is DataObject dataObject)
{
if(dataObject.IsInPool) dataObject.ReturnToPool();
}
else if (oldValue is DataArray dataArray)
{
if (dataArray.IsInPool) dataArray.ReturnToPool();
}
// 변경 추적 목록에서도 제거합니다.
changedProperies.Remove(key);
}
@@ -586,6 +651,17 @@ namespace UVC.Data
/// </summary>
public void RemoveAll()
{
foreach (var value in Values.ToList())
{
if(value is DataObject dataObject)
{
dataObject.ReturnToPool(); // DataObject를 풀에 반환합니다.
}
else if (value is DataArray dataArray)
{
dataArray.ReturnToPool(); // DataArray를 풀에 반환합니다.
}
}
base.Clear();
changedProperies.Clear();
}
@@ -606,10 +682,27 @@ namespace UVC.Data
/// </summary>
/// <remarks>복제된 객체는 원본 객체와 독립적이므로, 한 객체를 변경해도 다른 객체에는 영향을 미치지 않습니다.
///</remarks>
/// <param name="fromPool">복제된 객체가 풀에 있는지 여부를 나타냅니다. 기본값은 true입니다.</param>
/// <returns>현재 객체의 복사본인 새 <see cref="IDataObject"/> 인스턴스를 반환합니다.</returns>
public IDataObject Clone()
public IDataObject Clone(bool fromPool = true)
{
var clone = DataObjectPool.Get();
return Copy(fromPool);
}
/// <summary>
/// 모든 키-값 쌍을 포함하여 현재 <see cref="DataObject"/> 인스턴스의 깊은 복사본을 생성합니다.
/// </summary>
/// <remarks>이 메서드는 <see cref="DataObject"/>와 그 내용의 깊은 복사본을 수행합니다.
/// 키-값 쌍의 값이 다른 <see cref="DataObject"/> 또는 <see cref="DataArray"/>인 경우,
/// 메서드는 해당 객체를 재귀적으로 복제합니다. 기본 유형 및 기타 복제 불가능한 값은
/// 직접 복사됩니다.</remarks>
/// <param name="fromPool">복제된 객체가 풀에 있는지 여부를 나타냅니다. 기본값은 true입니다.</param>
/// <returns>현재 객체의 깊은 복사본인 새로운 <see cref="DataObject"/> 인스턴스를 반환합니다.</returns>
public DataObject Copy(bool fromPool = true)
{
DataObject clone = DataObjectPool.Get();
if(fromPool) clone = DataObjectPool.Get();
else clone = new DataObject();
clone.Name = Name;
clone.IdKey = IdKey;
@@ -619,13 +712,13 @@ namespace UVC.Data
object? clonedValue;
switch (pair.Value)
{
// 값이 DataObject인 경우, 재귀적으로 Clone을 호출합니다.
// 값이 DataObject인 경우, 재귀적으로 Copy을 호출합니다.
case DataObject dataObjectValue:
clonedValue = dataObjectValue.Clone();
clonedValue = dataObjectValue.Copy(fromPool);
break;
// 값이 DataArray인 경우, 재귀적으로 Clone을 호출합니다.
case DataArray dataArrayValue:
clonedValue = dataArrayValue.Clone();
clonedValue = dataArrayValue.Copy(fromPool);
break;
// 그 외의 경우 (primitive 타입 등)는 그대로 복사합니다.
default:
@@ -684,7 +777,23 @@ namespace UVC.Data
|| (this[keyValue.Key] != null && keyValue.Value == null)
|| (this[keyValue.Key] != null && keyValue.Value != null && !this[keyValue.Key]!.Equals(keyValue.Value)))
{
this[keyValue.Key] = keyValue.Value;
//참조 타입과 값 타입 구분하여 복사
object? valueToSet;
switch (keyValue.Value)
{
// DataObject나 DataArray는 풀을 사용하지 않는 깊은 복사를 수행합니다.
case DataObject dataObjectValue:
valueToSet = dataObjectValue.Copy(fromPool: false);
break;
case DataArray dataArrayValue:
valueToSet = dataArrayValue.Copy(fromPool: false);
break;
// 그 외 타입은 값을 그대로 할당합니다.
default:
valueToSet = keyValue.Value;
break;
}
this[keyValue.Key] = valueToSet;
changedProperies.Add(keyValue.Key);
}
}

View File

@@ -22,7 +22,7 @@ namespace UVC.Data
/// obj["age"] = 30;
///
/// // 작업 완료 후 풀에 반환
/// DataObjectPool.Return(obj);
/// obj.ReturnToPool();
///
/// // 풀 통계 확인
/// ULog.Debug(DataObjectPool.GetStats());
@@ -33,27 +33,26 @@ namespace UVC.Data
/// <summary>
/// DataObject 인스턴스를 저장하는 스레드 안전 큐입니다.
/// </summary>
private static ConcurrentQueue<DataObject> dataObjectPool = new ConcurrentQueue<DataObject>();
private static ConcurrentQueue<DataObject> pool = new ConcurrentQueue<DataObject>();
/// <summary>
/// 풀의 최대 크기입니다. 이 크기를 초과하는 객체는 풀에 저장되지 않습니다.
/// </summary>
private static int maxPoolSize = 1000;
private static int maxPoolSize = 4000;
// --- 통계용 필드 ---
private static int _inUseCount = 0;
private static int _peakUsage = 0;
private static int _poolMisses = 0;
private static int _inUseCount = 0; // 현재 사용 중인 DataObject 인스턴스의 수
private static int _peakUsage = 0; // 최대 사용량 기록
private static int _poolMisses = 0; // 풀에서 객체를 찾지 못하고 새로 생성한 횟수
private static readonly object _statsLock = new object();
/// <summary>
/// 풀의 최대 크기를 설정합니다.
/// </summary>
/// <param name="size">설정할 최대 크기</param>
public static void SetMaxPoolSize(int size)
static DataObjectPool()
{
maxPoolSize = size > 0 ? size : 1000;
// maxPoolSize만큼의 DataObject 인스턴스를 미리 생성하여 풀에 추가합니다.
for (int i = 0; i < maxPoolSize; i++)
{
pool.Enqueue(new DataObject() { IsInPool = true });
}
}
/// <summary>
@@ -63,7 +62,7 @@ namespace UVC.Data
/// <returns>재사용 가능한 DataObject 인스턴스</returns>
public static DataObject Get()
{
bool fromPool = dataObjectPool.TryDequeue(out var obj);
bool fromPool = pool.TryDequeue(out var obj);
lock (_statsLock)
{
@@ -82,17 +81,16 @@ namespace UVC.Data
{
int oldSize = maxPoolSize;
maxPoolSize += 1000;
Debug.Log($"DataObjectPool size automatically increased from {oldSize} to {maxPoolSize}. Peak usage: {_peakUsage}");
//Debug.Log($"DataObjectPool size automatically increased from {oldSize} to {maxPoolSize}. Peak usage: {_peakUsage}");
}
}
}
if (_peakUsage % 100 == 0) Debug.Log($"DataObjectPool stats: {GetStats()}");
//if (_peakUsage % 100 == 0) Debug.Log($"DataObjectPool stats: {GetStats()}");
if (fromPool)
{
obj.IsInPool = false;
obj.Reset();
}
return fromPool ? obj : new DataObject();
}
@@ -111,11 +109,11 @@ namespace UVC.Data
_inUseCount--;
}
if (dataObjectPool.Count < maxPoolSize)
if (pool.Count < maxPoolSize)
{
obj.Reset(); // 재사용 전 완벽한 초기화
obj.IsInPool = true;
dataObjectPool.Enqueue(obj);
pool.Enqueue(obj);
}
// 풀이 가득 차면 객체는 풀에 추가되지 않고 GC 대상이 됩니다.
}
@@ -126,7 +124,7 @@ namespace UVC.Data
/// <returns>풀 통계 (최대 사용량, 현재 사용량, 풀 비어있을 때 생성 횟수, 현재 풀 크기)</returns>
public static string GetStats()
{
return $"Peak Usage: {_peakUsage}, In Use: {_inUseCount}, Misses: {_poolMisses}, Pooled: {dataObjectPool.Count}, Max Size: {maxPoolSize}";
return $"최대 사용량: {_peakUsage}, 현재 사용량: {_inUseCount}, 풀 비어있을 때 생성 횟수: {_poolMisses}, 현재 풀 크기: {pool.Count}, Max Size: {maxPoolSize}";
}
/// <summary>

View File

@@ -69,7 +69,8 @@ namespace UVC.Data
{
if (!dataObjects.ContainsKey(key))
{
var newData = dataObject.Clone();
var newData = dataObject.Clone(false);
dataObjects.Add(key, newData);
dataObject.MarkAllAsUpdated();
//UniTask.Post(() => NotifyDataUpdate(key, newData));

View File

@@ -327,6 +327,10 @@ namespace UVC.Data
mappedObject = info.DataMapper.Map(source);
}
}
else
{
mappedObject = new DataObject(result);
}
}
else if (result.StartsWith("["))
{
@@ -403,6 +407,11 @@ namespace UVC.Data
}
}
}
else
{
// DataMapper가 없는 경우, JArray를 IDataObject로 변환
mappedObject = new DataArray(result);
}
}
}
catch (JsonException ex)

View File

@@ -41,8 +41,9 @@
/// </summary>
/// <remarks>복제된 객체는 원본 객체와 독립적이므로, 한 객체를 변경해도 다른 객체에는 영향을 미치지 않습니다.
///</remarks>
/// <param name="fromPool">객체 풀에서 복제할지 여부를 지정합니다. 기본값은 true입니다.</param>
/// <returns>현재 객체의 복사본인 새 <see cref="IDataObject"/> 인스턴스를 반환합니다.</returns>
public IDataObject Clone();
public IDataObject Clone(bool fromPool = true);
/// <summary>

View File

@@ -198,6 +198,7 @@ namespace UVC.Data
// 메시지 처리를 백그라운드 스레드에서 실행하여 메인 스레드 부하를 줄입니다.
UniTask.RunOnThreadPool(() =>
{
// 토픽이 infoList와 readyHandlerList에 존재하고, 준비 상태가 true인 경우에만 처리합니다.
if (infoList.ContainsKey(topic))
{
MQTTPipeLineInfo info = infoList[topic];
@@ -211,7 +212,14 @@ namespace UVC.Data
{
JObject source = JObject.Parse(message);
if (info.Validator != null && !info.Validator.IsValid(source)) return;
if (info.DataMapper != null) mappedObject = info.DataMapper.Map(source);
if (info.DataMapper != null)
{
mappedObject = info.DataMapper.Map(source);
}
else {
// DataMapper가 없으면 JObject를 IDataObject로 변환
mappedObject = new DataObject(source);
}
}
else if (message.StartsWith("["))
{
@@ -223,7 +231,15 @@ namespace UVC.Data
if (validSource == null || validSource.Count == 0) return;
source = validSource;
}
if (info.DataMapper != null) mappedObject = info.DataMapper.Map(source);
if (info.DataMapper != null)
{
mappedObject = info.DataMapper.Map(source);
}
else
{
// DataMapper가 없으면 JArray를 IDataObject로 변환
mappedObject = new DataArray(source);
}
}
if (mappedObject == null) return;

View File

@@ -1,81 +1,197 @@
using System;
#nullable enable
using SampleProject;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UVC.Factory;
using UVC.Core;
using UVC.Data;
using UVC.Extention;
using UVC.Factory.Component;
namespace UVC.Factory.Alarm
{
public class AlarmManager : MonoBehaviour
public class AlarmManager : SingletonScene<AlarmManager>
{
public static AlarmManager Instance { get; private set; }
[Tooltip("알람 UI 프리팹입니다. 이 프리팹은 알람 정보를 표시하는 UI 요소를 포함해야 합니다.")]
public GameObject alarmUIPrefab; // 알람 UI 프리팹 (아래에서 설명)
private Dictionary<string, AlarmUIController> activeAlarmUIs = new Dictionary<string, AlarmUIController>();
// FactoryDataManager에서 찾을 수 있도록 참조를 저장
private FactoryObjectManager dataManager;
private FactoryObjectManager? dataManager;
void Awake()
/// <summary>
/// AlarmManager의 초기화 메서드입니다.
/// Awake 메서드에서 호출되며, MonoBehaviour가 생성될 때 한 번만 실행됩니다.
/// </summary>
protected override void Init()
{
Instance = this;
SceneMain.Instance.Initialized += OnSceneInitialized;
}
void Start()
private void OnSceneInitialized()
{
dataManager = FactoryObjectManager.Instance;
// 예시: FactoryDataManager의 알람 이벤트에 구독
// dataManager.OnAlarmReceived += HandleNewAlarm;
// dataManager.OnAlarmCleared += HandleClearedAlarm;
}
public void HandleNewAlarm(AlarmData data)
/// <summary>
/// Alarm 데이터를 수신하기 위한 MQTT 파이프라인을 설정합니다.
/// </summary>
public void Run()
{
//데이터를 어떤 형식으로 받을지 정의합니다.
var dataMask = new DataMask();
dataMask.ObjectName = "Alarm"; // Alarm 객체의 이름을 설정합니다.
dataMask.ObjectIdKey = "ID"; // Alarm의 고유 식별자로 사용할 키를 설정합니다.
dataMask["ID"] = "";
dataMask["ALARM_TYPE"] = "";
dataMask["LEVEL"] = "";
dataMask["LOGISTIC"] = "";
dataMask["STATE"] = "";
dataMask["MESSAGE"] = "";
dataMask["CODE"] = "";
dataMask["ICON"] = "";
dataMask["MACHINENAME"] = "";
dataMask["SHOPNAME"] = "";
dataMask["TRANSPORT_EQP_NAME"] = "";
dataMask["TRANSPORT_UNIT_NAME"] = "";
dataMask["TRANSPORT_EQP_ID"] = "";
dataMask["TRANSPORT_UNIT_ID"] = "";
dataMask["SET_TIME"] = DateTime.Now;
dataMask["UPDATE_TIME"] = DateTime.Now;
dataMask["TIMESTAMP"] = DateTime.Now;
// MQTT 파이프라인 정보를 생성합니다.
// 'ALARM' 토픽을 구독하고, 받은 데이터는 위에서 정의한 dataMask로 매핑하며,
// 데이터 유효성 검사를 위해 DataValidator를 설정합니다.
// 데이터가 업데이트되면 OnUpdateData 메서드를 호출하여 처리합니다.
DataValidator validator = new DataValidator();
validator.AddValidator("MACHINENAME", value => value != null);
var pipelineInfo = new MQTTPipeLineInfo("ALARM")
.setDataMapper(new DataMapper(dataMask))
.setValidator(validator)
.setHandler(OnUpdateData);
// 생성한 파이프라인 정보를 전역 MQTT 파이프라인에 추가합니다.
AppMain.Instance.MQTTPipeLine.Add(pipelineInfo);
}
/// <summary>
/// 데이터 수신 시 호출되는 공개 핸들러입니다.
/// 수신된 ALARM 데이터 배열을 비동기적으로 처리하여 씬에 반영합니다.
/// 추가, 제거, 수정된 ALARM 데이터를 각각 구분하여 처리합니다.
/// </summary>
/// <param name="data">수신된 데이터 객체 (DataArray 형태)</param>
public void OnUpdateData(IDataObject? data)
{
if (data == null) return;
DataArray? arr = data as DataArray;
if (arr == null) return;
if (arr.Count == 0)
{
arr.ReturnToPool();
return;
}
// 데이터 배열에서 추가, 제거, 수정된 항목 리스트를 가져옵니다.
var AddedItems = arr.AddedItems;
var RemovedItems = arr.RemovedItems;
var ModifiedList = arr.ModifiedList;
Debug.Log($"AlarmManager OnUpdateData: Added={AddedItems.Count}, Removed={RemovedItems.Count}, Modified={ModifiedList.Count}");
// 새로 추가된 ALARM 처리
foreach (var item in AddedItems.ToList())
{
HandleNewAlarm(item.Copy());
}
// 제거된 ALARM 처리
foreach (var item in RemovedItems.ToList())
{
HandleClearedAlarm(item.Copy());
}
// 정보가 수정된 ALARM 처리
foreach (var item in ModifiedList.ToList())
{
HandleModifyAlarm(item.Copy());
}
arr.ReturnToPool();
}
/// <summary>
/// AlarmManager가 파괴될 때 호출됩니다.
/// MQTT 파이프라인에서 'ALARM' 핸들러를 제거하여 메모리 누수를 방지합니다.
/// </summary>
protected override void OnDestroy()
{
base.OnDestroy();
AppMain.Instance.MQTTPipeLine.Remove("ALARM");
}
public void HandleNewAlarm(DataObject data)
{
if (data.Id == null)
{
Debug.LogError($"New Alarm Received No data. {data}");
data.ReturnToPool();
return;
}
// 없으면 새로 생성
FactoryObject? targetObject = dataManager!.FindById(data.GetString("MACHINENAME")!);
if (targetObject != null)
{
GameObject newUIObject = Instantiate(alarmUIPrefab, transform); // 매니저 하위에 생성
AlarmUIController newUiController = newUIObject.GetComponent<AlarmUIController>();
newUiController.Initialize(targetObject, data);
activeAlarmUIs.Add(data.Id!, newUiController);
}
}
public void HandleModifyAlarm(DataObject data)
{
if (data.Id == null)
{
Debug.LogError($"Modify Alarm Received No data. {data}");
data.ReturnToPool();
return;
}
// 이미 해당 설비에 알람 UI가 떠 있는지 확인
if (activeAlarmUIs.TryGetValue(data.objectId, out AlarmUIController uiController))
if (activeAlarmUIs.TryGetValue(data.Id!, out AlarmUIController uiController))
{
// 있으면 기존 UI에 알람 정보만 추가
uiController.AddAlarm(data);
}
else
{
// 없으면 새로 생성
FactoryObject targetObject = dataManager.FindById(data.objectId);
if (targetObject != null)
{
GameObject newUIObject = Instantiate(alarmUIPrefab, transform); // 매니저 하위에 생성
AlarmUIController newUiController = newUIObject.GetComponent<AlarmUIController>();
newUiController.Initialize(targetObject, data);
activeAlarmUIs.Add(data.objectId, newUiController);
}
uiController.UpdateAlarm(data);
}
}
public void HandleClearedAlarm(AlarmData data)
public void HandleClearedAlarm(DataObject data)
{
if (activeAlarmUIs.TryGetValue(data.objectId, out AlarmUIController uiController))
if (data.Id.IsNullOrEmpty())
{
Debug.LogError($"Clear Alarm Received No data. {data}");
data.ReturnToPool();
return;
}
if (activeAlarmUIs.TryGetValue(data.Id!, out AlarmUIController uiController))
{
uiController.RemoveAlarm(data);
if (uiController.GetAlarmCount() == 0)
{
activeAlarmUIs.Remove(data.objectId);
activeAlarmUIs.Remove(data.Id!);
Destroy(uiController.gameObject);
}
data.ReturnToPool();
}
}
}
// 알람 데이터 전달용 클래스
public class AlarmData
{
public string objectId;
public string alarmId; // 각 알람의 고유 ID
public string message;
// ... 심각도, 시간 등 기타 정보
}
}

View File

@@ -1,23 +1,29 @@
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UVC.Data;
using UVC.Factory.Component;
using UVC.Util;
namespace UVC.Factory.Alarm
{
public class AlarmUIController : MonoBehaviour
public class AlarmUIController : MonoBehaviour, IPointerClickHandler
{
[Tooltip("알람 클러스터 뷰입니다.")]
[SerializeField] private GameObject clusterView;
[Tooltip("알람 개수를 표시하는 텍스트입니다.")]
[SerializeField] private TextMeshProUGUI alarmCountText;
[Tooltip("확장된 알람 뷰입니다. 개별 알람 아이콘을 표시합니다.")]
[SerializeField] private GameObject expandedView;
[Tooltip("개별 알람 아이콘 프리팹입니다. 이 프리팹은 개별 알람 정보를 표시하는 UI 요소를 포함해야 합니다.")]
[SerializeField] private GameObject singleAlarmIconPrefab; // 개별 알람 아이콘
private Transform targetObject;
private List<AlarmData> alarms = new List<AlarmData>();
private List<DataObject> alarms = new List<DataObject>();
private bool isExpanded = false;
public void Initialize(FactoryObject target, AlarmData initialAlarm)
public void Initialize(FactoryObject target, DataObject initialAlarm)
{
this.targetObject = target.transform;
AddAlarm(initialAlarm);
@@ -30,13 +36,32 @@ namespace UVC.Factory.Alarm
transform.rotation = Camera.main.transform.rotation;
}
public void AddAlarm(AlarmData alarm)
public void AddAlarm(DataObject alarm)
{
alarms.Add(alarm);
UpdateView();
}
public void RemoveAlarm(AlarmData alarm)
public void UpdateAlarm(DataObject alarm)
{
// 실제로는 alarmId로 찾아서 업데이트해야 함
int index = alarms.FindIndex(a => a.Id == alarm.Id);
if (index >= 0)
{
foreach (var key in alarm.Keys)
{
alarms[index][key] = alarm[key]; // 기존 알람 데이터 업데이트
}
alarm.ReturnToPool();
UpdateView();
}
else
{
Debug.LogWarning($"Alarm with ID {alarm.Id} not found for update.");
}
}
public void RemoveAlarm(DataObject alarm)
{
// 실제로는 alarmId로 찾아서 지워야 함
alarms.Remove(alarm);
@@ -123,5 +148,10 @@ namespace UVC.Factory.Alarm
}
public int GetAlarmCount() => alarms.Count;
public void OnPointerClick(PointerEventData eventData)
{
OnPointerClick();
}
}
}

View File

@@ -1,25 +1,56 @@
using UnityEngine;
using TMPro;
using UnityEngine;
using UVC.Data;
using UVC.Util;
namespace UVC.Factory.Alarm
{
public class SingleAlarmIcon : MonoBehaviour
{
private AlarmData myAlarmData;
[Tooltip("알람 내용을 표시하는 텍스트입니다.")]
[SerializeField] private TextMeshProUGUI text;
private DataObject data;
private Transform equipmentTransform;
public void SetData(AlarmData data, Transform equipment)
public void SetData(DataObject newData, Transform equipment)
{
myAlarmData = data;
if (data == null)
{
data = newData;
}
else
{
foreach (var keyValue in newData)
{
if (data.ContainsKey(keyValue.Key))
{
data[keyValue.Key] = keyValue.Value;
}
}
}
equipmentTransform = equipment;
// 아이콘 모양이나 색을 알람 심각도에 따라 변경 가능
if (text != null)
{
string combinedString = string.Empty;
foreach (var kvp in data)
{
// <indent> 태그를 사용하여 줄바꿈 시에도 정렬이 유지되도록 합니다.
combinedString += $"{kvp.Key}<pos=40%><indent=40%>{kvp.Value ?? "null"}</indent>\n";
}
combinedString = combinedString.TrimEnd('\n'); // 마지막 줄바꿈 제거
text.text = combinedString;
}
}
public void OnPointerClick()
{
// 클릭 시 해당 설비로 카메라 포커스
CameraController.Instance.FocusOnTarget(equipmentTransform, 3.0f);
Debug.Log($"알람 [{myAlarmData.message}]이 발생한 설비로 이동합니다.");
Debug.Log($"알람 [{data.GetString("MESSAGE")}]이 발생한 설비로 이동합니다.");
// 여기서 알람 상세정보 패널을 띄워도 좋음
}
}

View File

@@ -104,46 +104,63 @@ namespace UVC.Factory.Component
// 처음 데이터를 받는 경우, 받은 데이터로 즉시 위치를 설정합니다.
if (data == null)
{
float x = newData.GetFloat("X") * scaleFactor;
float y = newData.GetFloat("Y") * scaleFactor;
Quaternion rotation = Quaternion.Euler(0, newData.GetFloat("DEGREE"), 0);
float x = newData.GetFloat("X").Value * scaleFactor;
float y = newData.GetFloat("Y").Value * scaleFactor;
Quaternion rotation = Quaternion.Euler(0, newData.GetFloat("DEGREE").Value, 0);
transform.position = new Vector3(x, 0, y);
transform.rotation = rotation;
}
else // 이후 업데이트의 경우
{
// 새 데이터로부터 목표 위치와 회전값을 계산합니다.
float x = data.GetFloat("X") * scaleFactor;
float y = data.GetFloat("Y") * scaleFactor;
Quaternion rotation = Quaternion.Euler(0, data.GetFloat("DEGREE"), 0);
bool isTeleport = false;
float newX = (newData.ContainsKey("X") ? newData.GetFloat("X") : data.GetFloat("X")) * scaleFactor;
float newY = (newData.ContainsKey("Y") ? newData.GetFloat("Y") : data.GetFloat("Y")) * scaleFactor;
float newDegree = (newData.ContainsKey("DEGREE") ? newData.GetFloat("DEGREE") : data.GetFloat("DEGREE"));
Quaternion newRotation = Quaternion.Euler(0, newDegree, 0);
Vector3 newTargetPosition = new Vector3(x, transform.position.y, y);
Quaternion newTargetRotation = rotation;
if (x != newX || y != newY) newTargetPosition = new Vector3(newX, transform.position.y, newY);
if (rotation != newRotation) newTargetRotation = newRotation;
// 현재 위치와 새로운 목표 위치 사이의 거리를 계산합니다.
float distanceToTarget = Vector3.Distance(transform.position, newTargetPosition);
// 거리가 설정된 임계값을 초과하면, 보간을 건너뛰고 즉시 위치/회전을 설정합니다.
if (distanceToTarget > teleportDistanceThreshold)
float? newX = newData.GetFloat("X");
float? newY = newData.GetFloat("Y");
if (newX.HasValue || newY.HasValue)
{
transform.position = newTargetPosition;
transform.rotation = newTargetRotation;
float x = data.GetFloat("X").Value;
float y = data.GetFloat("Y").Value;
Vector3 newTargetPosition = transform.position;
if (newX.HasValue && x != newX) newTargetPosition.x = newX.Value * scaleFactor;
if (newY.HasValue && y != newY) newTargetPosition.z = newY.Value * scaleFactor;
if (newTargetPosition != transform.position)
{
// 현재 위치와 새로운 목표 위치 사이의 거리를 계산합니다.
float distanceToTarget = Vector3.Distance(transform.position, newTargetPosition);
// 거리가 설정된 임계값을 초과하면, 보간을 건너뛰고 즉시 위치을 설정합니다.
if (distanceToTarget > teleportDistanceThreshold)
{
transform.position = newTargetPosition;
isTeleport = true; // 순간이동이 발생했음을 표시합니다.
}
// 새로운 목표 지점을 설정합니다.
// (순간이동을 했든 안 했든, 다음 프레임부터의 보간을 위해 목표 지점은 항상 갱신되어야 합니다.)
this.targetPosition = newTargetPosition;
}
}
// 새로운 목표 지점을 설정합니다.
// (순간이동을 했든 안 했든, 다음 프레임부터의 보간을 위해 목표 지점은 항상 갱신되어야 합니다.)
this.targetPosition = newTargetPosition;
this.targetRotation = newTargetRotation;
float? newDegree = newData.GetFloat("DEGREE");
if(newDegree.HasValue)
{
if (data.GetFloat("DEGREE").Value != newDegree.Value)
{
Quaternion newTargetRotation = Quaternion.Euler(0, newDegree.Value, 0);
// 거리가 설정된 임계값을 초과하면, 보간을 건너뛰고 즉시 위치/회전을 설정합니다
if (isTeleport) transform.rotation = newTargetRotation;
// 새로운 목표 지점을 설정합니다.
// (순간이동을 했든 안 했든, 다음 프레임부터의 보간을 위해 목표 지점은 항상 갱신되어야 합니다.)
this.targetRotation = newTargetRotation;
}
}
newData.ReturnToPool(); // 사용이 끝난 데이터 객체를 풀에 반환합니다.
}
}

View File

@@ -80,7 +80,21 @@ namespace UVC.Factory.Component
{
private readonly string prefabPath = "Prefabs/SampleProject/Factory/AGV";
private MonoBehaviourPool<AGV>? agvPool;
private GameObjectPool<AGV>? agvPool;
public GameObjectPool<AGV> AGVPool
{
get
{
if (agvPool == null)
{
Debug.LogError("AGVPool is not initialized. Please call InitializePoolAsync first.");
}
return agvPool!;
}
}
private bool created = false;
/// <summary>
/// AGVManager의 초기화 메서드입니다.
@@ -150,7 +164,7 @@ namespace UVC.Factory.Component
Debug.LogError($"Prefab not found at path: {prefabPath}");
return;
}
agvPool = new MonoBehaviourPool<AGV>(prefab, transform);
agvPool = new GameObjectPool<AGV>(prefab, transform);
}
/// <summary>
@@ -176,7 +190,7 @@ namespace UVC.Factory.Component
var RemovedItems = arr.RemovedItems;
var ModifiedList = arr.ModifiedList;
Debug.Log($"AGVManager received data: Added={AddedItems.Count}, Removed={RemovedItems.Count}, Modified={ModifiedList.Count}");
//Debug.Log($"AGVManager received data: Added={AddedItems.Count}, Removed={RemovedItems.Count}, Modified={ModifiedList.Count}");
// 새로 추가된 AGV 처리
foreach (var item in AddedItems.ToList())
@@ -194,7 +208,7 @@ namespace UVC.Factory.Component
"",
item.GetString("MODE")
);
agv.UpdateData(item.Clone());
agv.UpdateData(item.Copy());
}
// 제거된 AGV 처리
@@ -211,16 +225,22 @@ namespace UVC.Factory.Component
// 정보가 수정된 AGV 처리
foreach (var item in ModifiedList.ToList())
{
Debug.Log($"AGVManager modified data: {item.ToString()}");
if(item.Id == "HFF09CNA8047") Debug.Log($"AGVManager modified data: {item.ToString()}");
string vhlName = item.GetString("VHL_NAME")!;
AGV? agv = agvPool.FindActiveItem(vhlName);
if (agv != null)
{
agv.UpdateData(item.Clone());
agv.UpdateData(item.Copy());
}
}
arr.ReturnToPool();
if(created == false)
{
created = true;
// 씬이 처음 초기화될 때 AGVManager가 생성되었음을 알립니다.
SceneMain.Instance.OnAGVManagerCreated();
}
}
/// <summary>

View File

@@ -110,8 +110,11 @@ namespace UVC.Factory.Component
/// }
/// </code>
/// </example>
public abstract class FactoryObject : InteractiveObject
public abstract class FactoryObject : MonoBehaviour, IPointerClickHandler, IPointerEnterHandler, IPointerExitHandler
{
[Tooltip("3D 모델 객체")]
public InteractiveObject? modelObject;
protected FactoryObjectInfo? info;
/// <summary>
@@ -149,9 +152,30 @@ namespace UVC.Factory.Component
}
}
private void OnDestroy()
protected virtual void Awake()
{
// 초기화 작업을 수행합니다.
if (modelObject == null)
{
Debug.LogError("FactoryObject requires an InteractiveObject component.");
}
else
{
modelObject.OnPointerClickHandler += OnPointerClick;
modelObject.OnPointerEnterHandler += OnPointerEnter;
modelObject.OnPointerExitHandler += OnPointerExit;
}
}
protected virtual void OnDestroy()
{
if (modelObject != null)
{
modelObject.OnPointerClickHandler -= OnPointerClick;
modelObject.OnPointerEnterHandler -= OnPointerEnter;
modelObject.OnPointerExitHandler -= OnPointerExit;
}
FactoryObjectManager.Instance.UnregisterFactoryObject(Info!);
}
@@ -165,7 +189,7 @@ namespace UVC.Factory.Component
/// `DataOrderedMask`가 설정되어 있으면 해당 순서대로, 그렇지 않으면 모든 데이터를 표시합니다.
/// </remarks>
/// <param name="eventData">포인터 클릭과 관련된 이벤트 데이터입니다.</param>
public override void OnPointerClick(PointerEventData eventData)
public void OnPointerClick(PointerEventData eventData)
{
if (data != null && data.Count > 0)
{
@@ -190,18 +214,28 @@ namespace UVC.Factory.Component
}
}
/// <summary>
/// 포인터가 이 객체 위로 들어왔을 때 호출됩니다. 하이라이트 효과 등에 사용할 수 있습니다.
/// </summary>
/// <param name="eventData">포인터 이벤트와 관련된 데이터입니다.</param>
public void OnPointerEnter(PointerEventData eventData) { }
/// <summary>
/// 포인터가 이 객체에서 벗어났을 때 호출됩니다.
/// </summary>
/// <param name="eventData">포인터 이벤트와 관련된 데이터입니다.</param>
public void OnPointerExit(PointerEventData eventData) { }
/// <summary>
/// 외부로부터 받은 새로운 데이터로 객체의 상태를 업데이트합니다.
/// 이 메서드는 내부적으로 `ProcessData`를 호출하여 실제 데이터 처리 로직을 수행합니다.
/// MQTTPipeLineInfo.updatedDataOnly가 true인 경우, 데이터가 변경된 경우에만 호출됩니다.
/// </summary>
/// <param name="newData">업데이트할 새로운 데이터가 포함된 IDataObject 객체입니다.</param>
public void UpdateData(IDataObject? newData)
public void UpdateData(DataObject newData)
{
if(newData == null) return;
DataObject? dataObject = newData as DataObject;
if (dataObject == null || dataObject.Count == 0) return;
ProcessData(dataObject);
ProcessData(newData);
}
/// <summary>

View File

@@ -148,7 +148,7 @@ namespace UVC.Factory.Component
/// <param name="id">찾고자 하는 객체의 ID입니다.</param>
/// <returns>처음으로 발견된 일치하는 FactoryObject. 없으면 null을 반환합니다.</returns>
public FactoryObject? FindById(string id)
{
{
foreach (var kvp in FactoryObjects)
{
if (kvp.Key.Id.Equals(id, StringComparison.OrdinalIgnoreCase))

View File

@@ -1,4 +1,6 @@
using UnityEngine;
using System;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.EventSystems;
namespace UVC.Object3d
@@ -14,24 +16,37 @@ namespace UVC.Object3d
/// 3. 메인 카메라에 <see cref="PhysicsRaycaster"/> 컴포넌트가 부착되어 있어야 합니다.
/// </remarks>
[RequireComponent(typeof(Collider))]
public abstract class InteractiveObject : MonoBehaviour, IPointerClickHandler, IPointerEnterHandler, IPointerExitHandler
public class InteractiveObject : MonoBehaviour, IPointerClickHandler, IPointerEnterHandler, IPointerExitHandler
{
public Action<PointerEventData> OnPointerClickHandler;
public Action<PointerEventData> OnPointerEnterHandler;
public Action<PointerEventData> OnPointerExitHandler;
/// <summary>
/// 포인터로 이 객체를 클릭했을 때 호출됩니다.
/// </summary>
/// <param name="eventData">클릭 이벤트와 관련된 데이터입니다.</param>
public virtual void OnPointerClick(PointerEventData eventData) {}
public virtual void OnPointerClick(PointerEventData eventData)
{
OnPointerClickHandler?.Invoke(eventData);
}
/// <summary>
/// 포인터가 이 객체 위로 들어왔을 때 호출됩니다. 하이라이트 효과 등에 사용할 수 있습니다.
/// </summary>
/// <param name="eventData">포인터 이벤트와 관련된 데이터입니다.</param>
public virtual void OnPointerEnter(PointerEventData eventData) {}
public virtual void OnPointerEnter(PointerEventData eventData)
{
OnPointerEnterHandler?.Invoke(eventData);
}
/// <summary>
/// 포인터가 이 객체에서 벗어났을 때 호출됩니다.
/// </summary>
/// <param name="eventData">포인터 이벤트와 관련된 데이터입니다.</param>
public virtual void OnPointerExit(PointerEventData eventData) {}
public virtual void OnPointerExit(PointerEventData eventData)
{
OnPointerExitHandler?.Invoke(eventData);
}
}
}

View File

@@ -17,12 +17,12 @@ namespace UVC.Pool
/// {
/// public GameObject bulletPrefab;
/// public Transform bulletContainer;
/// private MonoBehaviourPool&lt;Bullet&gt; _bulletPool;
/// private GameObjectPool<Bulle> _bulletPool;
///
/// void Start()
/// {
/// // 총알 프리팹과 부모 컨테이너를 사용하여 풀을 초기화합니다.
/// _bulletPool = new MonoBehaviourPool&lt;Bullet&gt;(bulletPrefab, bulletContainer);
/// _bulletPool = new GameObjectPool<Bulle>(bulletPrefab, bulletContainer);
/// }
///
/// void SpawnBullet()
@@ -42,7 +42,7 @@ namespace UVC.Pool
/// </code>
/// </summary>
/// <typeparam name="T">풀링할 MonoBehaviour 타입입니다.</typeparam>
public class MonoBehaviourPool<T> where T : MonoBehaviour
public class GameObjectPool<T> where T : MonoBehaviour
{
/// <summary>
/// Resources 폴더에 있는 프리팹의 경로입니다.
@@ -88,13 +88,19 @@ namespace UVC.Pool
/// </summary>
public Transform? RecycledItemContainer { get { return _recycledItemContainer; } }
// --- 통계용 필드 ---
private int _inUseCount = 0;
private int _peakUsage = 0;
private int _poolMisses = 0;
private readonly object _statsLock = new object();
/// <summary>
/// GameObject 프리팹을 사용하여 풀을 초기화합니다.
/// </summary>
/// <param name="originalPrefab">풀링할 오브젝트의 원본 프리팹입니다.</param>
/// <param name="activeItemContainer">활성화된 오브젝트가 위치할 부모 Transform입니다.</param>
/// <param name="recycledItemContainer">비활성화된 오브젝트가 위치할 부모 Transform입니다. 지정하지 않으면 activeItemContainer가 사용됩니다.</param>
public MonoBehaviourPool(GameObject originalPrefab, Transform activeItemContainer, Transform recycledItemContainer = null)
public GameObjectPool(GameObject originalPrefab, Transform activeItemContainer, Transform recycledItemContainer = null)
{
_originalPrefab = originalPrefab;
_activeItemContainer = activeItemContainer;
@@ -109,7 +115,7 @@ namespace UVC.Pool
/// <param name="prefabsPath">Resources 폴더 기준의 프리팹 경로입니다.</param>
/// <param name="activeItemContainer">활성화된 오브젝트가 위치할 부모 Transform입니다.</param>
/// <param name="recycledItemContainer">비활성화된 오브젝트가 위치할 부모 Transform입니다. 지정하지 않으면 activeItemContainer가 사용됩니다.</param>
public MonoBehaviourPool(string prefabsPath, Transform activeItemContainer, Transform recycledItemContainer)
public GameObjectPool(string prefabsPath, Transform activeItemContainer, Transform recycledItemContainer)
{
_prefabsPath = prefabsPath;
_originalPrefab = Resources.Load<GameObject>(prefabsPath);
@@ -133,6 +139,16 @@ namespace UVC.Pool
}
T? item = null;
lock (_statsLock)
{
_inUseCount++;
if (_inUseCount > _peakUsage)
{
_peakUsage = _inUseCount;
}
}
if (_recycledItems.Count > 0)
{
item = _recycledItems[0];
@@ -140,6 +156,11 @@ namespace UVC.Pool
}
else
{
lock (_statsLock)
{
_poolMisses++;
}
GameObject go = UnityEngine.Object.Instantiate(_originalPrefab);
item = go.GetComponent<T>();
}
@@ -173,6 +194,11 @@ namespace UVC.Pool
return; // 키에 해당하는 아이템이 없으면 반환
}
lock (_statsLock)
{
_inUseCount--;
}
_activeItems.Remove(key);
_recycledItems.Add(item);
@@ -224,5 +250,26 @@ namespace UVC.Pool
}
_recycledItems.Clear();
}
/// <summary>
/// 풀의 현재 성능 통계를 문자열로 반환합니다.
/// </summary>
/// <returns>풀 통계 (최대 사용량, 현재 사용량, 풀 비어있을 때 생성 횟수, 현재 풀 크기)</returns>
public string GetStats()
{
return $"최대 사용량: {_peakUsage}, 현재 사용량: {_inUseCount}, 풀 비어있을 때 생성 횟수: {_poolMisses}, 현재 풀 크기: {_recycledItems.Count}";
}
/// <summary>
/// 풀 통계를 초기화합니다.
/// </summary>
public void ResetStats()
{
lock (_statsLock)
{
_peakUsage = 0;
_poolMisses = 0;
}
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: e163d2d37fef9bd49acd0f3fa5b5d2dc

View File

@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: e9be41afc37616649924d7190be6215a

View File

@@ -1,5 +1,6 @@
#nullable enable
using System;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
@@ -19,10 +20,14 @@ namespace UVC.UI.Info
[SerializeField]
private TextMeshProUGUI text;
[Tooltip("정보 창을 닫 버튼")]
[Tooltip("정보 창을 닫 버튼")]
[SerializeField]
private Button closeButton;
[Tooltip("정보 창의 내용을 클립보드에 복사할 버튼")]
[SerializeField]
private Button copyButton;
[Tooltip("UI가 객체를 가리지 않도록 할 월드 좌표계 오프셋")]
[SerializeField]
private Vector2 screenOffset = new Vector2(10f, 10f);
@@ -34,6 +39,7 @@ namespace UVC.UI.Info
// 정보 창이 따라다닐 3D 객체의 Transform
private Transform? target;
private string message = string.Empty;
/// <summary>
/// 정보 창이 현재 화면에 표시되고 있는지 여부를 반환합니다.
@@ -54,6 +60,12 @@ namespace UVC.UI.Info
closeButton.onClick.AddListener(Hide);
}
if (copyButton != null)
{
copyButton.onClick.AddListener(CopyToClipboard);
}
// 처음에는 정보 창을 숨깁니다.
if (gameObject.activeSelf)
{
@@ -61,6 +73,15 @@ namespace UVC.UI.Info
}
}
private void CopyToClipboard()
{
// 클립보드에 현재 메시지를 복사합니다.
if (!string.IsNullOrEmpty(message))
{
GUIUtility.systemCopyBuffer = message;
}
}
private void LateUpdate()
{
// target이 설정되어 있고 활성화 상태일 때만 위치와 방향을 업데이트합니다.
@@ -158,6 +179,7 @@ namespace UVC.UI.Info
}
combinedString = combinedString.TrimEnd('\n'); // 마지막 줄바꿈 제거
text.text = combinedString;
message = combinedString;
}
// size를 text에 맞게 조정합니다.
RectTransform? rect = GetComponent<RectTransform>();

View File

@@ -1,6 +1,8 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UVC.Data;
using UVC.Factory.Component;
using UVC.Locale;
using UVC.Log;
using UVC.UI.Commands;
@@ -215,17 +217,12 @@ namespace UVC.UI.Menu
new MenuItemData("file_exit", "menu_file_exit", new QuitApplicationCommand()) // 애플리케이션 종료 명령 연결
}));
// "편집" 메뉴 및 하위 메뉴들 정의
model.MenuItems.Add(new MenuItemData("edit", "menu_edit", subMenuItems: new List<MenuItemData>
// pool 로그
model.MenuItems.Add(new MenuItemData("log", "Log", subMenuItems: new List<MenuItemData>
{
new MenuItemData("edit_undo", "menu_edit_undo", new DebugLogCommand("실행 취소 선택됨 (Command 실행)")),
new MenuItemData("edit_redo", "menu_edit_redo", new DebugLogCommand("다시 실행 선택됨 (Command 실행)")),
MenuItemData.CreateSeparator("edit_sep1"), // 구분선
new MenuItemData("file_save", "menu_file_save", command: new DebugLogCommand("저장 선택됨 (Command 실행)") , subMenuItems: new List<MenuItemData>
{
new MenuItemData("file_save_as", "menu_file_save_as", new DebugLogCommand("다른 이름으로 저장 선택됨 (Command 실행)"))
}),
new MenuItemData("preferences", "menu_preferences", new DebugLogCommand("환경설정 선택됨 (Command 실행)"))
new MenuItemData("dataArray", "DataArray", new ActionCommand(() => Debug.Log($"DataArrayPool stats: {DataArrayPool.GetStats()}"))),
new MenuItemData("dataObject", "DataObjet", new ActionCommand(() => Debug.Log($"DataObjectPool stats: {DataObjectPool.GetStats()}"))),
new MenuItemData("agv", "AGVPool", new ActionCommand(() => Debug.Log($"AGVPool stats: {AGVManager.Instance.AGVPool.GetStats()}"))),
}));
model.MenuItems.Add(new MenuItemData("modal", "모달", subMenuItems: new List<MenuItemData>
{