DI 추가
This commit is contained in:
8
Assets/Sample/Injector.meta
Normal file
8
Assets/Sample/Injector.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d8cdbce078d291e4f973e2f7655c0145
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
1901
Assets/Sample/Injector/InjectorSample.unity
Normal file
1901
Assets/Sample/Injector/InjectorSample.unity
Normal file
File diff suppressed because it is too large
Load Diff
7
Assets/Sample/Injector/InjectorSample.unity.meta
Normal file
7
Assets/Sample/Injector/InjectorSample.unity.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0562dcfb29668494badcb7b04e5e55f5
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
665
Assets/Sample/Injector/InjectorSample2.unity
Normal file
665
Assets/Sample/Injector/InjectorSample2.unity
Normal file
@@ -0,0 +1,665 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!29 &1
|
||||
OcclusionCullingSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_OcclusionBakeSettings:
|
||||
smallestOccluder: 5
|
||||
smallestHole: 0.25
|
||||
backfaceThreshold: 100
|
||||
m_SceneGUID: 00000000000000000000000000000000
|
||||
m_OcclusionCullingData: {fileID: 0}
|
||||
--- !u!104 &2
|
||||
RenderSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 10
|
||||
m_Fog: 0
|
||||
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
|
||||
m_FogMode: 3
|
||||
m_FogDensity: 0.01
|
||||
m_LinearFogStart: 0
|
||||
m_LinearFogEnd: 300
|
||||
m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
|
||||
m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
|
||||
m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
|
||||
m_AmbientIntensity: 1
|
||||
m_AmbientMode: 0
|
||||
m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
|
||||
m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_HaloStrength: 0.5
|
||||
m_FlareStrength: 1
|
||||
m_FlareFadeSpeed: 3
|
||||
m_HaloTexture: {fileID: 0}
|
||||
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
|
||||
m_DefaultReflectionMode: 0
|
||||
m_DefaultReflectionResolution: 128
|
||||
m_ReflectionBounces: 1
|
||||
m_ReflectionIntensity: 1
|
||||
m_CustomReflection: {fileID: 0}
|
||||
m_Sun: {fileID: 0}
|
||||
m_UseRadianceAmbientProbe: 0
|
||||
--- !u!157 &3
|
||||
LightmapSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 13
|
||||
m_BakeOnSceneLoad: 0
|
||||
m_GISettings:
|
||||
serializedVersion: 2
|
||||
m_BounceScale: 1
|
||||
m_IndirectOutputScale: 1
|
||||
m_AlbedoBoost: 1
|
||||
m_EnvironmentLightingMode: 0
|
||||
m_EnableBakedLightmaps: 1
|
||||
m_EnableRealtimeLightmaps: 0
|
||||
m_LightmapEditorSettings:
|
||||
serializedVersion: 12
|
||||
m_Resolution: 2
|
||||
m_BakeResolution: 40
|
||||
m_AtlasSize: 1024
|
||||
m_AO: 0
|
||||
m_AOMaxDistance: 1
|
||||
m_CompAOExponent: 1
|
||||
m_CompAOExponentDirect: 0
|
||||
m_ExtractAmbientOcclusion: 0
|
||||
m_Padding: 2
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_LightmapsBakeMode: 1
|
||||
m_TextureCompression: 1
|
||||
m_ReflectionCompression: 2
|
||||
m_MixedBakeMode: 2
|
||||
m_BakeBackend: 1
|
||||
m_PVRSampling: 1
|
||||
m_PVRDirectSampleCount: 32
|
||||
m_PVRSampleCount: 512
|
||||
m_PVRBounces: 2
|
||||
m_PVREnvironmentSampleCount: 256
|
||||
m_PVREnvironmentReferencePointCount: 2048
|
||||
m_PVRFilteringMode: 1
|
||||
m_PVRDenoiserTypeDirect: 1
|
||||
m_PVRDenoiserTypeIndirect: 1
|
||||
m_PVRDenoiserTypeAO: 1
|
||||
m_PVRFilterTypeDirect: 0
|
||||
m_PVRFilterTypeIndirect: 0
|
||||
m_PVRFilterTypeAO: 0
|
||||
m_PVREnvironmentMIS: 1
|
||||
m_PVRCulling: 1
|
||||
m_PVRFilteringGaussRadiusDirect: 1
|
||||
m_PVRFilteringGaussRadiusIndirect: 1
|
||||
m_PVRFilteringGaussRadiusAO: 1
|
||||
m_PVRFilteringAtrousPositionSigmaDirect: 0.5
|
||||
m_PVRFilteringAtrousPositionSigmaIndirect: 2
|
||||
m_PVRFilteringAtrousPositionSigmaAO: 1
|
||||
m_ExportTrainingData: 0
|
||||
m_TrainingDataDestination: TrainingData
|
||||
m_LightProbeSampleCountMultiplier: 4
|
||||
m_LightingDataAsset: {fileID: 20201, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_LightingSettings: {fileID: 0}
|
||||
--- !u!196 &4
|
||||
NavMeshSettings:
|
||||
serializedVersion: 2
|
||||
m_ObjectHideFlags: 0
|
||||
m_BuildSettings:
|
||||
serializedVersion: 3
|
||||
agentTypeID: 0
|
||||
agentRadius: 0.5
|
||||
agentHeight: 2
|
||||
agentSlope: 45
|
||||
agentClimb: 0.4
|
||||
ledgeDropHeight: 0
|
||||
maxJumpAcrossDistance: 0
|
||||
minRegionArea: 2
|
||||
manualCellSize: 0
|
||||
cellSize: 0.16666667
|
||||
manualTileSize: 0
|
||||
tileSize: 256
|
||||
buildHeightMesh: 0
|
||||
maxJobWorkers: 0
|
||||
preserveTilesOutsideBounds: 0
|
||||
debug:
|
||||
m_Flags: 0
|
||||
m_NavMeshData: {fileID: 0}
|
||||
--- !u!1 &333992323
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 333992324}
|
||||
- component: {fileID: 333992326}
|
||||
- component: {fileID: 333992325}
|
||||
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 &333992324
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 333992323}
|
||||
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: 1737878052}
|
||||
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: 200, y: 50}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &333992325
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 333992323}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Unity.TextMeshPro::TMPro.TextMeshProUGUI
|
||||
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: Scene2
|
||||
m_isRightToLeft: 0
|
||||
m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
|
||||
m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, 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: 36
|
||||
m_fontSizeBase: 36
|
||||
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!222 &333992326
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 333992323}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!1 &1424161726
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1424161729}
|
||||
- component: {fileID: 1424161728}
|
||||
- component: {fileID: 1424161727}
|
||||
m_Layer: 0
|
||||
m_Name: EventSystem
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &1424161727
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1424161726}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 01614664b831546d2ae94a42149d80ac, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Unity.InputSystem::UnityEngine.InputSystem.UI.InputSystemUIInputModule
|
||||
m_SendPointerHoverToParent: 1
|
||||
m_MoveRepeatDelay: 0.5
|
||||
m_MoveRepeatRate: 0.1
|
||||
m_XRTrackingOrigin: {fileID: 0}
|
||||
m_ActionsAsset: {fileID: -944628639613478452, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_PointAction: {fileID: -1654692200621890270, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_MoveAction: {fileID: -8784545083839296357, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_SubmitAction: {fileID: 392368643174621059, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_CancelAction: {fileID: 7727032971491509709, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_LeftClickAction: {fileID: 3001919216989983466, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_MiddleClickAction: {fileID: -2185481485913320682, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_RightClickAction: {fileID: -4090225696740746782, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_ScrollWheelAction: {fileID: 6240969308177333660, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_TrackedDevicePositionAction: {fileID: 6564999863303420839, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_TrackedDeviceOrientationAction: {fileID: 7970375526676320489, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
|
||||
m_DeselectOnBackgroundClick: 1
|
||||
m_PointerBehavior: 0
|
||||
m_CursorLockBehavior: 0
|
||||
m_ScrollDeltaPerTick: 6
|
||||
--- !u!114 &1424161728
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1424161726}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.EventSystems.EventSystem
|
||||
m_FirstSelected: {fileID: 0}
|
||||
m_sendNavigationEvents: 1
|
||||
m_DragThreshold: 10
|
||||
--- !u!4 &1424161729
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1424161726}
|
||||
serializedVersion: 2
|
||||
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: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &1626779944
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1626779947}
|
||||
- component: {fileID: 1626779946}
|
||||
- component: {fileID: 1626779945}
|
||||
m_Layer: 0
|
||||
m_Name: Main Camera
|
||||
m_TagString: MainCamera
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!81 &1626779945
|
||||
AudioListener:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1626779944}
|
||||
m_Enabled: 1
|
||||
--- !u!20 &1626779946
|
||||
Camera:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1626779944}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 2
|
||||
m_ClearFlags: 1
|
||||
m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
|
||||
m_projectionMatrixMode: 1
|
||||
m_GateFitMode: 2
|
||||
m_FOVAxisMode: 0
|
||||
m_Iso: 200
|
||||
m_ShutterSpeed: 0.005
|
||||
m_Aperture: 16
|
||||
m_FocusDistance: 10
|
||||
m_FocalLength: 50
|
||||
m_BladeCount: 5
|
||||
m_Curvature: {x: 2, y: 11}
|
||||
m_BarrelClipping: 0.25
|
||||
m_Anamorphism: 0
|
||||
m_SensorSize: {x: 36, y: 24}
|
||||
m_LensShift: {x: 0, y: 0}
|
||||
m_NormalizedViewPortRect:
|
||||
serializedVersion: 2
|
||||
x: 0
|
||||
y: 0
|
||||
width: 1
|
||||
height: 1
|
||||
near clip plane: 0.3
|
||||
far clip plane: 1000
|
||||
field of view: 60
|
||||
orthographic: 0
|
||||
orthographic size: 5
|
||||
m_Depth: -1
|
||||
m_CullingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_RenderingPath: -1
|
||||
m_TargetTexture: {fileID: 0}
|
||||
m_TargetDisplay: 0
|
||||
m_TargetEye: 3
|
||||
m_HDR: 1
|
||||
m_AllowMSAA: 1
|
||||
m_AllowDynamicResolution: 0
|
||||
m_ForceIntoRT: 0
|
||||
m_OcclusionCulling: 1
|
||||
m_StereoConvergence: 10
|
||||
m_StereoSeparation: 0.022
|
||||
--- !u!4 &1626779947
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1626779944}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 1, z: -10}
|
||||
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!1 &1737878048
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1737878052}
|
||||
- component: {fileID: 1737878051}
|
||||
- component: {fileID: 1737878050}
|
||||
- component: {fileID: 1737878049}
|
||||
m_Layer: 5
|
||||
m_Name: Canvas
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &1737878049
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1737878048}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.GraphicRaycaster
|
||||
m_IgnoreReversedGraphics: 1
|
||||
m_BlockingObjects: 0
|
||||
m_BlockingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
--- !u!114 &1737878050
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1737878048}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.CanvasScaler
|
||||
m_UiScaleMode: 0
|
||||
m_ReferencePixelsPerUnit: 100
|
||||
m_ScaleFactor: 1
|
||||
m_ReferenceResolution: {x: 800, y: 600}
|
||||
m_ScreenMatchMode: 0
|
||||
m_MatchWidthOrHeight: 0
|
||||
m_PhysicalUnit: 3
|
||||
m_FallbackScreenDPI: 96
|
||||
m_DefaultSpriteDPI: 96
|
||||
m_DynamicPixelsPerUnit: 1
|
||||
m_PresetInfoIsWorld: 0
|
||||
--- !u!223 &1737878051
|
||||
Canvas:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1737878048}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_RenderMode: 0
|
||||
m_Camera: {fileID: 0}
|
||||
m_PlaneDistance: 100
|
||||
m_PixelPerfect: 0
|
||||
m_ReceivesEvents: 1
|
||||
m_OverrideSorting: 0
|
||||
m_OverridePixelPerfect: 0
|
||||
m_SortingBucketNormalizedSize: 0
|
||||
m_VertexColorAlwaysGammaSpace: 0
|
||||
m_AdditionalShaderChannelsFlag: 25
|
||||
m_UpdateRectTransformForStandalone: 0
|
||||
m_SortingLayerID: 0
|
||||
m_SortingOrder: 0
|
||||
m_TargetDisplay: 0
|
||||
--- !u!224 &1737878052
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1737878048}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 0, y: 0, z: 0}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 333992324}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 0, y: 0}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0, y: 0}
|
||||
--- !u!1 &1957996552
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1957996554}
|
||||
- component: {fileID: 1957996553}
|
||||
- component: {fileID: 1957996555}
|
||||
m_Layer: 0
|
||||
m_Name: Directional Light
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!108 &1957996553
|
||||
Light:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1957996552}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 11
|
||||
m_Type: 1
|
||||
m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1}
|
||||
m_Intensity: 1
|
||||
m_Range: 10
|
||||
m_SpotAngle: 30
|
||||
m_InnerSpotAngle: 21.80208
|
||||
m_CookieSize: 10
|
||||
m_Shadows:
|
||||
m_Type: 2
|
||||
m_Resolution: -1
|
||||
m_CustomResolution: -1
|
||||
m_Strength: 1
|
||||
m_Bias: 0.05
|
||||
m_NormalBias: 0.4
|
||||
m_NearPlane: 0.2
|
||||
m_CullingMatrixOverride:
|
||||
e00: 1
|
||||
e01: 0
|
||||
e02: 0
|
||||
e03: 0
|
||||
e10: 0
|
||||
e11: 1
|
||||
e12: 0
|
||||
e13: 0
|
||||
e20: 0
|
||||
e21: 0
|
||||
e22: 1
|
||||
e23: 0
|
||||
e30: 0
|
||||
e31: 0
|
||||
e32: 0
|
||||
e33: 1
|
||||
m_UseCullingMatrixOverride: 0
|
||||
m_Cookie: {fileID: 0}
|
||||
m_DrawHalo: 0
|
||||
m_Flare: {fileID: 0}
|
||||
m_RenderMode: 0
|
||||
m_CullingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_RenderingLayerMask: 1
|
||||
m_Lightmapping: 4
|
||||
m_LightShadowCasterMode: 0
|
||||
m_AreaSize: {x: 1, y: 1}
|
||||
m_BounceIntensity: 1
|
||||
m_ColorTemperature: 6570
|
||||
m_UseColorTemperature: 0
|
||||
m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_UseBoundingSphereOverride: 0
|
||||
m_UseViewFrustumForShadowCasterCull: 1
|
||||
m_ForceVisible: 0
|
||||
m_ShadowRadius: 0
|
||||
m_ShadowAngle: 0
|
||||
m_LightUnit: 1
|
||||
m_LuxAtDistance: 1
|
||||
m_EnableSpotReflector: 1
|
||||
--- !u!4 &1957996554
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1957996552}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261}
|
||||
m_LocalPosition: {x: 0, y: 3, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0}
|
||||
--- !u!114 &1957996555
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1957996552}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 474bcb49853aa07438625e644c072ee6, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.UniversalAdditionalLightData
|
||||
m_UsePipelineSettings: 1
|
||||
m_AdditionalLightsShadowResolutionTier: 2
|
||||
m_CustomShadowLayers: 0
|
||||
m_LightCookieSize: {x: 1, y: 1}
|
||||
m_LightCookieOffset: {x: 0, y: 0}
|
||||
m_SoftShadowQuality: 0
|
||||
m_RenderingLayersMask:
|
||||
serializedVersion: 0
|
||||
m_Bits: 1
|
||||
m_ShadowRenderingLayersMask:
|
||||
serializedVersion: 0
|
||||
m_Bits: 1
|
||||
m_Version: 4
|
||||
m_LightLayerMask: 1
|
||||
m_ShadowLayerMask: 1
|
||||
m_RenderingLayers: 1
|
||||
m_ShadowRenderingLayers: 1
|
||||
--- !u!1660057539 &9223372036854775807
|
||||
SceneRoots:
|
||||
m_ObjectHideFlags: 0
|
||||
m_Roots:
|
||||
- {fileID: 1626779947}
|
||||
- {fileID: 1957996554}
|
||||
- {fileID: 1424161729}
|
||||
- {fileID: 1737878052}
|
||||
7
Assets/Sample/Injector/InjectorSample2.unity.meta
Normal file
7
Assets/Sample/Injector/InjectorSample2.unity.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: be808ba01dc1fb54ca27fa1f5e2f95bf
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
84
Assets/Sample/Injector/InjectorSampleAppContext.cs
Normal file
84
Assets/Sample/Injector/InjectorSampleAppContext.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
using UnityEngine;
|
||||
using UVC.Core;
|
||||
|
||||
namespace Sample
|
||||
{
|
||||
/// <summary>
|
||||
/// App 레벨 서비스 등록 컨텍스트 - 애플리케이션 전체 생명주기 동안 유지되는 서비스를 등록합니다.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para><b>[ 역할 ]</b></para>
|
||||
/// <para>InjectorAppContext를 상속받아 App 라이프사이클 서비스를 등록합니다.</para>
|
||||
/// <para>씬 전환 시에도 DontDestroyOnLoad로 유지되며, 등록된 서비스들도 함께 유지됩니다.</para>
|
||||
///
|
||||
/// <para><b>[ 설정 방법 ]</b></para>
|
||||
/// <list type="number">
|
||||
/// <item><description>빈 GameObject 생성 → 이름: "AppContext"</description></item>
|
||||
/// <item><description>이 컴포넌트(InjectorSampleAppContext) 추가</description></item>
|
||||
/// <item><description>Inspector에서 UI Manager Prefab 연결</description></item>
|
||||
/// </list>
|
||||
///
|
||||
/// <para><b>[ 등록되는 서비스 ]</b></para>
|
||||
/// <list type="table">
|
||||
/// <item><term>Type A</term><description>ILogService → ConsoleLogger (순수 C#)</description></item>
|
||||
/// <item><term>Type A</term><description>IGameService → GameService (순수 C#, ILogService 의존성)</description></item>
|
||||
/// <item><term>Type B</term><description>IAudioManager → InjectorSampleAudioManager (MonoBehaviour 동적 생성)</description></item>
|
||||
/// <item><term>Type C</term><description>IUIManager → InjectorSampleUIManager (Prefab 기반)</description></item>
|
||||
/// <item><term>Type D</term><description>InjectorSampleSettingsManager (순수 C# Singleton)</description></item>
|
||||
/// <item><term>Type D</term><description>InjectorSampleNetworkManager (MonoBehaviour SingletonApp)</description></item>
|
||||
/// <item><term>Factory</term><description>ISceneConfig → SceneConfig (팩토리 패턴)</description></item>
|
||||
/// </list>
|
||||
///
|
||||
/// <para><b>[ 실행 순서 ]</b></para>
|
||||
/// <para>DefaultExecutionOrder(-1000)으로 가장 먼저 실행됩니다.</para>
|
||||
/// </remarks>
|
||||
public class InjectorSampleAppContext : InjectorAppContext
|
||||
{
|
||||
[Header("Prefabs")]
|
||||
[SerializeField]
|
||||
[Tooltip("Type C 테스트용 - InjectorSampleUIManager 프리팹을 연결하세요")]
|
||||
private InjectorSampleUIManager uiManagerPrefab;
|
||||
|
||||
/// <summary>
|
||||
/// App 라이프사이클 서비스들을 등록합니다.
|
||||
/// Awake 시점에 자동 호출되며, 모든 서비스는 씬 전환 시에도 유지됩니다.
|
||||
/// </summary>
|
||||
protected override void RegisterServices()
|
||||
{
|
||||
base.RegisterServices();
|
||||
|
||||
// ========== Type A: 순수 C# 클래스 등록 ==========
|
||||
// MonoBehaviour가 아닌 일반 클래스를 서비스로 등록
|
||||
// new T()로 인스턴스 생성되며, [Inject] 필드도 자동 주입됨
|
||||
Injector.Register<ILogService, ConsoleLogger>(ServiceLifetime.App);
|
||||
Injector.Register<IGameService, GameService>(ServiceLifetime.App);
|
||||
|
||||
// ========== Type B: MonoBehaviour 동적 생성 등록 ==========
|
||||
// 런타임에 새 GameObject를 생성하고 컴포넌트를 추가
|
||||
// DontDestroyOnLoad가 자동 적용됨
|
||||
Injector.Register<IAudioManager, InjectorSampleAudioManager>(ServiceLifetime.App);
|
||||
|
||||
// ========== Type C: Prefab 기반 등록 ==========
|
||||
// Inspector에서 연결한 프리팹을 Instantiate하여 서비스로 등록
|
||||
// 프리팹에 미리 설정된 값들이 유지됨
|
||||
if (uiManagerPrefab != null)
|
||||
Injector.RegisterPrefab<IUIManager>(uiManagerPrefab.gameObject, ServiceLifetime.App);
|
||||
|
||||
// ========== Type D: 기존 Singleton 연동 ==========
|
||||
// Singleton<T> 또는 SingletonApp<T>를 상속받은 클래스를 Injector에 등록
|
||||
// 기존 Instance 접근 방식과 [Inject] 방식 모두 사용 가능
|
||||
Injector.RegisterSingleton<InjectorSampleSettingsManager>(); // 순수 C# Singleton
|
||||
Injector.RegisterSingleton<InjectorSampleNetworkManager>(); // MonoBehaviour SingletonApp
|
||||
|
||||
// ========== Factory 패턴 사용 ==========
|
||||
// 복잡한 초기화 로직이 필요한 경우 팩토리 함수를 사용
|
||||
Injector.RegisterFactory<ISceneConfig>(injector => new SceneConfig
|
||||
{
|
||||
SceneName = "MainMenu",
|
||||
Difficulty = 1.0f
|
||||
}, ServiceLifetime.App);
|
||||
|
||||
Debug.Log("[InjectorSampleAppContext] All App services registered");
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Sample/Injector/InjectorSampleAppContext.cs.meta
Normal file
2
Assets/Sample/Injector/InjectorSampleAppContext.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cc5b33cc3be966c4db70e1cff7286af1
|
||||
63
Assets/Sample/Injector/InjectorSampleAudioManager.cs
Normal file
63
Assets/Sample/Injector/InjectorSampleAudioManager.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using UnityEngine;
|
||||
using UVC.Core;
|
||||
|
||||
namespace Sample
|
||||
{
|
||||
/// <summary>
|
||||
/// Type B 예시: MonoBehaviour 동적 생성 - 오디오 매니저
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para><b>[ 타입 ]</b> Type B - MonoBehaviour 동적 생성 (Prefab 없이)</para>
|
||||
/// <para><b>[ 라이프사이클 ]</b> App - 씬 전환 시에도 유지</para>
|
||||
///
|
||||
/// <para><b>[ 특징 ]</b></para>
|
||||
/// <list type="bullet">
|
||||
/// <item><description>프리팹 없이 런타임에 새 GameObject가 자동 생성됨</description></item>
|
||||
/// <item><description>Injector가 new GameObject().AddComponent<T>()로 생성</description></item>
|
||||
/// <item><description>App 라이프사이클이면 DontDestroyOnLoad 자동 적용</description></item>
|
||||
/// <item><description>다른 서비스([Inject] ILogService)에 대한 의존성 주입 지원</description></item>
|
||||
/// </list>
|
||||
///
|
||||
/// <para><b>[ 등록 방법 ]</b></para>
|
||||
/// <code>Injector.Register<IAudioManager, InjectorSampleAudioManager>(ServiceLifetime.App);</code>
|
||||
///
|
||||
/// <para><b>[ 사용 방법 ]</b></para>
|
||||
/// <code>
|
||||
/// [Inject] private IAudioManager _audioManager;
|
||||
/// _audioManager.PlayBGM("main_theme");
|
||||
/// _audioManager.PlaySFX("button_click");
|
||||
/// </code>
|
||||
///
|
||||
/// <para><b>[ Type C(Prefab)와의 차이점 ]</b></para>
|
||||
/// <list type="bullet">
|
||||
/// <item><description>Type B: 코드로만 구성, Inspector 설정 불가</description></item>
|
||||
/// <item><description>Type C: 프리팹에 미리 설정된 AudioSource, AudioClip 등 활용 가능</description></item>
|
||||
/// </list>
|
||||
///
|
||||
/// <para><b>[ 실제 구현 시 확장 예시 ]</b></para>
|
||||
/// <code>
|
||||
/// private AudioSource bgmSource;
|
||||
/// private AudioSource sfxSource;
|
||||
///
|
||||
/// private void Awake()
|
||||
/// {
|
||||
/// bgmSource = gameObject.AddComponent<AudioSource>();
|
||||
/// sfxSource = gameObject.AddComponent<AudioSource>();
|
||||
/// bgmSource.loop = true;
|
||||
/// }
|
||||
/// </code>
|
||||
/// </remarks>
|
||||
public class InjectorSampleAudioManager : MonoBehaviour, IAudioManager
|
||||
{
|
||||
[Inject] private ILogService _logger;
|
||||
|
||||
/// <summary>효과음을 재생합니다.</summary>
|
||||
public void PlaySFX(string clipName) => _logger?.Log($"Playing SFX: {clipName}");
|
||||
|
||||
/// <summary>배경음악을 재생합니다.</summary>
|
||||
public void PlayBGM(string clipName) => _logger?.Log($"Playing BGM: {clipName}");
|
||||
|
||||
/// <summary>모든 오디오를 중지합니다.</summary>
|
||||
public void StopAll() => _logger?.Log("Stopping all audio");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6ff95a63fd64135439e1b50f5447c996
|
||||
44
Assets/Sample/Injector/InjectorSampleEnemy.cs
Normal file
44
Assets/Sample/Injector/InjectorSampleEnemy.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using UnityEngine;
|
||||
using UVC.Core;
|
||||
|
||||
namespace Sample
|
||||
{
|
||||
/// <summary>
|
||||
/// 런타임에 Instantiate로 생성되는 오브젝트
|
||||
/// 생성 후 수동으로 Inject 호출 필요
|
||||
/// </summary>
|
||||
public class InjectorSampleEnemy : MonoBehaviour
|
||||
{
|
||||
[Header("Enemy Settings")]
|
||||
[SerializeField] private int baseHealth = 100;
|
||||
|
||||
[Inject] private ILogService _logger;
|
||||
[Inject(Optional = true)] private ISceneConfig _sceneConfig;
|
||||
|
||||
public int Health { get; private set; }
|
||||
|
||||
private void Start()
|
||||
{
|
||||
var difficulty = _sceneConfig?.Difficulty ?? 1.0f;
|
||||
Health = (int)(baseHealth * difficulty);
|
||||
_logger?.Log($"Enemy spawned with {Health} HP");
|
||||
}
|
||||
|
||||
public void TakeDamage(int damage)
|
||||
{
|
||||
Health -= damage;
|
||||
_logger?.Log($"Enemy took {damage} damage. HP: {Health}");
|
||||
|
||||
if (Health <= 0)
|
||||
{
|
||||
Die();
|
||||
}
|
||||
}
|
||||
|
||||
private void Die()
|
||||
{
|
||||
_logger?.Log("Enemy died");
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Sample/Injector/InjectorSampleEnemy.cs.meta
Normal file
2
Assets/Sample/Injector/InjectorSampleEnemy.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 13788b24c7d924e4a8e7a993c744131c
|
||||
47
Assets/Sample/Injector/InjectorSampleEnemy.prefab
Normal file
47
Assets/Sample/Injector/InjectorSampleEnemy.prefab
Normal file
@@ -0,0 +1,47 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &9218091633217479843
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 3634990420135164290}
|
||||
- component: {fileID: 2633905929725207538}
|
||||
m_Layer: 0
|
||||
m_Name: InjectorSampleEnemy
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &3634990420135164290
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 9218091633217479843}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: -24.16767, y: -42.30578, z: 82.73528}
|
||||
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 &2633905929725207538
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 9218091633217479843}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 13788b24c7d924e4a8e7a993c744131c, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::Sample.InjectorSampleEnemy
|
||||
baseHealth: 100
|
||||
7
Assets/Sample/Injector/InjectorSampleEnemy.prefab.meta
Normal file
7
Assets/Sample/Injector/InjectorSampleEnemy.prefab.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 299a37c57fb733d47a6bf62ec19f810e
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
21
Assets/Sample/Injector/InjectorSampleEnemySpawner.cs
Normal file
21
Assets/Sample/Injector/InjectorSampleEnemySpawner.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using UnityEngine;
|
||||
using UVC.Core;
|
||||
|
||||
namespace Sample
|
||||
{
|
||||
/// <summary>
|
||||
/// Type B: MonoBehaviour - Scene 라이프사이클
|
||||
/// 씬 전환 시 자동 파괴됨
|
||||
/// </summary>
|
||||
public class InjectorSampleEnemySpawner : MonoBehaviour, IEnemySpawner
|
||||
{
|
||||
[Inject] private ILogService _logger;
|
||||
public int EnemyCount { get; private set; }
|
||||
|
||||
public void SpawnEnemy(Vector3 position)
|
||||
{
|
||||
EnemyCount++;
|
||||
_logger?.Log($"Enemy spawned at {position}. Total: {EnemyCount}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d30b988f7d67e2546a573cc4c9d707a4
|
||||
46
Assets/Sample/Injector/InjectorSampleEnemySpawnerExample.cs
Normal file
46
Assets/Sample/Injector/InjectorSampleEnemySpawnerExample.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using UnityEngine;
|
||||
using UVC.Core;
|
||||
|
||||
namespace Sample
|
||||
{
|
||||
/// <summary>
|
||||
/// 동적 생성 오브젝트 스포너 예시
|
||||
/// Instantiate 후 수동으로 Inject 호출하는 방법을 보여줍니다.
|
||||
/// </summary>
|
||||
public class InjectorSampleEnemySpawnerExample : MonoBehaviour
|
||||
{
|
||||
[Header("Spawn Settings")]
|
||||
[SerializeField] private InjectorSampleEnemy enemyPrefab;
|
||||
[SerializeField] private Transform spawnPoint;
|
||||
|
||||
[Inject] private ILogService _logger;
|
||||
|
||||
public void SpawnEnemy()
|
||||
{
|
||||
SpawnEnemy(spawnPoint != null ? spawnPoint.position : transform.position);
|
||||
}
|
||||
|
||||
public void SpawnEnemy(Vector3 position)
|
||||
{
|
||||
if (enemyPrefab == null)
|
||||
{
|
||||
_logger?.LogError("Enemy prefab is not assigned!");
|
||||
return;
|
||||
}
|
||||
|
||||
// 프리팹 인스턴스화
|
||||
var enemy = Instantiate(enemyPrefab, position, Quaternion.identity);
|
||||
|
||||
// 생성된 오브젝트에 의존성 주입
|
||||
InjectorAppContext.Instance?.InjectInto(enemy.gameObject);
|
||||
|
||||
_logger?.Log($"Enemy spawned at {position}");
|
||||
}
|
||||
|
||||
[ContextMenu("Spawn Enemy At Position")]
|
||||
private void SpawnEnemyAtPosition()
|
||||
{
|
||||
SpawnEnemy();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 26fa406f6120d7f48b3f2aaef4d7e1ec
|
||||
34
Assets/Sample/Injector/InjectorSampleLevelManager.cs
Normal file
34
Assets/Sample/Injector/InjectorSampleLevelManager.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using UnityEngine;
|
||||
using UVC.Core;
|
||||
|
||||
namespace Sample
|
||||
{
|
||||
/// <summary>
|
||||
/// Type D: SingletonScene 상속 클래스 (씬 단위)
|
||||
/// </summary>
|
||||
public class InjectorSampleLevelManager : SingletonScene<InjectorSampleLevelManager>
|
||||
{
|
||||
public int CurrentLevel { get; private set; }
|
||||
public int Score { get; private set; }
|
||||
|
||||
protected override void Init()
|
||||
{
|
||||
base.Init();
|
||||
CurrentLevel = 1;
|
||||
Score = 0;
|
||||
Debug.Log("[InjectorSampleLevelManager] Initialized");
|
||||
}
|
||||
|
||||
public void NextLevel()
|
||||
{
|
||||
CurrentLevel++;
|
||||
Debug.Log($"[InjectorSampleLevelManager] Level {CurrentLevel}");
|
||||
}
|
||||
|
||||
public void AddScore(int points)
|
||||
{
|
||||
Score += points;
|
||||
Debug.Log($"[InjectorSampleLevelManager] Score: {Score}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 728473fae6f9a0a47b0af68dbeefe92d
|
||||
214
Assets/Sample/Injector/InjectorSampleLoadingPanel.prefab
Normal file
214
Assets/Sample/Injector/InjectorSampleLoadingPanel.prefab
Normal file
@@ -0,0 +1,214 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &6021452925437567283
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 4674143885865561173}
|
||||
- component: {fileID: 3037942522900345903}
|
||||
- component: {fileID: 5971126230357167009}
|
||||
m_Layer: 5
|
||||
m_Name: InjectorSampleLoadingPanel
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 0
|
||||
--- !u!224 &4674143885865561173
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6021452925437567283}
|
||||
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: 1849606022384707511}
|
||||
m_Father: {fileID: 0}
|
||||
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 &3037942522900345903
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6021452925437567283}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &5971126230357167009
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6021452925437567283}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 0.392}
|
||||
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: 10907, guid: 0000000000000000f000000000000000, type: 0}
|
||||
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 &6968343366183727466
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1849606022384707511}
|
||||
- component: {fileID: 8530662954360578181}
|
||||
- component: {fileID: 3792713529586069920}
|
||||
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 &1849606022384707511
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6968343366183727466}
|
||||
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: 4674143885865561173}
|
||||
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: 500, y: 50}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &8530662954360578181
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6968343366183727466}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &3792713529586069920
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6968343366183727466}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Unity.TextMeshPro::TMPro.TextMeshProUGUI
|
||||
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: Loading Panel
|
||||
m_isRightToLeft: 0
|
||||
m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
|
||||
m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, 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: 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}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a6236378cb37a6b478b40be07700502e
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
80
Assets/Sample/Injector/InjectorSampleNetworkManager.cs
Normal file
80
Assets/Sample/Injector/InjectorSampleNetworkManager.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using UnityEngine;
|
||||
using UVC.Core;
|
||||
|
||||
namespace Sample
|
||||
{
|
||||
/// <summary>
|
||||
/// Type D 예시: MonoBehaviour SingletonApp - 네트워크 관리자
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para><b>[ 타입 ]</b> Type D - MonoBehaviour SingletonApp (SingletonApp<T> 상속)</para>
|
||||
/// <para><b>[ 라이프사이클 ]</b> App - 씬 전환 시에도 유지 (DontDestroyOnLoad)</para>
|
||||
///
|
||||
/// <para><b>[ 특징 ]</b></para>
|
||||
/// <list type="bullet">
|
||||
/// <item><description>MonoBehaviour이므로 Unity 생명주기 메서드 사용 가능</description></item>
|
||||
/// <item><description>씬에 미리 배치하거나 Instance 접근 시 자동 생성</description></item>
|
||||
/// <item><description>DontDestroyOnLoad 자동 적용</description></item>
|
||||
/// <item><description>Inspector에서 설정 가능</description></item>
|
||||
/// </list>
|
||||
///
|
||||
/// <para><b>[ 등록 방법 ]</b></para>
|
||||
/// <code>Injector.RegisterSingleton<InjectorSampleNetworkManager>();</code>
|
||||
///
|
||||
/// <para><b>[ 씬 설정 (선택) ]</b></para>
|
||||
/// <list type="number">
|
||||
/// <item><description>빈 GameObject 생성 → 이름: "NetworkManager"</description></item>
|
||||
/// <item><description>InjectorSampleNetworkManager 컴포넌트 추가</description></item>
|
||||
/// </list>
|
||||
/// <para>또는 코드에서 Instance 접근 시 자동으로 GameObject가 생성됩니다.</para>
|
||||
///
|
||||
/// <para><b>[ 사용 방법 ]</b></para>
|
||||
/// <code>
|
||||
/// // 방법 1: [Inject] 어트리뷰트 사용
|
||||
/// [Inject] private InjectorSampleNetworkManager _networkManager;
|
||||
/// _networkManager.Connect("wss://server.example.com");
|
||||
///
|
||||
/// // 방법 2: 직접 Instance 접근 (기존 Singleton 방식)
|
||||
/// InjectorSampleNetworkManager.Instance.Connect("wss://server.example.com");
|
||||
/// </code>
|
||||
///
|
||||
/// <para><b>[ InjectorSampleSettingsManager와의 차이점 ]</b></para>
|
||||
/// <list type="table">
|
||||
/// <item><term>SettingsManager</term><description>Singleton<T> - 순수 C#, GameObject 없음</description></item>
|
||||
/// <item><term>NetworkManager</term><description>SingletonApp<T> - MonoBehaviour, GameObject 필요</description></item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
public class InjectorSampleNetworkManager : SingletonApp<InjectorSampleNetworkManager>
|
||||
{
|
||||
/// <summary>서버 연결 상태</summary>
|
||||
public bool IsConnected { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// SingletonApp 초기화 메서드 - Awake에서 자동 호출됩니다.
|
||||
/// </summary>
|
||||
protected override void Init()
|
||||
{
|
||||
base.Init();
|
||||
Debug.Log("[InjectorSampleNetworkManager] Initialized");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 서버에 연결합니다.
|
||||
/// </summary>
|
||||
/// <param name="serverUrl">서버 URL (예: wss://server.example.com)</param>
|
||||
public void Connect(string serverUrl)
|
||||
{
|
||||
Debug.Log($"[InjectorSampleNetworkManager] Connecting to {serverUrl}");
|
||||
IsConnected = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 서버 연결을 해제합니다.
|
||||
/// </summary>
|
||||
public void Disconnect()
|
||||
{
|
||||
Debug.Log("[InjectorSampleNetworkManager] Disconnected");
|
||||
IsConnected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 39e835c57c05187499737b002734574f
|
||||
68
Assets/Sample/Injector/InjectorSamplePlayerController.cs
Normal file
68
Assets/Sample/Injector/InjectorSamplePlayerController.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using UnityEngine;
|
||||
using UVC.Core;
|
||||
|
||||
namespace Sample
|
||||
{
|
||||
/// <summary>
|
||||
/// 의존성 주입을 받는 클래스 예시
|
||||
/// [Inject] 어트리뷰트로 필요한 서비스를 자동 주입받습니다.
|
||||
/// </summary>
|
||||
public class InjectorSamplePlayerController : MonoBehaviour
|
||||
{
|
||||
[Header("Player Settings")]
|
||||
[SerializeField] private float moveSpeed = 5f;
|
||||
|
||||
// 필수 의존성
|
||||
[Inject] private ILogService _logger;
|
||||
[Inject] private IAudioManager _audioManager;
|
||||
[Inject] private IGameService _gameService;
|
||||
|
||||
// 선택적 의존성
|
||||
[Inject(Optional = true)] private IUIManager _uiManager;
|
||||
[Inject(Optional = true)] private IEnemySpawner _enemySpawner;
|
||||
|
||||
// 기존 Singleton 주입
|
||||
[Inject] private InjectorSampleSettingsManager _settings;
|
||||
[Inject] private InjectorSampleNetworkManager _networkManager;
|
||||
|
||||
// Scene 라이프사이클 서비스
|
||||
[Inject(Optional = true)] private InjectorSampleLevelManager _levelManager;
|
||||
[Inject(Optional = true)] private ISceneConfig _sceneConfig;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
_logger?.Log("PlayerController Start");
|
||||
_logger?.Log($"Settings - Volume: {_settings?.MasterVolume}");
|
||||
_logger?.Log($"Network Connected: {_networkManager?.IsConnected}");
|
||||
_logger?.Log($"Scene: {_sceneConfig?.SceneName}");
|
||||
_gameService?.Initialize();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
// Space: 점프 + 점수 추가
|
||||
if (Input.GetKeyDown(KeyCode.Space))
|
||||
{
|
||||
_audioManager?.PlaySFX("jump");
|
||||
_levelManager?.AddScore(10);
|
||||
}
|
||||
|
||||
// Escape: 일시정지 팝업
|
||||
if (Input.GetKeyDown(KeyCode.Escape))
|
||||
_uiManager?.ShowPopup("Pause", "Game Paused");
|
||||
|
||||
// E: 적 스폰
|
||||
if (Input.GetKeyDown(KeyCode.E))
|
||||
_enemySpawner?.SpawnEnemy(transform.position + Vector3.forward * 5f);
|
||||
|
||||
// S: 게임 저장
|
||||
if (Input.GetKeyDown(KeyCode.S))
|
||||
_gameService?.SaveGame();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
_logger?.Log("PlayerController Destroyed");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d9bc3b1df077df04c82459629cd1d492
|
||||
214
Assets/Sample/Injector/InjectorSamplePopupPanel.prefab
Normal file
214
Assets/Sample/Injector/InjectorSamplePopupPanel.prefab
Normal file
@@ -0,0 +1,214 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &906533046013280643
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 4129217316887975640}
|
||||
- component: {fileID: 358819916789992341}
|
||||
- component: {fileID: 8713247871210716181}
|
||||
m_Layer: 5
|
||||
m_Name: InjectorSamplePopupPanel
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &4129217316887975640
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 906533046013280643}
|
||||
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: 2309685012082784136}
|
||||
m_Father: {fileID: 0}
|
||||
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 &358819916789992341
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 906533046013280643}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &8713247871210716181
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 906533046013280643}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 0.392}
|
||||
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: 10907, guid: 0000000000000000f000000000000000, type: 0}
|
||||
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 &8763879408225593254
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 2309685012082784136}
|
||||
- component: {fileID: 380997242396970337}
|
||||
- component: {fileID: 6956743522295511630}
|
||||
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 &2309685012082784136
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8763879408225593254}
|
||||
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: 4129217316887975640}
|
||||
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: 500, y: 50}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &380997242396970337
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8763879408225593254}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &6956743522295511630
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8763879408225593254}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Unity.TextMeshPro::TMPro.TextMeshProUGUI
|
||||
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: Popup Panel
|
||||
m_isRightToLeft: 0
|
||||
m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
|
||||
m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, 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: 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}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 32c4e31457a0cf941b7c048dd1ac2b5e
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
73
Assets/Sample/Injector/InjectorSampleSceneContext.cs
Normal file
73
Assets/Sample/Injector/InjectorSampleSceneContext.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using UnityEngine;
|
||||
using UVC.Core;
|
||||
|
||||
namespace Sample
|
||||
{
|
||||
/// <summary>
|
||||
/// Scene 레벨 서비스 등록 컨텍스트 - 현재 씬에서만 유지되는 서비스를 등록합니다.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para><b>[ 역할 ]</b></para>
|
||||
/// <para>InjectorSceneContext를 상속받아 Scene 라이프사이클 서비스를 등록합니다.</para>
|
||||
/// <para>씬 전환 시 자동으로 정리되며, 새 씬에서는 해당 씬의 SceneContext가 서비스를 등록합니다.</para>
|
||||
///
|
||||
/// <para><b>[ 설정 방법 ]</b></para>
|
||||
/// <list type="number">
|
||||
/// <item><description>각 씬에 빈 GameObject 생성 → 이름: "SceneContext"</description></item>
|
||||
/// <item><description>이 컴포넌트(InjectorSampleSceneContext) 추가</description></item>
|
||||
/// <item><description>Inspector에서 Scene UI Prefab 연결 (선택)</description></item>
|
||||
/// </list>
|
||||
///
|
||||
/// <para><b>[ 등록되는 서비스 ]</b></para>
|
||||
/// <list type="table">
|
||||
/// <item><term>Type A</term><description>ISceneConfig → SceneConfig (순수 C#, Scene 라이프사이클)</description></item>
|
||||
/// <item><term>Type B</term><description>IEnemySpawner → InjectorSampleEnemySpawner (MonoBehaviour 동적 생성)</description></item>
|
||||
/// <item><term>Type C</term><description>ISceneUI → InjectorSampleSceneUI (Prefab 기반)</description></item>
|
||||
/// <item><term>Type D</term><description>InjectorSampleLevelManager (MonoBehaviour SingletonScene)</description></item>
|
||||
/// </list>
|
||||
///
|
||||
/// <para><b>[ 실행 순서 ]</b></para>
|
||||
/// <para>DefaultExecutionOrder(-900)으로 AppContext 다음에 실행됩니다.</para>
|
||||
///
|
||||
/// <para><b>[ App vs Scene 라이프사이클 ]</b></para>
|
||||
/// <list type="bullet">
|
||||
/// <item><description>App: 게임 전체에서 공유 (설정, 네트워크, 오디오 등)</description></item>
|
||||
/// <item><description>Scene: 현재 씬에서만 유효 (레벨 매니저, 적 스포너 등)</description></item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
public class InjectorSampleSceneContext : InjectorSceneContext
|
||||
{
|
||||
[Header("Scene Prefabs")]
|
||||
[SerializeField]
|
||||
[Tooltip("Scene용 UI 프리팹 - InjectorSampleSceneUI 프리팹을 연결하세요 (선택)")]
|
||||
private InjectorSampleSceneUI sceneUIPrefab;
|
||||
|
||||
/// <summary>
|
||||
/// Scene 라이프사이클 서비스들을 등록합니다.
|
||||
/// 씬 로드 시 자동 호출되며, 씬 전환 시 자동으로 정리됩니다.
|
||||
/// </summary>
|
||||
protected override void RegisterSceneServices()
|
||||
{
|
||||
base.RegisterSceneServices();
|
||||
|
||||
// ========== Type A: Scene 라이프사이클 순수 C# 클래스 ==========
|
||||
// 씬 전환 시 자동으로 해제되는 일반 클래스
|
||||
Injector.Register<ISceneConfig, SceneConfig>(ServiceLifetime.Scene);
|
||||
|
||||
// ========== Type B: Scene 라이프사이클 MonoBehaviour ==========
|
||||
// 씬 전환 시 GameObject와 함께 자동 파괴
|
||||
Injector.Register<IEnemySpawner, InjectorSampleEnemySpawner>(ServiceLifetime.Scene);
|
||||
|
||||
// ========== Type C: Scene 라이프사이클 Prefab ==========
|
||||
// 프리팹 기반 UI, 씬 전환 시 자동 파괴
|
||||
if (sceneUIPrefab != null)
|
||||
Injector.RegisterPrefab<ISceneUI>(sceneUIPrefab.gameObject, ServiceLifetime.Scene);
|
||||
|
||||
// ========== Type D: Scene 단위 Singleton 연동 ==========
|
||||
// SingletonScene<T>를 상속받은 클래스, 씬 전환 시 자동 파괴
|
||||
Injector.RegisterSingleton<InjectorSampleLevelManager>();
|
||||
|
||||
Debug.Log("[InjectorSampleSceneContext] All Scene services registered");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c8a09d99fdfcb9c42851b41aa3433701
|
||||
36
Assets/Sample/Injector/InjectorSampleSceneUI.cs
Normal file
36
Assets/Sample/Injector/InjectorSampleSceneUI.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using UnityEngine;
|
||||
using UVC.Core;
|
||||
|
||||
namespace Sample
|
||||
{
|
||||
/// <summary>
|
||||
/// Type C: Prefab 기반 - Scene 라이프사이클
|
||||
/// 씬별 UI 테마나 설정이 다른 경우
|
||||
/// </summary>
|
||||
public class InjectorSampleSceneUI : MonoBehaviour, ISceneUI
|
||||
{
|
||||
[Header("Scene Specific UI")]
|
||||
[SerializeField] private GameObject hudPanel;
|
||||
|
||||
[Inject] private ILogService _logger;
|
||||
[Inject(Optional = true)] private ISceneConfig _sceneConfig;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
var sceneName = _sceneConfig?.SceneName ?? "Unknown";
|
||||
_logger?.Log($"SceneUI initialized for: {sceneName}");
|
||||
}
|
||||
|
||||
public void ShowHUD()
|
||||
{
|
||||
_logger?.Log("Show HUD");
|
||||
if (hudPanel != null) hudPanel.SetActive(true);
|
||||
}
|
||||
|
||||
public void HideHUD()
|
||||
{
|
||||
_logger?.Log("Hide HUD");
|
||||
if (hudPanel != null) hudPanel.SetActive(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Sample/Injector/InjectorSampleSceneUI.cs.meta
Normal file
2
Assets/Sample/Injector/InjectorSampleSceneUI.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 249dbfa6b707154418519e4f9b85c150
|
||||
47
Assets/Sample/Injector/InjectorSampleSceneUI.prefab
Normal file
47
Assets/Sample/Injector/InjectorSampleSceneUI.prefab
Normal file
@@ -0,0 +1,47 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &3023500581229647168
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 4817480804472946038}
|
||||
- component: {fileID: 8836124997619267999}
|
||||
m_Layer: 0
|
||||
m_Name: InjectorSampleSceneUI
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &4817480804472946038
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3023500581229647168}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: -24.16767, y: -42.30578, z: 82.73528}
|
||||
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 &8836124997619267999
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3023500581229647168}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 249dbfa6b707154418519e4f9b85c150, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::Sample.InjectorSampleSceneUI
|
||||
hudPanel: {fileID: 6021452925437567283, guid: 7958a03e33f18e94b859c9d436e94bef, type: 3}
|
||||
7
Assets/Sample/Injector/InjectorSampleSceneUI.prefab.meta
Normal file
7
Assets/Sample/Injector/InjectorSampleSceneUI.prefab.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fd03d2bb254609f4fad184ff54fda941
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
214
Assets/Sample/Injector/InjectorSampleSceneUIHudPanel.prefab
Normal file
214
Assets/Sample/Injector/InjectorSampleSceneUIHudPanel.prefab
Normal file
@@ -0,0 +1,214 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &6021452925437567283
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 4674143885865561173}
|
||||
- component: {fileID: 3037942522900345903}
|
||||
- component: {fileID: 5971126230357167009}
|
||||
m_Layer: 5
|
||||
m_Name: InjectorSampleSceneUIHudPanel
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &4674143885865561173
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6021452925437567283}
|
||||
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: 1849606022384707511}
|
||||
m_Father: {fileID: 0}
|
||||
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 &3037942522900345903
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6021452925437567283}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &5971126230357167009
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6021452925437567283}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 0.392}
|
||||
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: 10907, guid: 0000000000000000f000000000000000, type: 0}
|
||||
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 &6968343366183727466
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1849606022384707511}
|
||||
- component: {fileID: 8530662954360578181}
|
||||
- component: {fileID: 3792713529586069920}
|
||||
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 &1849606022384707511
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6968343366183727466}
|
||||
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: 4674143885865561173}
|
||||
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: 500, y: 50}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &8530662954360578181
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6968343366183727466}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &3792713529586069920
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6968343366183727466}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Unity.TextMeshPro::TMPro.TextMeshProUGUI
|
||||
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: Loading Panel
|
||||
m_isRightToLeft: 0
|
||||
m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
|
||||
m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, 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: 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}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7958a03e33f18e94b859c9d436e94bef
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
82
Assets/Sample/Injector/InjectorSampleSettingsManager.cs
Normal file
82
Assets/Sample/Injector/InjectorSampleSettingsManager.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
using UnityEngine;
|
||||
using UVC.Core;
|
||||
|
||||
namespace Sample
|
||||
{
|
||||
/// <summary>
|
||||
/// Type D 예시: 순수 C# Singleton - 게임 설정 관리자
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para><b>[ 타입 ]</b> Type D - 순수 C# Singleton (Singleton<T> 상속)</para>
|
||||
/// <para><b>[ 라이프사이클 ]</b> App - 애플리케이션 전체 유지</para>
|
||||
///
|
||||
/// <para><b>[ 특징 ]</b></para>
|
||||
/// <list type="bullet">
|
||||
/// <item><description>MonoBehaviour가 아니므로 GameObject/씬에 배치 불필요</description></item>
|
||||
/// <item><description>Lazy 초기화: 첫 Instance 접근 시 자동 생성</description></item>
|
||||
/// <item><description>스레드 안전한 싱글톤 패턴</description></item>
|
||||
/// <item><description>Injector와 기존 Instance 접근 방식 모두 지원</description></item>
|
||||
/// </list>
|
||||
///
|
||||
/// <para><b>[ 등록 방법 ]</b></para>
|
||||
/// <code>Injector.RegisterSingleton<InjectorSampleSettingsManager>();</code>
|
||||
///
|
||||
/// <para><b>[ 사용 방법 ]</b></para>
|
||||
/// <code>
|
||||
/// // 방법 1: [Inject] 어트리뷰트 사용
|
||||
/// [Inject] private InjectorSampleSettingsManager _settings;
|
||||
/// _settings.MasterVolume = 0.5f;
|
||||
///
|
||||
/// // 방법 2: 직접 Instance 접근 (기존 Singleton 방식)
|
||||
/// InjectorSampleSettingsManager.Instance.MasterVolume = 0.5f;
|
||||
///
|
||||
/// // 두 방식 모두 동일한 인스턴스를 반환
|
||||
/// </code>
|
||||
///
|
||||
/// <para><b>[ 주의사항 ]</b></para>
|
||||
/// <list type="bullet">
|
||||
/// <item><description>Unity Inspector에서 설정 불가 (MonoBehaviour 아님)</description></item>
|
||||
/// <item><description>씬에 배치하면 컴파일 에러 발생</description></item>
|
||||
/// <item><description>PlayerPrefs 등을 통한 영속화는 별도 구현 필요</description></item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
public class InjectorSampleSettingsManager : Singleton<InjectorSampleSettingsManager>
|
||||
{
|
||||
/// <summary>마스터 볼륨 (0.0 ~ 1.0)</summary>
|
||||
public float MasterVolume { get; set; } = 1.0f;
|
||||
|
||||
/// <summary>효과음 볼륨 (0.0 ~ 1.0)</summary>
|
||||
public float SFXVolume { get; set; } = 1.0f;
|
||||
|
||||
/// <summary>배경음악 볼륨 (0.0 ~ 1.0)</summary>
|
||||
public float BGMVolume { get; set; } = 0.8f;
|
||||
|
||||
/// <summary>언어 설정 (ko, en, jp 등)</summary>
|
||||
public string Language { get; set; } = "ko";
|
||||
|
||||
/// <summary>
|
||||
/// 설정을 저장합니다.
|
||||
/// 실제 구현에서는 PlayerPrefs나 파일 시스템에 저장합니다.
|
||||
/// </summary>
|
||||
public void Save()
|
||||
{
|
||||
// 실제 구현 예시:
|
||||
// PlayerPrefs.SetFloat("MasterVolume", MasterVolume);
|
||||
// PlayerPrefs.SetString("Language", Language);
|
||||
// PlayerPrefs.Save();
|
||||
Debug.Log("[InjectorSampleSettingsManager] Settings saved");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 설정을 불러옵니다.
|
||||
/// 실제 구현에서는 PlayerPrefs나 파일 시스템에서 로드합니다.
|
||||
/// </summary>
|
||||
public void Load()
|
||||
{
|
||||
// 실제 구현 예시:
|
||||
// MasterVolume = PlayerPrefs.GetFloat("MasterVolume", 1.0f);
|
||||
// Language = PlayerPrefs.GetString("Language", "ko");
|
||||
Debug.Log("[InjectorSampleSettingsManager] Settings loaded");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 78ed6c29cec050048a9d62ce124694a7
|
||||
824
Assets/Sample/Injector/InjectorSampleTest.cs
Normal file
824
Assets/Sample/Injector/InjectorSampleTest.cs
Normal file
@@ -0,0 +1,824 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UVC.Core;
|
||||
|
||||
namespace Sample
|
||||
{
|
||||
/// <summary>
|
||||
/// Injector 샘플 테스트 클래스 - 4가지 타입별 의존성 주입 사용법을 테스트합니다.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para><b>[ Unity Editor 설정 가이드 ]</b></para>
|
||||
///
|
||||
/// <para><b>1. 씬 구조 설정</b></para>
|
||||
/// <code>
|
||||
/// [SampleScene]
|
||||
/// ├── AppContext ← InjectorSampleAppContext 컴포넌트 추가
|
||||
/// ├── SceneContext ← InjectorSampleSceneContext 컴포넌트 추가
|
||||
/// ├── NetworkManager ← InjectorSampleNetworkManager 컴포넌트 추가 (선택적, 자동 생성 가능)
|
||||
/// ├── Player ← InjectorSamplePlayerController 컴포넌트 추가
|
||||
/// ├── TestRunner ← InjectorSampleTest 컴포넌트 추가 (이 클래스)
|
||||
/// └── EnemySpawner ← InjectorSampleEnemySpawnerExample 컴포넌트 추가
|
||||
/// </code>
|
||||
///
|
||||
/// <para><b>2. 프리팹 생성 (Assets/Sample/Prefabs 폴더 권장)</b></para>
|
||||
/// <list type="table">
|
||||
/// <item><term>InjectorSampleUIManager</term><description>Type C: Prefab 기반 MonoBehaviour (AppContext에 연결)</description></item>
|
||||
/// <item><term>InjectorSampleSceneUI</term><description>Scene용 UI 프리팹 (SceneContext에 연결)</description></item>
|
||||
/// <item><term>InjectorSampleEnemySpawner</term><description>적 스포너 프리팹 (SceneContext에 연결)</description></item>
|
||||
/// <item><term>InjectorSampleEnemy</term><description>동적 생성될 적 프리팹 (EnemySpawnerExample에 연결)</description></item>
|
||||
/// </list>
|
||||
///
|
||||
/// <para><b>3. AppContext Inspector 설정</b></para>
|
||||
/// <list type="bullet">
|
||||
/// <item><description>UI Manager Prefab: InjectorSampleUIManager 프리팹 연결</description></item>
|
||||
/// </list>
|
||||
///
|
||||
/// <para><b>4. SceneContext Inspector 설정</b></para>
|
||||
/// <list type="bullet">
|
||||
/// <item><description>Scene UI Prefab: InjectorSampleSceneUI 프리팹 연결 (선택)</description></item>
|
||||
/// <item><description>Enemy Spawner Prefab: InjectorSampleEnemySpawner 프리팹 연결 (선택)</description></item>
|
||||
/// </list>
|
||||
///
|
||||
/// <para><b>5. EnemySpawnerExample Inspector 설정</b></para>
|
||||
/// <list type="bullet">
|
||||
/// <item><description>Enemy Prefab: InjectorSampleEnemy 프리팹 연결</description></item>
|
||||
/// <item><description>Spawn Point: 스폰 위치 Transform 연결 (선택)</description></item>
|
||||
/// </list>
|
||||
///
|
||||
/// <para><b>6. Singleton 타입별 설정 방법</b></para>
|
||||
/// <list type="table">
|
||||
/// <item>
|
||||
/// <term>InjectorSampleSettingsManager</term>
|
||||
/// <description>Singleton<T> (순수 C#) - 씬 설정 불필요, 코드에서 Instance 접근 시 자동 생성</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>InjectorSampleNetworkManager</term>
|
||||
/// <description>SingletonApp<T> (MonoBehaviour) - 씬에 배치 가능 또는 Instance 접근 시 자동 생성</description>
|
||||
/// </item>
|
||||
/// </list>
|
||||
///
|
||||
/// <para><b>7. 실행 순서 (자동 설정됨)</b></para>
|
||||
/// <list type="number">
|
||||
/// <item><description>InjectorAppContext (-1000) - App 서비스 등록</description></item>
|
||||
/// <item><description>InjectorSceneContext (-900) - Scene 서비스 등록</description></item>
|
||||
/// <item><description>일반 MonoBehaviour (0) - 의존성 주입된 상태로 Start 실행</description></item>
|
||||
/// </list>
|
||||
///
|
||||
/// <para><b>[ 등록 순서와 인스턴스 생성 시점 ]</b></para>
|
||||
/// <para>Register와 Resolve는 별개의 작업입니다:</para>
|
||||
/// <list type="bullet">
|
||||
/// <item><description><b>Register:</b> 서비스 메타데이터만 등록 (인스턴스 생성 안 함)</description></item>
|
||||
/// <item><description><b>Resolve:</b> 처음 호출될 때 인스턴스 생성 (Lazy Instantiation)</description></item>
|
||||
/// </list>
|
||||
/// <para>따라서 등록 순서는 중요하지 않으며, Resolve 시점에 의존성이 등록되어 있기만 하면 됩니다.</para>
|
||||
///
|
||||
/// <para><b>[ Unity 실행 순서 타임라인 ]</b></para>
|
||||
/// <code>
|
||||
/// ┌─────────────────────────────────────────────────────────────────────┐
|
||||
/// │ InjectorAppContext.Awake (-1000) │
|
||||
/// │ └→ Injector 생성 + App 서비스 메타데이터 등록 │
|
||||
/// ├─────────────────────────────────────────────────────────────────────┤
|
||||
/// │ InjectorSceneContext.Awake (-900) │
|
||||
/// │ └→ Scene 서비스 메타데이터 등록 │
|
||||
/// │ └→ 씬 오브젝트들에 의존성 주입 (InjectGameObject) │
|
||||
/// │ └→ 이 시점에 [Inject] 필드에 Resolve하여 인스턴스 할당 │
|
||||
/// ├─────────────────────────────────────────────────────────────────────┤
|
||||
/// │ InjectorSampleTest.Awake (0) │
|
||||
/// │ └→ [Inject] 필드(_logger, _audioManager 등)에 이미 인스턴스 주입됨 │
|
||||
/// │ └→ ⚠ 다른 MonoBehaviour의 Awake 실행 순서는 보장 안 됨 │
|
||||
/// ├─────────────────────────────────────────────────────────────────────┤
|
||||
/// │ InjectorSampleTest.Start │
|
||||
/// │ └→ ✅ 안전: 모든 Awake 완료 후 실행, 모든 의존성 사용 가능 │
|
||||
/// │ └→ RunAllTests() 호출 │
|
||||
/// └─────────────────────────────────────────────────────────────────────┘
|
||||
/// </code>
|
||||
///
|
||||
/// <para><b>[ 의존성 접근 권장 시점 ]</b></para>
|
||||
/// <list type="table">
|
||||
/// <listheader>
|
||||
/// <term>메서드</term>
|
||||
/// <description>안전성</description>
|
||||
/// </listheader>
|
||||
/// <item>
|
||||
/// <term>Awake()</term>
|
||||
/// <description>⚠ 주의 - [Inject] 필드 사용 가능하나, 다른 컴포넌트 Awake 순서 보장 안 됨</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>Start()</term>
|
||||
/// <description>✅ 안전 - 모든 Awake 완료 후 실행, 의존성 사용 권장 (이 클래스에서 사용)</description>
|
||||
/// </item>
|
||||
/// </list>
|
||||
///
|
||||
/// <para><b>[ Awake에서 반드시 의존성이 필요한 경우 해결 방법 ]</b></para>
|
||||
/// <list type="table">
|
||||
/// <listheader>
|
||||
/// <term>방법</term>
|
||||
/// <description>설명 및 예시</description>
|
||||
/// </listheader>
|
||||
/// <item>
|
||||
/// <term>1. DefaultExecutionOrder</term>
|
||||
/// <description>컴포넌트 실행 순서 명시적 지정 (가장 권장)</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>2. 직접 접근</term>
|
||||
/// <description>InjectorAppContext.Instance.Get<T>() 사용</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>3. Lazy 패턴</term>
|
||||
/// <description>프로퍼티로 첫 접근 시 초기화</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>4. 코루틴 대기</term>
|
||||
/// <description>조건 충족까지 대기 후 초기화</description>
|
||||
/// </item>
|
||||
/// </list>
|
||||
///
|
||||
/// <para><b>방법 1 예시: DefaultExecutionOrder</b></para>
|
||||
/// <code>
|
||||
/// [DefaultExecutionOrder(-100)] // InjectorSceneContext(-900) 이후, 일반(0) 이전
|
||||
/// public class EarlyInitService : MonoBehaviour
|
||||
/// {
|
||||
/// [Inject] private ILogService _logger;
|
||||
/// private void Awake() { _logger?.Log("Early init"); }
|
||||
/// }
|
||||
///
|
||||
/// [DefaultExecutionOrder(-50)] // EarlyInitService 이후
|
||||
/// public class LateInitService : MonoBehaviour
|
||||
/// {
|
||||
/// [Inject] private EarlyInitService _early; // 이미 초기화됨
|
||||
/// private void Awake() { _early.DoSomething(); } // ✅ 안전
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// <para><b>방법 2 예시: 직접 접근</b></para>
|
||||
/// <code>
|
||||
/// public class MyComponent : MonoBehaviour
|
||||
/// {
|
||||
/// private ILogService _logger;
|
||||
/// private void Awake()
|
||||
/// {
|
||||
/// _logger = InjectorAppContext.Instance.Get<ILogService>();
|
||||
/// _logger.Log("Awake에서 직접 접근");
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// <para><b>방법 3 예시: Lazy 패턴</b></para>
|
||||
/// <code>
|
||||
/// public class MyComponent : MonoBehaviour
|
||||
/// {
|
||||
/// private ILogService _logger;
|
||||
/// private ILogService Logger => _logger ??= InjectorAppContext.Instance.Get<ILogService>();
|
||||
/// private void Awake() { Logger.Log("Lazy 접근"); }
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// <para><b>[ 권장 순서 ]</b></para>
|
||||
/// <list type="number">
|
||||
/// <item><description>Start() 사용 (가장 단순하고 안전)</description></item>
|
||||
/// <item><description>DefaultExecutionOrder (명시적 순서 필요 시)</description></item>
|
||||
/// <item><description>직접 접근 또는 Lazy 패턴 (Awake 필수인 경우)</description></item>
|
||||
/// <item><description>코루틴 대기 (복잡한 초기화 의존성)</description></item>
|
||||
/// </list>
|
||||
///
|
||||
/// <para><b>[ RegisterSingleton vs RegisterInstance 사용 가이드 ]</b></para>
|
||||
/// <para>Singleton 타입을 등록할 때는 항상 <b>RegisterSingleton<T>()</b>를 사용하세요.</para>
|
||||
///
|
||||
/// <list type="table">
|
||||
/// <listheader>
|
||||
/// <term>상황</term>
|
||||
/// <description>등록 방법</description>
|
||||
/// </listheader>
|
||||
/// <item>
|
||||
/// <term>Singleton<T> (순수 C#)</term>
|
||||
/// <description>RegisterSingleton<T>()</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>SingletonApp<T> (씬 배치 or 자동 생성)</term>
|
||||
/// <description>RegisterSingleton<T>()</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>SingletonScene<T> (씬 배치 or 자동 생성)</term>
|
||||
/// <description>RegisterSingleton<T>()</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>일반 MonoBehaviour (씬에 미리 배치)</term>
|
||||
/// <description>RegisterInstance<T>(instance)</description>
|
||||
/// </item>
|
||||
/// </list>
|
||||
///
|
||||
/// <para><b>[ 왜 SingletonScene도 RegisterSingleton을 사용하나요? ]</b></para>
|
||||
/// <para>SingletonScene<T>.Instance 프로퍼티가 내부적으로 FindFirstObjectByType을 사용하여</para>
|
||||
/// <para>씬에 이미 배치된 인스턴스를 자동으로 찾기 때문입니다.</para>
|
||||
///
|
||||
/// <code>
|
||||
/// // SingletonScene.Instance 내부 동작
|
||||
/// public static T Instance
|
||||
/// {
|
||||
/// get
|
||||
/// {
|
||||
/// if (instance == null)
|
||||
/// {
|
||||
/// instance = (T)FindFirstObjectByType(typeof(T)); // 씬에서 검색
|
||||
/// if (instance == null)
|
||||
/// instance = new GameObject(typeof(T).Name).AddComponent<T>(); // 없으면 생성
|
||||
/// }
|
||||
/// return instance;
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// // 등록 예시
|
||||
/// // ✅ 올바름 - Singleton 타입
|
||||
/// Injector.RegisterSingleton<InjectorSampleSettingsManager>(); // Singleton<T>
|
||||
/// Injector.RegisterSingleton<InjectorSampleNetworkManager>(); // SingletonApp<T>
|
||||
/// Injector.RegisterSingleton<InjectorSampleLevelManager>(); // SingletonScene<T>
|
||||
///
|
||||
/// // ✅ 올바름 - 일반 MonoBehaviour
|
||||
/// [SerializeField] private MyManager myManager;
|
||||
/// Injector.RegisterInstance<IMyManager>(myManager);
|
||||
///
|
||||
/// // ❌ 불필요 - Singleton에 RegisterInstance
|
||||
/// // Injector.RegisterInstance(FindObjectOfType<MySceneSingleton>());
|
||||
/// </code>
|
||||
///
|
||||
/// <para><b>8. 테스트 실행 확인</b></para>
|
||||
/// <para>Play 모드 실행 시 Console에 다음 로그가 출력되면 성공:</para>
|
||||
/// <code>
|
||||
/// [InjectorSampleAppContext] All App services registered
|
||||
/// ========== Injector Sample Tests ==========
|
||||
/// ----- Type A: Pure C# Class -----
|
||||
/// [ConsoleLogger] Type A Test: Logger is working!
|
||||
/// ...
|
||||
/// ========== All Tests Completed ==========
|
||||
/// </code>
|
||||
/// </remarks>
|
||||
public class InjectorSampleTest : MonoBehaviour
|
||||
{
|
||||
[Header("Test Settings")]
|
||||
[SerializeField] private bool runTestOnStart = true;
|
||||
|
||||
[Inject] private ILogService _logger;
|
||||
[Inject] private IAudioManager _audioManager;
|
||||
[Inject] private IGameService _gameService;
|
||||
[Inject(Optional = true)] private IUIManager _uiManager;
|
||||
[Inject] private InjectorSampleSettingsManager _settings;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (runTestOnStart)
|
||||
RunAllTests();
|
||||
}
|
||||
|
||||
[ContextMenu("Run All Tests")]
|
||||
public void RunAllTests()
|
||||
{
|
||||
Debug.Log("========== Injector Sample Tests ==========");
|
||||
|
||||
TestTypeA_PureCSharp();
|
||||
TestTypeB_MonoBehaviour();
|
||||
TestTypeC_Prefab();
|
||||
TestTypeD_Singleton();
|
||||
TestLifecycles();
|
||||
|
||||
Debug.Log("========== All Tests Completed ==========");
|
||||
}
|
||||
|
||||
private void TestTypeA_PureCSharp()
|
||||
{
|
||||
Debug.Log("----- Type A: Pure C# Class -----");
|
||||
_logger?.Log("Type A Test: Logger is working!");
|
||||
_gameService?.Initialize();
|
||||
_gameService?.SaveGame();
|
||||
}
|
||||
|
||||
private void TestTypeB_MonoBehaviour()
|
||||
{
|
||||
Debug.Log("----- Type B: MonoBehaviour (Dynamic) -----");
|
||||
_audioManager?.PlayBGM("main_theme");
|
||||
_audioManager?.PlaySFX("click");
|
||||
}
|
||||
|
||||
private void TestTypeC_Prefab()
|
||||
{
|
||||
Debug.Log("----- Type C: Prefab-based MonoBehaviour -----");
|
||||
_uiManager?.ShowLoading();
|
||||
_uiManager?.HideLoading();
|
||||
_uiManager?.ShowPopup("Test", "This is a test popup");
|
||||
}
|
||||
|
||||
private void TestTypeD_Singleton()
|
||||
{
|
||||
Debug.Log("----- Type D: Existing Singleton -----");
|
||||
|
||||
// 1. Inject를 통한 접근 테스트
|
||||
_logger?.Log($"[Inject] Master Volume: {_settings?.MasterVolume}");
|
||||
_logger?.Log($"[Inject] SFX Volume: {_settings?.SFXVolume}");
|
||||
_logger?.Log($"[Inject] BGM Volume: {_settings?.BGMVolume}");
|
||||
_logger?.Log($"[Inject] Language: {_settings?.Language}");
|
||||
|
||||
// 2. 직접 Instance 접근 테스트 (순수 C# Singleton)
|
||||
var directSettings = InjectorSampleSettingsManager.Instance;
|
||||
_logger?.Log($"[Direct] Master Volume: {directSettings.MasterVolume}");
|
||||
|
||||
// 3. 동일 인스턴스 확인
|
||||
bool isSameInstance = ReferenceEquals(_settings, directSettings);
|
||||
_logger?.Log($"Same instance check: {isSameInstance}");
|
||||
|
||||
// 4. 값 변경 및 저장 테스트
|
||||
if (_settings != null)
|
||||
{
|
||||
float originalVolume = _settings.MasterVolume;
|
||||
_settings.MasterVolume = 0.5f;
|
||||
_settings.SFXVolume = 0.7f;
|
||||
_settings.Language = "en";
|
||||
_settings.Save();
|
||||
|
||||
_logger?.Log($"Changed Master Volume: {originalVolume} -> {_settings.MasterVolume}");
|
||||
_logger?.Log($"Changed Language: ko -> {_settings.Language}");
|
||||
|
||||
// 직접 접근으로도 변경된 값 확인
|
||||
_logger?.Log($"[Direct] Verify changed value: {directSettings.MasterVolume}");
|
||||
|
||||
// 값 복원
|
||||
_settings.MasterVolume = originalVolume;
|
||||
_settings.Language = "ko";
|
||||
_settings.Load();
|
||||
}
|
||||
}
|
||||
|
||||
private void TestLifecycles()
|
||||
{
|
||||
Debug.Log("----- Lifecycle Tests -----");
|
||||
_logger?.Log("App services persist across scenes");
|
||||
|
||||
var levelManager = InjectorAppContext.Instance?.TryGet<InjectorSampleLevelManager>();
|
||||
if (levelManager != null)
|
||||
{
|
||||
_logger?.Log($"Current Level: {levelManager.CurrentLevel}");
|
||||
levelManager.AddScore(100);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger?.Log("LevelManager not available (Scene service)");
|
||||
}
|
||||
}
|
||||
|
||||
[ContextMenu("Test Dynamic Instantiation")]
|
||||
public void TestDynamicInstantiation()
|
||||
{
|
||||
Debug.Log("----- Dynamic Instantiation Test -----");
|
||||
|
||||
var go = new GameObject("DynamicObject");
|
||||
var component = go.AddComponent<InjectorSampleDynamicTestComponent>();
|
||||
|
||||
InjectorAppContext.Instance?.InjectInto(go);
|
||||
component.Test();
|
||||
|
||||
Destroy(go, 2f);
|
||||
}
|
||||
|
||||
#region Scene Transition & Memory Test
|
||||
|
||||
/// <summary>
|
||||
/// 씬 전환 테스트용 메서드
|
||||
/// </summary>
|
||||
/// <param name="sceneName">이동할 씬 이름</param>
|
||||
/// <remarks>
|
||||
/// <para><b>[ 사용법 ]</b></para>
|
||||
/// <code>
|
||||
/// // 코드에서 호출
|
||||
/// GetComponent<InjectorSampleTest>().MoveScene("NextScene");
|
||||
///
|
||||
/// // 또는 Inspector에서 Button OnClick에 연결
|
||||
/// </code>
|
||||
/// </remarks>
|
||||
public void MoveScene(string sceneName)
|
||||
{
|
||||
Debug.Log($"[SceneTransition] Moving to scene: {sceneName}");
|
||||
LogMemoryStatus("Before Scene Load");
|
||||
SceneManager.LoadScene(sceneName);
|
||||
}
|
||||
|
||||
public void TestMoveScene()
|
||||
{
|
||||
string sceneName = "InjectorSample2";
|
||||
Debug.Log($"========== Scene Move Test to {sceneName} ==========");
|
||||
LogMemoryStatus("Before Scene Load");
|
||||
LogServiceStatus("Before Scene Load");
|
||||
|
||||
SceneManager.sceneLoaded += OnSceneLoadedForTest;
|
||||
SceneManager.LoadScene(sceneName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 씬 전환 테스트 - 현재 씬을 다시 로드하여 Scene 라이프사이클 서비스 정리 확인
|
||||
/// </summary>
|
||||
[ContextMenu("Test Scene Reload (Memory Check)")]
|
||||
public void TestSceneReload()
|
||||
{
|
||||
Debug.Log("========== Scene Reload Test ==========");
|
||||
LogMemoryStatus("Before Reload");
|
||||
LogServiceStatus("Before Reload");
|
||||
|
||||
string currentScene = SceneManager.GetActiveScene().name;
|
||||
Debug.Log($"[SceneTransition] Reloading current scene: {currentScene}");
|
||||
|
||||
// 씬 로드 완료 이벤트 등록
|
||||
SceneManager.sceneLoaded += OnSceneLoadedForTest;
|
||||
SceneManager.LoadScene(currentScene);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 씬 전환 테스트 - 다른 씬으로 이동 후 다시 돌아오기
|
||||
/// </summary>
|
||||
/// <param name="targetScene">이동할 대상 씬</param>
|
||||
/// <param name="returnAfterSeconds">돌아올 때까지 대기 시간 (초)</param>
|
||||
[ContextMenu("Test Scene Round Trip")]
|
||||
public void TestSceneRoundTrip()
|
||||
{
|
||||
TestSceneRoundTrip("InjectorTestScene2", 2f);
|
||||
}
|
||||
|
||||
public void TestSceneRoundTrip(string targetScene, float returnAfterSeconds)
|
||||
{
|
||||
Debug.Log("========== Scene Round Trip Test ==========");
|
||||
LogMemoryStatus("Before Round Trip");
|
||||
LogServiceStatus("Before Round Trip");
|
||||
|
||||
string originalScene = SceneManager.GetActiveScene().name;
|
||||
Debug.Log($"[SceneTransition] {originalScene} -> {targetScene} -> {originalScene}");
|
||||
|
||||
// 원래 씬 이름 저장 (static으로 씬 전환 후에도 유지)
|
||||
_returnSceneName = originalScene;
|
||||
_returnDelay = returnAfterSeconds;
|
||||
|
||||
SceneManager.sceneLoaded += OnSceneLoadedForRoundTrip;
|
||||
SceneManager.LoadScene(targetScene);
|
||||
}
|
||||
|
||||
private static string _returnSceneName;
|
||||
private static float _returnDelay;
|
||||
private static bool _isReturning;
|
||||
private static bool _pendingSceneReloadTest;
|
||||
private static bool _pendingRoundTripTest;
|
||||
private static readonly WaitForSeconds _waitOneSecond = new(1f);
|
||||
|
||||
/// <summary>
|
||||
/// 씬 로드 완료 후 새로운 씬의 InjectorSampleTest 인스턴스에서 처리
|
||||
/// </summary>
|
||||
private void OnEnable()
|
||||
{
|
||||
// 씬 로드 후 대기 중인 테스트가 있으면 실행
|
||||
if (_pendingSceneReloadTest)
|
||||
{
|
||||
_pendingSceneReloadTest = false;
|
||||
OnSceneReloadCompleted();
|
||||
}
|
||||
else if (_pendingRoundTripTest)
|
||||
{
|
||||
OnRoundTripSceneLoaded();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
// 이벤트 핸들러 정리 (씬 전환 시 호출됨)
|
||||
SceneManager.sceneLoaded -= OnSceneLoadedForTest;
|
||||
SceneManager.sceneLoaded -= OnSceneLoadedForRoundTrip;
|
||||
}
|
||||
|
||||
private static void OnSceneLoadedForTest(Scene scene, LoadSceneMode mode)
|
||||
{
|
||||
SceneManager.sceneLoaded -= OnSceneLoadedForTest;
|
||||
Debug.Log($"[SceneTransition] Scene loaded: {scene.name}");
|
||||
|
||||
// 새로운 씬의 InjectorSampleTest에서 처리하도록 플래그 설정
|
||||
_pendingSceneReloadTest = true;
|
||||
}
|
||||
|
||||
private void OnSceneReloadCompleted()
|
||||
{
|
||||
LogMemoryStatusStatic("After Reload");
|
||||
LogServiceStatusStatic("After Reload");
|
||||
|
||||
// GC 강제 실행 후 메모리 재확인
|
||||
StartCoroutine(ForceGCAndLogMemory());
|
||||
}
|
||||
|
||||
private static void OnSceneLoadedForRoundTrip(Scene scene, LoadSceneMode mode)
|
||||
{
|
||||
SceneManager.sceneLoaded -= OnSceneLoadedForRoundTrip;
|
||||
|
||||
Debug.Log($"[SceneTransition] Arrived at: {scene.name}");
|
||||
LogMemoryStatusStatic($"At {scene.name}");
|
||||
LogServiceStatusStatic($"At {scene.name}");
|
||||
|
||||
// 새로운 씬의 InjectorSampleTest에서 처리하도록 플래그 설정
|
||||
_pendingRoundTripTest = true;
|
||||
}
|
||||
|
||||
private void OnRoundTripSceneLoaded()
|
||||
{
|
||||
if (!_isReturning && SceneManager.GetActiveScene().name != _returnSceneName)
|
||||
{
|
||||
_isReturning = true;
|
||||
_pendingRoundTripTest = false;
|
||||
// 잠시 대기 후 원래 씬으로 복귀
|
||||
StartCoroutine(ReturnToOriginalScene());
|
||||
}
|
||||
else if (_isReturning)
|
||||
{
|
||||
_isReturning = false;
|
||||
_pendingRoundTripTest = false;
|
||||
Debug.Log("========== Round Trip Completed ==========");
|
||||
StartCoroutine(ForceGCAndLogMemory());
|
||||
}
|
||||
}
|
||||
|
||||
private System.Collections.IEnumerator ReturnToOriginalScene()
|
||||
{
|
||||
Debug.Log($"[SceneTransition] Waiting {_returnDelay}s before returning...");
|
||||
yield return new WaitForSeconds(_returnDelay);
|
||||
|
||||
Debug.Log($"[SceneTransition] Returning to: {_returnSceneName}");
|
||||
LogMemoryStatusStatic("Before Return");
|
||||
|
||||
SceneManager.sceneLoaded += OnSceneLoadedForRoundTrip;
|
||||
SceneManager.LoadScene(_returnSceneName);
|
||||
}
|
||||
|
||||
private System.Collections.IEnumerator ForceGCAndLogMemory()
|
||||
{
|
||||
Debug.Log("[Memory] Forcing GC collection...");
|
||||
|
||||
// GC 강제 실행
|
||||
System.GC.Collect();
|
||||
System.GC.WaitForPendingFinalizers();
|
||||
System.GC.Collect();
|
||||
|
||||
// 1프레임 대기
|
||||
yield return null;
|
||||
|
||||
// Resources.UnloadUnusedAssets는 코루틴으로 실행
|
||||
var unloadOperation = Resources.UnloadUnusedAssets();
|
||||
yield return unloadOperation;
|
||||
|
||||
LogMemoryStatus("After GC");
|
||||
Debug.Log("========== Memory Check Completed ==========");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 현재 메모리 상태를 로그로 출력
|
||||
/// </summary>
|
||||
/// <param name="label">로그 라벨</param>
|
||||
[ContextMenu("Log Memory Status")]
|
||||
public void LogMemoryStatus()
|
||||
{
|
||||
LogMemoryStatus("Current");
|
||||
}
|
||||
|
||||
public void LogMemoryStatus(string label)
|
||||
{
|
||||
// Unity 메모리 정보
|
||||
long totalMemory = UnityEngine.Profiling.Profiler.GetTotalReservedMemoryLong();
|
||||
long usedMemory = UnityEngine.Profiling.Profiler.GetTotalAllocatedMemoryLong();
|
||||
long monoHeap = UnityEngine.Profiling.Profiler.GetMonoHeapSizeLong();
|
||||
long monoUsed = UnityEngine.Profiling.Profiler.GetMonoUsedSizeLong();
|
||||
|
||||
Debug.Log($"[Memory] === {label} ===");
|
||||
Debug.Log($"[Memory] Total Reserved: {FormatBytes(totalMemory)}");
|
||||
Debug.Log($"[Memory] Total Allocated: {FormatBytes(usedMemory)}");
|
||||
Debug.Log($"[Memory] Mono Heap: {FormatBytes(monoHeap)}");
|
||||
Debug.Log($"[Memory] Mono Used: {FormatBytes(monoUsed)}");
|
||||
|
||||
// GC 정보
|
||||
Debug.Log($"[Memory] GC Total Memory: {FormatBytes(System.GC.GetTotalMemory(false))}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 현재 등록된 서비스 상태를 로그로 출력
|
||||
/// </summary>
|
||||
/// <param name="label">로그 라벨</param>
|
||||
[ContextMenu("Log Service Status")]
|
||||
public void LogServiceStatus()
|
||||
{
|
||||
LogServiceStatus("Current");
|
||||
}
|
||||
|
||||
public void LogServiceStatus(string label)
|
||||
{
|
||||
Debug.Log($"[Services] === {label} ===");
|
||||
|
||||
var appContext = InjectorAppContext.Instance;
|
||||
if (appContext == null)
|
||||
{
|
||||
Debug.Log("[Services] InjectorAppContext not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.Log($"[Services] AppContext Initialized: {appContext.IsInitialized}");
|
||||
|
||||
// App 라이프사이클 서비스 확인
|
||||
var logger = appContext.TryGet<ILogService>();
|
||||
var audio = appContext.TryGet<IAudioManager>();
|
||||
var game = appContext.TryGet<IGameService>();
|
||||
var ui = appContext.TryGet<IUIManager>();
|
||||
var settings = appContext.TryGet<InjectorSampleSettingsManager>();
|
||||
|
||||
Debug.Log($"[Services] App Services:");
|
||||
Debug.Log($" - ILogService: {(logger != null ? "OK" : "NULL")}");
|
||||
Debug.Log($" - IAudioManager: {(audio != null ? "OK" : "NULL")} {GetMonoBehaviourStatus(audio)}");
|
||||
Debug.Log($" - IGameService: {(game != null ? "OK" : "NULL")}");
|
||||
Debug.Log($" - IUIManager: {(ui != null ? "OK" : "NULL")} {GetMonoBehaviourStatus(ui)}");
|
||||
Debug.Log($" - SettingsManager: {(settings != null ? "OK" : "NULL")}");
|
||||
|
||||
// Scene 라이프사이클 서비스 확인
|
||||
var sceneContext = FindAnyObjectByType<InjectorSceneContext>();
|
||||
Debug.Log($"[Services] SceneContext: {(sceneContext != null ? $"Found ({sceneContext.IsInitialized})" : "NULL")}");
|
||||
|
||||
var levelManager = appContext.TryGet<InjectorSampleLevelManager>();
|
||||
var enemySpawner = appContext.TryGet<InjectorSampleEnemySpawner>();
|
||||
|
||||
Debug.Log($"[Services] Scene Services:");
|
||||
Debug.Log($" - LevelManager: {(levelManager != null ? "OK" : "NULL (expected after scene change)")}");
|
||||
Debug.Log($" - EnemySpawner: {(enemySpawner != null ? "OK" : "NULL (expected after scene change)")} {GetMonoBehaviourStatus(enemySpawner)}");
|
||||
}
|
||||
|
||||
private string GetMonoBehaviourStatus(object obj)
|
||||
{
|
||||
if (obj is MonoBehaviour mb)
|
||||
{
|
||||
if (mb == null) return "(Destroyed)";
|
||||
return $"(GameObject: {mb.gameObject.name})";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private static string FormatBytes(long bytes)
|
||||
{
|
||||
string[] sizes = { "B", "KB", "MB", "GB" };
|
||||
int order = 0;
|
||||
double size = bytes;
|
||||
|
||||
while (size >= 1024 && order < sizes.Length - 1)
|
||||
{
|
||||
order++;
|
||||
size /= 1024;
|
||||
}
|
||||
|
||||
return $"{size:0.##} {sizes[order]}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Static 버전의 메모리 상태 로그 - 씬 전환 중에도 호출 가능
|
||||
/// </summary>
|
||||
private static void LogMemoryStatusStatic(string label)
|
||||
{
|
||||
long totalMemory = UnityEngine.Profiling.Profiler.GetTotalReservedMemoryLong();
|
||||
long usedMemory = UnityEngine.Profiling.Profiler.GetTotalAllocatedMemoryLong();
|
||||
long monoHeap = UnityEngine.Profiling.Profiler.GetMonoHeapSizeLong();
|
||||
long monoUsed = UnityEngine.Profiling.Profiler.GetMonoUsedSizeLong();
|
||||
|
||||
Debug.Log($"[Memory] === {label} ===");
|
||||
Debug.Log($"[Memory] Total Reserved: {FormatBytes(totalMemory)}");
|
||||
Debug.Log($"[Memory] Total Allocated: {FormatBytes(usedMemory)}");
|
||||
Debug.Log($"[Memory] Mono Heap: {FormatBytes(monoHeap)}");
|
||||
Debug.Log($"[Memory] Mono Used: {FormatBytes(monoUsed)}");
|
||||
Debug.Log($"[Memory] GC Total Memory: {FormatBytes(System.GC.GetTotalMemory(false))}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Static 버전의 서비스 상태 로그 - 씬 전환 중에도 호출 가능
|
||||
/// </summary>
|
||||
private static void LogServiceStatusStatic(string label)
|
||||
{
|
||||
Debug.Log($"[Services] === {label} ===");
|
||||
|
||||
var appContext = InjectorAppContext.Instance;
|
||||
if (appContext == null)
|
||||
{
|
||||
Debug.Log("[Services] InjectorAppContext not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.Log($"[Services] AppContext Initialized: {appContext.IsInitialized}");
|
||||
|
||||
// App 라이프사이클 서비스 확인
|
||||
var logger = appContext.TryGet<ILogService>();
|
||||
var audio = appContext.TryGet<IAudioManager>();
|
||||
var game = appContext.TryGet<IGameService>();
|
||||
var ui = appContext.TryGet<IUIManager>();
|
||||
var settings = appContext.TryGet<InjectorSampleSettingsManager>();
|
||||
|
||||
Debug.Log($"[Services] App Services:");
|
||||
Debug.Log($" - ILogService: {(logger != null ? "OK" : "NULL")}");
|
||||
Debug.Log($" - IAudioManager: {(audio != null ? "OK" : "NULL")} {GetMonoBehaviourStatusStatic(audio)}");
|
||||
Debug.Log($" - IGameService: {(game != null ? "OK" : "NULL")}");
|
||||
Debug.Log($" - IUIManager: {(ui != null ? "OK" : "NULL")} {GetMonoBehaviourStatusStatic(ui)}");
|
||||
Debug.Log($" - SettingsManager: {(settings != null ? "OK" : "NULL")}");
|
||||
|
||||
// Scene 라이프사이클 서비스 확인
|
||||
var sceneContext = Object.FindAnyObjectByType<InjectorSceneContext>();
|
||||
Debug.Log($"[Services] SceneContext: {(sceneContext != null ? $"Found ({sceneContext.IsInitialized})" : "NULL")}");
|
||||
|
||||
var levelManager = appContext.TryGet<InjectorSampleLevelManager>();
|
||||
var enemySpawner = appContext.TryGet<InjectorSampleEnemySpawner>();
|
||||
|
||||
Debug.Log($"[Services] Scene Services:");
|
||||
Debug.Log($" - LevelManager: {(levelManager != null ? "OK" : "NULL (expected after scene change)")}");
|
||||
Debug.Log($" - EnemySpawner: {(enemySpawner != null ? "OK" : "NULL (expected after scene change)")} {GetMonoBehaviourStatusStatic(enemySpawner)}");
|
||||
}
|
||||
|
||||
private static string GetMonoBehaviourStatusStatic(object obj)
|
||||
{
|
||||
if (obj is MonoBehaviour mb)
|
||||
{
|
||||
if (mb == null) return "(Destroyed)";
|
||||
return $"(GameObject: {mb.gameObject.name})";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 메모리 누수 테스트 - 여러 번 씬 전환 후 메모리 증가 확인
|
||||
/// </summary>
|
||||
/// <param name="iterations">반복 횟수</param>
|
||||
[ContextMenu("Test Memory Leak (5 iterations)")]
|
||||
public void TestMemoryLeak()
|
||||
{
|
||||
TestMemoryLeak(5);
|
||||
}
|
||||
|
||||
public void TestMemoryLeak(int iterations)
|
||||
{
|
||||
StartCoroutine(MemoryLeakTestCoroutine(iterations));
|
||||
}
|
||||
|
||||
private System.Collections.IEnumerator MemoryLeakTestCoroutine(int iterations)
|
||||
{
|
||||
Debug.Log($"========== Memory Leak Test ({iterations} iterations) ==========");
|
||||
string currentScene = SceneManager.GetActiveScene().name;
|
||||
|
||||
long initialMemory = System.GC.GetTotalMemory(true);
|
||||
Debug.Log($"[MemoryLeak] Initial GC Memory: {FormatBytes(initialMemory)}");
|
||||
|
||||
for (int i = 0; i < iterations; i++)
|
||||
{
|
||||
Debug.Log($"[MemoryLeak] Iteration {i + 1}/{iterations}");
|
||||
|
||||
// 씬 리로드
|
||||
var loadOp = SceneManager.LoadSceneAsync(currentScene);
|
||||
yield return loadOp;
|
||||
|
||||
// 1초 대기
|
||||
yield return _waitOneSecond;
|
||||
|
||||
// GC 실행
|
||||
System.GC.Collect();
|
||||
System.GC.WaitForPendingFinalizers();
|
||||
System.GC.Collect();
|
||||
|
||||
yield return Resources.UnloadUnusedAssets();
|
||||
|
||||
long currentMemory = System.GC.GetTotalMemory(false);
|
||||
long diff = currentMemory - initialMemory;
|
||||
Debug.Log($"[MemoryLeak] Iteration {i + 1} - GC Memory: {FormatBytes(currentMemory)} (diff: {FormatBytes(diff)})");
|
||||
}
|
||||
|
||||
long finalMemory = System.GC.GetTotalMemory(true);
|
||||
long totalDiff = finalMemory - initialMemory;
|
||||
|
||||
Debug.Log($"[MemoryLeak] Final GC Memory: {FormatBytes(finalMemory)}");
|
||||
Debug.Log($"[MemoryLeak] Total Difference: {FormatBytes(totalDiff)}");
|
||||
|
||||
if (totalDiff > 1024 * 1024) // 1MB 이상 증가
|
||||
{
|
||||
Debug.LogWarning($"[MemoryLeak] Potential memory leak detected! Memory increased by {FormatBytes(totalDiff)}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("[MemoryLeak] No significant memory leak detected.");
|
||||
}
|
||||
|
||||
Debug.Log("========== Memory Leak Test Completed ==========");
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 동적 생성 테스트용 컴포넌트
|
||||
/// </summary>
|
||||
public class InjectorSampleDynamicTestComponent : MonoBehaviour
|
||||
{
|
||||
[Inject] private ILogService _logger;
|
||||
[Inject] private IAudioManager _audioManager;
|
||||
|
||||
public void Test()
|
||||
{
|
||||
_logger?.Log("DynamicTestComponent: Injection successful!");
|
||||
_audioManager?.PlaySFX("test_sound");
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Sample/Injector/InjectorSampleTest.cs.meta
Normal file
2
Assets/Sample/Injector/InjectorSampleTest.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2b975ab877e73b1418a39771634c34d4
|
||||
95
Assets/Sample/Injector/InjectorSampleUIManager.cs
Normal file
95
Assets/Sample/Injector/InjectorSampleUIManager.cs
Normal file
@@ -0,0 +1,95 @@
|
||||
using UnityEngine;
|
||||
using UVC.Core;
|
||||
|
||||
namespace Sample
|
||||
{
|
||||
/// <summary>
|
||||
/// Type C 예시: Prefab 기반 MonoBehaviour - UI 매니저
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para><b>[ 타입 ]</b> Type C - Prefab 기반 MonoBehaviour</para>
|
||||
/// <para><b>[ 라이프사이클 ]</b> App - 씬 전환 시에도 유지</para>
|
||||
///
|
||||
/// <para><b>[ 특징 ]</b></para>
|
||||
/// <list type="bullet">
|
||||
/// <item><description>프리팹에 미리 설정된 UI 요소들이 Instantiate 시 유지됨</description></item>
|
||||
/// <item><description>Inspector에서 Canvas, Panel, Button 등 시각적으로 구성 가능</description></item>
|
||||
/// <item><description>디자이너와 협업 시 유용 (코드 수정 없이 UI 변경 가능)</description></item>
|
||||
/// <item><description>다른 서비스([Inject] ILogService)에 대한 의존성 주입 지원</description></item>
|
||||
/// </list>
|
||||
///
|
||||
/// <para><b>[ 프리팹 생성 방법 ]</b></para>
|
||||
/// <list type="number">
|
||||
/// <item><description>Hierarchy에서 Canvas 생성</description></item>
|
||||
/// <item><description>Canvas 하위에 Popup Panel, Loading Panel 등 UI 요소 배치</description></item>
|
||||
/// <item><description>Canvas에 InjectorSampleUIManager 컴포넌트 추가</description></item>
|
||||
/// <item><description>Inspector에서 popupPanel, loadingPanel 필드에 해당 GameObject 연결</description></item>
|
||||
/// <item><description>Project 폴더로 드래그하여 프리팹화</description></item>
|
||||
/// </list>
|
||||
///
|
||||
/// <para><b>[ 등록 방법 ]</b></para>
|
||||
/// <code>
|
||||
/// // InjectorSampleAppContext에서
|
||||
/// [SerializeField] private InjectorSampleUIManager uiManagerPrefab;
|
||||
///
|
||||
/// protected override void RegisterServices()
|
||||
/// {
|
||||
/// Injector.RegisterPrefab<IUIManager>(uiManagerPrefab.gameObject, ServiceLifetime.App);
|
||||
/// }
|
||||
/// </code>
|
||||
///
|
||||
/// <para><b>[ 사용 방법 ]</b></para>
|
||||
/// <code>
|
||||
/// [Inject] private IUIManager _uiManager;
|
||||
/// _uiManager.ShowPopup("알림", "저장되었습니다.");
|
||||
/// _uiManager.ShowLoading();
|
||||
/// </code>
|
||||
///
|
||||
/// <para><b>[ Type B(동적 생성)와의 차이점 ]</b></para>
|
||||
/// <list type="bullet">
|
||||
/// <item><description>Type B: 빈 GameObject에 컴포넌트만 추가, Inspector 설정 불가</description></item>
|
||||
/// <item><description>Type C: 프리팹의 모든 설정(자식 오브젝트, 컴포넌트 값 등) 유지</description></item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
public class InjectorSampleUIManager : MonoBehaviour, IUIManager
|
||||
{
|
||||
[Header("UI References")]
|
||||
[SerializeField]
|
||||
[Tooltip("팝업 패널 GameObject - 프리팹에서 연결")]
|
||||
private GameObject popupPanel;
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("로딩 패널 GameObject - 프리팹에서 연결")]
|
||||
private GameObject loadingPanel;
|
||||
|
||||
[Inject] private ILogService _logger;
|
||||
|
||||
/// <summary>팝업을 표시합니다.</summary>
|
||||
public void ShowPopup(string title, string message)
|
||||
{
|
||||
_logger?.Log($"Show Popup: {title} - {message}");
|
||||
if (popupPanel != null) popupPanel.SetActive(true);
|
||||
}
|
||||
|
||||
/// <summary>팝업을 숨깁니다.</summary>
|
||||
public void HidePopup()
|
||||
{
|
||||
_logger?.Log("Hide Popup");
|
||||
if (popupPanel != null) popupPanel.SetActive(false);
|
||||
}
|
||||
|
||||
/// <summary>로딩 UI를 표시합니다.</summary>
|
||||
public void ShowLoading()
|
||||
{
|
||||
_logger?.Log("Show Loading");
|
||||
if (loadingPanel != null) loadingPanel.SetActive(true);
|
||||
}
|
||||
|
||||
/// <summary>로딩 UI를 숨깁니다.</summary>
|
||||
public void HideLoading()
|
||||
{
|
||||
_logger?.Log("Hide Loading");
|
||||
if (loadingPanel != null) loadingPanel.SetActive(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Sample/Injector/InjectorSampleUIManager.cs.meta
Normal file
2
Assets/Sample/Injector/InjectorSampleUIManager.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: df100fc1db5fd6c4a89e2d19ec4a086d
|
||||
48
Assets/Sample/Injector/InjectorSampleUIManager.prefab
Normal file
48
Assets/Sample/Injector/InjectorSampleUIManager.prefab
Normal file
@@ -0,0 +1,48 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &5034473315543819875
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 7928633308645434308}
|
||||
- component: {fileID: 4150560687353231002}
|
||||
m_Layer: 0
|
||||
m_Name: InjectorSampleUIManager
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &7928633308645434308
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5034473315543819875}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: -24.16767, y: -42.30578, z: 82.73528}
|
||||
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 &4150560687353231002
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5034473315543819875}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: df100fc1db5fd6c4a89e2d19ec4a086d, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::Sample.InjectorSampleUIManager
|
||||
popupPanel: {fileID: 906533046013280643, guid: 32c4e31457a0cf941b7c048dd1ac2b5e, type: 3}
|
||||
loadingPanel: {fileID: 6021452925437567283, guid: a6236378cb37a6b478b40be07700502e, type: 3}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4b22965ad1b38c84b9756d29e5358680
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
290
Assets/Sample/Injector/InjectorSamples.cs
Normal file
290
Assets/Sample/Injector/InjectorSamples.cs
Normal file
@@ -0,0 +1,290 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UVC.Core;
|
||||
|
||||
namespace Sample
|
||||
{
|
||||
/*
|
||||
* ============================================================================
|
||||
* Injector 샘플 - 인터페이스 및 순수 C# 클래스
|
||||
* ============================================================================
|
||||
*
|
||||
* 이 파일에는 MonoBehaviour가 아닌 인터페이스와 순수 C# 클래스가 정의되어 있습니다.
|
||||
* MonoBehaviour를 상속받는 클래스들은 별도 파일로 분리되어 있습니다.
|
||||
*
|
||||
* [ 4가지 등록 타입 ]
|
||||
* - Type A: 순수 C# 클래스 (new T()로 생성)
|
||||
* - Type B: MonoBehaviour 동적 생성 (별도 파일: InjectorSampleAudioManager.cs 등)
|
||||
* - Type C: Prefab 기반 MonoBehaviour (별도 파일: InjectorSampleUIManager.cs 등)
|
||||
* - Type D: Singleton 연동 (별도 파일: InjectorSampleSettingsManager.cs 등)
|
||||
*
|
||||
* [ 3가지 라이프사이클 ]
|
||||
* - App: 애플리케이션 전체 유지 (DontDestroyOnLoad)
|
||||
* - Scene: 현재 씬에서만 유지 (씬 전환 시 해제)
|
||||
* - Transient: 매번 새 인스턴스 생성
|
||||
*
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
#region ========== Type A: 순수 C# 인터페이스 및 클래스 ==========
|
||||
|
||||
/// <summary>
|
||||
/// 로깅 서비스 인터페이스 - 애플리케이션 전역 로깅 기능을 제공합니다.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para><b>구현체:</b> ConsoleLogger</para>
|
||||
/// <para><b>등록:</b> Injector.Register<ILogService, ConsoleLogger>(ServiceLifetime.App)</para>
|
||||
/// <para><b>사용:</b> [Inject] private ILogService _logger;</para>
|
||||
/// </remarks>
|
||||
public interface ILogService
|
||||
{
|
||||
/// <summary>일반 로그 메시지를 출력합니다.</summary>
|
||||
void Log(string message);
|
||||
/// <summary>경고 로그 메시지를 출력합니다.</summary>
|
||||
void LogWarning(string message);
|
||||
/// <summary>에러 로그 메시지를 출력합니다.</summary>
|
||||
void LogError(string message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Type A 예시: 순수 C# 클래스 - Unity Debug.Log를 래핑한 콘솔 로거
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para><b>타입:</b> Type A - 순수 C# 클래스</para>
|
||||
/// <para><b>라이프사이클:</b> App (애플리케이션 전체 유지)</para>
|
||||
/// <para><b>특징:</b></para>
|
||||
/// <list type="bullet">
|
||||
/// <item><description>MonoBehaviour가 아니므로 GameObject 없이 동작</description></item>
|
||||
/// <item><description>new ConsoleLogger()로 생성됨</description></item>
|
||||
/// <item><description>모든 로그에 [ConsoleLogger] 접두어 추가</description></item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
public class ConsoleLogger : ILogService
|
||||
{
|
||||
private readonly string _prefix = "[ConsoleLogger]";
|
||||
|
||||
public void Log(string message) => Debug.Log($"{_prefix} {message}");
|
||||
public void LogWarning(string message) => Debug.LogWarning($"{_prefix} {message}");
|
||||
public void LogError(string message) => Debug.LogError($"{_prefix} {message}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 씬 설정 인터페이스 - 현재 씬의 설정 정보를 제공합니다.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para><b>구현체:</b> SceneConfig</para>
|
||||
/// <para><b>등록:</b> Injector.Register<ISceneConfig, SceneConfig>(ServiceLifetime.Scene)</para>
|
||||
/// <para><b>또는 Factory:</b> Injector.RegisterFactory<ISceneConfig>(injector => new SceneConfig { ... })</para>
|
||||
/// </remarks>
|
||||
public interface ISceneConfig
|
||||
{
|
||||
/// <summary>현재 씬 이름</summary>
|
||||
string SceneName { get; }
|
||||
/// <summary>난이도 배율 (1.0 = 기본)</summary>
|
||||
float Difficulty { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Type A 예시: 순수 C# 클래스 - 씬별 설정 정보
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para><b>타입:</b> Type A - 순수 C# 클래스</para>
|
||||
/// <para><b>라이프사이클:</b> Scene (씬 전환 시 해제) 또는 App (Factory 사용 시)</para>
|
||||
/// <para><b>용도:</b> 씬별 난이도, 이름 등 설정 정보 관리</para>
|
||||
/// </remarks>
|
||||
public class SceneConfig : ISceneConfig
|
||||
{
|
||||
public string SceneName { get; set; } = "Default";
|
||||
public float Difficulty { get; set; } = 1.0f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 게임 서비스 인터페이스 - 게임 초기화 및 저장 기능을 제공합니다.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para><b>구현체:</b> GameService</para>
|
||||
/// <para><b>특징:</b> 다른 서비스(ILogService)에 대한 의존성을 가짐</para>
|
||||
/// </remarks>
|
||||
public interface IGameService
|
||||
{
|
||||
/// <summary>게임을 초기화합니다.</summary>
|
||||
void Initialize();
|
||||
/// <summary>게임을 저장합니다.</summary>
|
||||
void SaveGame();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Type A 예시: 순수 C# 클래스 - 다른 서비스에 대한 의존성 주입
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para><b>타입:</b> Type A - 순수 C# 클래스</para>
|
||||
/// <para><b>라이프사이클:</b> App</para>
|
||||
/// <para><b>핵심 포인트:</b></para>
|
||||
/// <list type="bullet">
|
||||
/// <item><description>순수 C# 클래스도 [Inject] 어트리뷰트로 의존성 주입 가능</description></item>
|
||||
/// <item><description>Injector가 인스턴스 생성 후 자동으로 ILogService를 주입</description></item>
|
||||
/// <item><description>생성자 주입이 아닌 필드 주입 방식 사용</description></item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // 등록
|
||||
/// Injector.Register<IGameService, GameService>(ServiceLifetime.App);
|
||||
///
|
||||
/// // 사용
|
||||
/// [Inject] private IGameService _gameService;
|
||||
/// _gameService.Initialize();
|
||||
/// </code>
|
||||
/// </example>
|
||||
public class GameService : IGameService
|
||||
{
|
||||
[Inject] private ILogService _logger;
|
||||
|
||||
public void Initialize() => _logger?.Log("GameService initialized");
|
||||
public void SaveGame() => _logger?.Log("Game saved!");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ========== Type B: MonoBehaviour 인터페이스 ==========
|
||||
|
||||
/// <summary>
|
||||
/// 오디오 매니저 인터페이스 - BGM/SFX 재생 기능을 제공합니다.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para><b>구현체:</b> InjectorSampleAudioManager (별도 파일)</para>
|
||||
/// <para><b>타입:</b> Type B - MonoBehaviour 동적 생성</para>
|
||||
/// <para><b>등록:</b> Injector.Register<IAudioManager, InjectorSampleAudioManager>(ServiceLifetime.App)</para>
|
||||
/// <para><b>특징:</b> 런타임에 새 GameObject가 생성되고 AudioSource 등 Unity 컴포넌트 활용 가능</para>
|
||||
/// </remarks>
|
||||
public interface IAudioManager
|
||||
{
|
||||
/// <summary>효과음을 재생합니다.</summary>
|
||||
void PlaySFX(string clipName);
|
||||
/// <summary>배경음악을 재생합니다.</summary>
|
||||
void PlayBGM(string clipName);
|
||||
/// <summary>모든 오디오를 중지합니다.</summary>
|
||||
void StopAll();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 적 스포너 인터페이스 - 적 오브젝트 생성 기능을 제공합니다.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para><b>구현체:</b> InjectorSampleEnemySpawner (별도 파일)</para>
|
||||
/// <para><b>타입:</b> Type B - MonoBehaviour 동적 생성</para>
|
||||
/// <para><b>등록:</b> Injector.Register<IEnemySpawner, InjectorSampleEnemySpawner>(ServiceLifetime.Scene)</para>
|
||||
/// <para><b>라이프사이클:</b> Scene - 씬 전환 시 적 스포너와 생성된 적들이 함께 정리됨</para>
|
||||
/// </remarks>
|
||||
public interface IEnemySpawner
|
||||
{
|
||||
/// <summary>지정된 위치에 적을 스폰합니다.</summary>
|
||||
void SpawnEnemy(Vector3 position);
|
||||
/// <summary>현재 활성화된 적의 수</summary>
|
||||
int EnemyCount { get; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ========== Type C: Prefab 기반 인터페이스 ==========
|
||||
|
||||
/// <summary>
|
||||
/// UI 매니저 인터페이스 - 팝업 및 로딩 UI 기능을 제공합니다.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para><b>구현체:</b> InjectorSampleUIManager (별도 파일)</para>
|
||||
/// <para><b>타입:</b> Type C - Prefab 기반 MonoBehaviour</para>
|
||||
/// <para><b>등록:</b> Injector.RegisterPrefab<IUIManager>(uiManagerPrefab, ServiceLifetime.App)</para>
|
||||
/// <para><b>특징:</b></para>
|
||||
/// <list type="bullet">
|
||||
/// <item><description>프리팹에 미리 설정된 UI 요소들이 유지됨</description></item>
|
||||
/// <item><description>Inspector에서 시각적으로 UI 구성 가능</description></item>
|
||||
/// <item><description>Canvas, Button, Text 등 Unity UI 컴포넌트 활용</description></item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
public interface IUIManager
|
||||
{
|
||||
/// <summary>팝업을 표시합니다.</summary>
|
||||
void ShowPopup(string title, string message);
|
||||
/// <summary>팝업을 숨깁니다.</summary>
|
||||
void HidePopup();
|
||||
/// <summary>로딩 UI를 표시합니다.</summary>
|
||||
void ShowLoading();
|
||||
/// <summary>로딩 UI를 숨깁니다.</summary>
|
||||
void HideLoading();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 씬 UI 인터페이스 - 씬별 HUD 기능을 제공합니다.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para><b>구현체:</b> InjectorSampleSceneUI (별도 파일)</para>
|
||||
/// <para><b>타입:</b> Type C - Prefab 기반 MonoBehaviour</para>
|
||||
/// <para><b>등록:</b> Injector.RegisterPrefab<ISceneUI>(sceneUIPrefab, ServiceLifetime.Scene)</para>
|
||||
/// <para><b>라이프사이클:</b> Scene - 씬 전환 시 UI가 함께 정리됨</para>
|
||||
/// </remarks>
|
||||
public interface ISceneUI
|
||||
{
|
||||
/// <summary>씬 UI를 초기화합니다.</summary>
|
||||
void Initialize();
|
||||
/// <summary>HUD를 표시합니다.</summary>
|
||||
void ShowHUD();
|
||||
/// <summary>HUD를 숨깁니다.</summary>
|
||||
void HideHUD();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ========== Transient 라이프사이클 ==========
|
||||
|
||||
/// <summary>
|
||||
/// 요청 핸들러 인터페이스 - 개별 요청 처리 기능을 제공합니다.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para><b>구현체:</b> RequestHandler</para>
|
||||
/// <para><b>라이프사이클:</b> Transient - 매번 새 인스턴스 생성</para>
|
||||
/// <para><b>용도:</b> HTTP 요청, 이벤트 처리 등 상태를 공유하지 않아야 하는 경우</para>
|
||||
/// </remarks>
|
||||
public interface IRequestHandler
|
||||
{
|
||||
/// <summary>고유 요청 ID</summary>
|
||||
string RequestId { get; }
|
||||
/// <summary>데이터를 처리합니다.</summary>
|
||||
void Process(string data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transient 라이프사이클 예시: 매번 새 인스턴스가 생성되는 클래스
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para><b>라이프사이클:</b> Transient</para>
|
||||
/// <para><b>특징:</b></para>
|
||||
/// <list type="bullet">
|
||||
/// <item><description>Resolve 호출 시마다 새 인스턴스 생성</description></item>
|
||||
/// <item><description>각 인스턴스는 고유한 RequestId를 가짐</description></item>
|
||||
/// <item><description>상태를 공유하지 않아야 하는 경우에 적합</description></item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // 등록
|
||||
/// Injector.Register<IRequestHandler, RequestHandler>(ServiceLifetime.Transient);
|
||||
///
|
||||
/// // 사용 - 매번 다른 인스턴스
|
||||
/// var handler1 = Injector.Resolve<IRequestHandler>(); // RequestId: "a1b2c3d4"
|
||||
/// var handler2 = Injector.Resolve<IRequestHandler>(); // RequestId: "e5f6g7h8"
|
||||
/// </code>
|
||||
/// </example>
|
||||
public class RequestHandler : IRequestHandler
|
||||
{
|
||||
[Inject] private ILogService _logger;
|
||||
public string RequestId { get; } = Guid.NewGuid().ToString().Substring(0, 8);
|
||||
|
||||
public void Process(string data)
|
||||
{
|
||||
_logger?.Log($"[{RequestId}] Processing: {data}");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
2
Assets/Sample/Injector/InjectorSamples.cs.meta
Normal file
2
Assets/Sample/Injector/InjectorSamples.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 56717ccb574095e48b3d4e6f02b251c1
|
||||
@@ -7,6 +7,9 @@ using UVC.Util;
|
||||
|
||||
public class PropertyWindowSample : MonoBehaviour
|
||||
{
|
||||
|
||||
[SerializeField] private PropertyWindow propertyWindow;
|
||||
|
||||
// Start is called once before the first execution of Update after the MonoBehaviour is created
|
||||
void Start()
|
||||
{
|
||||
@@ -75,16 +78,16 @@ public class PropertyWindowSample : MonoBehaviour
|
||||
}
|
||||
};
|
||||
|
||||
PropertyWindow.Instance.LoadMixedProperties(entries);
|
||||
propertyWindow.LoadMixedProperties(entries);
|
||||
|
||||
// 값 변경 이벤트 핸들러
|
||||
PropertyWindow.Instance.PropertyValueChanged += (sender, e) =>
|
||||
propertyWindow.PropertyValueChanged += (sender, e) =>
|
||||
{
|
||||
Debug.Log($"[PropertyChanged] Id:{e.PropertyId}, Type:{e.PropertyType}, Value:{e.NewValue}");
|
||||
};
|
||||
|
||||
// 그룹 펼침/접힘 이벤트 핸들러
|
||||
PropertyWindow.Instance.GroupExpandedChanged += (sender, e) =>
|
||||
propertyWindow.GroupExpandedChanged += (sender, e) =>
|
||||
{
|
||||
Debug.Log($"[GroupExpanded] Group:{e.Group.GroupName}, Expanded:{e.IsExpanded}");
|
||||
};
|
||||
|
||||
@@ -319,6 +319,7 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: 3e3d877c5ac2e9744a8e91e85fbfa44d, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
propertyWindow: {fileID: 512550211}
|
||||
--- !u!4 &312817880
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -358,6 +359,22 @@ PrefabInstance:
|
||||
propertyPath: m_Name
|
||||
value: PropertyWindow
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4618630843252738151, guid: 4b98d7ee8b805ff42be384e91f3bf8a4, type: 3}
|
||||
propertyPath: m_AnchorMax.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4618630843252738151, guid: 4b98d7ee8b805ff42be384e91f3bf8a4, type: 3}
|
||||
propertyPath: m_AnchorMin.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4618630843252738151, guid: 4b98d7ee8b805ff42be384e91f3bf8a4, type: 3}
|
||||
propertyPath: m_AnchoredPosition.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4618630843252738151, guid: 4b98d7ee8b805ff42be384e91f3bf8a4, type: 3}
|
||||
propertyPath: m_AnchoredPosition.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7125265927081152491, guid: 4b98d7ee8b805ff42be384e91f3bf8a4, type: 3}
|
||||
propertyPath: m_SizeDelta.y
|
||||
value: 0
|
||||
@@ -452,6 +469,17 @@ RectTransform:
|
||||
m_CorrespondingSourceObject: {fileID: 8065352563668446013, guid: 4b98d7ee8b805ff42be384e91f3bf8a4, type: 3}
|
||||
m_PrefabInstance: {fileID: 512550209}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
--- !u!114 &512550211 stripped
|
||||
MonoBehaviour:
|
||||
m_CorrespondingSourceObject: {fileID: 8243371294842205870, guid: 4b98d7ee8b805ff42be384e91f3bf8a4, type: 3}
|
||||
m_PrefabInstance: {fileID: 512550209}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 1bbe888d699e08c41bd01f39bb566480, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
--- !u!1001 &676413027
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using UnityEngine;
|
||||
using UVC.Data;
|
||||
using UVC.Data.Mqtt;
|
||||
using UVC.network;
|
||||
using UVC.Network;
|
||||
|
||||
public class WebGLSample : MonoBehaviour
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user