DI 추가

This commit is contained in:
logonkhi
2025-12-15 20:17:38 +09:00
parent ab86affa32
commit 0df2f0d8da
131 changed files with 19661 additions and 554 deletions

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 0562dcfb29668494badcb7b04e5e55f5
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View 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}

View File

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

View 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");
}
}
}

View File

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

View 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&lt;T&gt;()로 생성</description></item>
/// <item><description>App 라이프사이클이면 DontDestroyOnLoad 자동 적용</description></item>
/// <item><description>다른 서비스([Inject] ILogService)에 대한 의존성 주입 지원</description></item>
/// </list>
///
/// <para><b>[ 등록 방법 ]</b></para>
/// <code>Injector.Register&lt;IAudioManager, InjectorSampleAudioManager&gt;(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&lt;AudioSource&gt;();
/// sfxSource = gameObject.AddComponent&lt;AudioSource&gt;();
/// 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");
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 6ff95a63fd64135439e1b50f5447c996

View 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);
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 13788b24c7d924e4a8e7a993c744131c

View 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

View File

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

View 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}");
}
}
}

View File

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

View 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();
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 26fa406f6120d7f48b3f2aaef4d7e1ec

View 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}");
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 728473fae6f9a0a47b0af68dbeefe92d

View 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}

View File

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

View 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&lt;T&gt; 상속)</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&lt;InjectorSampleNetworkManager&gt;();</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&lt;T&gt; - 순수 C#, GameObject 없음</description></item>
/// <item><term>NetworkManager</term><description>SingletonApp&lt;T&gt; - 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;
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 39e835c57c05187499737b002734574f

View 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");
}
}
}

View File

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

View 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}

View File

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

View 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");
}
}
}

View File

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

View 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);
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 249dbfa6b707154418519e4f9b85c150

View 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}

View File

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

View 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}

View File

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

View 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&lt;T&gt; 상속)</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&lt;InjectorSampleSettingsManager&gt;();</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");
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 78ed6c29cec050048a9d62ce124694a7

View 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");
}
}
}

View File

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

View 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&lt;IUIManager&gt;(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);
}
}
}

View File

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

View 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}

View File

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

View 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&lt;ILogService, ConsoleLogger&gt;(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&lt;ISceneConfig, SceneConfig&gt;(ServiceLifetime.Scene)</para>
/// <para><b>또는 Factory:</b> Injector.RegisterFactory&lt;ISceneConfig&gt;(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&lt;IGameService, GameService&gt;(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&lt;IAudioManager, InjectorSampleAudioManager&gt;(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&lt;IEnemySpawner, InjectorSampleEnemySpawner&gt;(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&lt;IUIManager&gt;(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&lt;ISceneUI&gt;(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&lt;IRequestHandler, RequestHandler&gt;(ServiceLifetime.Transient);
///
/// // 사용 - 매번 다른 인스턴스
/// var handler1 = Injector.Resolve&lt;IRequestHandler&gt;(); // RequestId: "a1b2c3d4"
/// var handler2 = Injector.Resolve&lt;IRequestHandler&gt;(); // 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
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 56717ccb574095e48b3d4e6f02b251c1

View File

@@ -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}");
};

View File

@@ -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

View File

@@ -1,7 +1,7 @@
using UnityEngine;
using UVC.Data;
using UVC.Data.Mqtt;
using UVC.network;
using UVC.Network;
public class WebGLSample : MonoBehaviour
{