auto: 2026-04-23 23:47 · scene: SampleScene · 20 files

This commit is contained in:
깃 관리자 2026-04-23 23:47:51 +09:00
parent 9f689c1661
commit a10c38605d
20 changed files with 257070 additions and 31 deletions

View File

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

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 50dac50a87be5624191ce4a471d65653

View File

@ -16,6 +16,7 @@ GameObject:
- component: {fileID: 2651140156555518892} - component: {fileID: 2651140156555518892}
- component: {fileID: 772289407653213039} - component: {fileID: 772289407653213039}
- component: {fileID: 5843668731025413174} - component: {fileID: 5843668731025413174}
- component: {fileID: 7700000000000000002}
m_Layer: 13 m_Layer: 13
m_Name: Enemy m_Name: Enemy
m_TagString: Player m_TagString: Player
@ -277,3 +278,16 @@ AudioSource:
m_PreInfinity: 2 m_PreInfinity: 2
m_PostInfinity: 2 m_PostInfinity: 2
m_RotationOrder: 4 m_RotationOrder: 4
--- !u!114 &7700000000000000002
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1661912868639658944}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: ffad43bb006db4856a9c527b89b48db9, type: 3}
m_Name:
m_EditorClassIdentifier:
maxHP: 1

View File

@ -0,0 +1,279 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &1661912868639658944
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1658460978237467174}
- component: {fileID: 1577774018119691272}
- component: {fileID: 1605217082131907960}
- component: {fileID: 1702612949800919892}
- component: {fileID: 1724124757368974630}
- component: {fileID: 2651140156555518892}
- component: {fileID: 772289407653213039}
- component: {fileID: 5843668731025413174}
m_Layer: 13
m_Name: Enemy
m_TagString: Player
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &1658460978237467174
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1661912868639658944}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 5.508, y: 1.032, z: 1}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!212 &1577774018119691272
SpriteRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1661912868639658944}
m_Enabled: 1
m_CastShadows: 0
m_ReceiveShadows: 0
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 0
m_ReflectionProbeUsage: 0
m_RayTracingMode: 0
m_RayTraceProcedural: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: 9dfc825aed78fcd4ba02077103263b40, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 0
m_SelectedEditorRenderState: 0
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 1907945055
m_SortingLayer: 0
m_SortingOrder: 5
m_Sprite: {fileID: 21300000, guid: fe68c6e1242e94a9eab222e1f49440ff, type: 3}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_FlipX: 0
m_FlipY: 0
m_DrawMode: 0
m_Size: {x: 1.28, y: 1.26}
m_AdaptiveModeThreshold: 0.5
m_SpriteTileMode: 0
m_WasSpriteAssigned: 1
m_MaskInteraction: 0
m_SpriteSortPoint: 0
--- !u!95 &1605217082131907960
Animator:
serializedVersion: 4
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1661912868639658944}
m_Enabled: 1
m_Avatar: {fileID: 0}
m_Controller: {fileID: 9100000, guid: ed1bbb2dccb7a424a9969f916919f446, type: 2}
m_CullingMode: 0
m_UpdateMode: 0
m_ApplyRootMotion: 0
m_LinearVelocityBlending: 0
m_StabilizeFeet: 0
m_WarningMessage:
m_HasTransformHierarchy: 1
m_AllowConstantClipSamplingOptimization: 1
m_KeepAnimatorControllerStateOnDisable: 0
--- !u!50 &1702612949800919892
Rigidbody2D:
serializedVersion: 4
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1661912868639658944}
m_BodyType: 1
m_Simulated: 1
m_UseFullKinematicContacts: 1
m_UseAutoMass: 0
m_Mass: 1
m_LinearDrag: 0
m_AngularDrag: 0.05
m_GravityScale: 1
m_Material: {fileID: 0}
m_Interpolate: 1
m_SleepingMode: 0
m_CollisionDetection: 1
m_Constraints: 4
--- !u!70 &1724124757368974630
CapsuleCollider2D:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1661912868639658944}
m_Enabled: 1
m_Density: 1
m_Material: {fileID: 0}
m_IsTrigger: 0
m_UsedByEffector: 0
m_UsedByComposite: 0
m_Offset: {x: 0, y: -0.11}
m_Size: {x: 0.45, y: 0.09}
m_Direction: 0
--- !u!114 &2651140156555518892
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1661912868639658944}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: be4d02721cd9d4baa869d3b890cec48f, type: 3}
m_Name:
m_EditorClassIdentifier:
minGroundNormalY: 0.65
gravityModifier: 1
velocity: {x: 0, y: 0}
maxSpeed: 7
jumpTakeOffSpeed: 7
move: {x: 0, y: 0}
jump: 0
stopJump: 0
--- !u!114 &772289407653213039
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1661912868639658944}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: cd654acc1f7894a509f3420e5c9ffea6, type: 3}
m_Name:
m_EditorClassIdentifier:
path: {fileID: 0}
ouch: {fileID: 8300000, guid: b7f741588644cd64bbee6387cb54a96d, type: 3}
--- !u!82 &5843668731025413174
AudioSource:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1661912868639658944}
m_Enabled: 1
serializedVersion: 4
OutputAudioMixerGroup: {fileID: 0}
m_audioClip: {fileID: 0}
m_PlayOnAwake: 0
m_Volume: 1
m_Pitch: 1
Loop: 0
Mute: 0
Spatialize: 0
SpatializePostEffects: 0
Priority: 128
DopplerLevel: 1
MinDistance: 1
MaxDistance: 500
Pan2D: 0
rolloffMode: 0
BypassEffects: 0
BypassListenerEffects: 0
BypassReverbZones: 0
rolloffCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
- serializedVersion: 3
time: 1
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
panLevelCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
spreadCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
reverbZoneMixCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4

View File

@ -16,6 +16,7 @@ GameObject:
- component: {fileID: 8509783501685719824} - component: {fileID: 8509783501685719824}
- component: {fileID: 6082575806313374961} - component: {fileID: 6082575806313374961}
- component: {fileID: 1824045667} - component: {fileID: 1824045667}
- component: {fileID: 7700000000000000001}
m_Layer: 13 m_Layer: 13
m_Name: Player m_Name: Player
m_TagString: Player m_TagString: Player
@ -325,3 +326,22 @@ BoxCollider2D:
m_AutoTiling: 0 m_AutoTiling: 0
m_Size: {x: 0.32, y: 0.54} m_Size: {x: 0.32, y: 0.54}
m_EdgeRadius: 0 m_EdgeRadius: 0
--- !u!114 &7700000000000000001
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1208144871472054}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: b2c3d4e5f60718293a4b5c6d7e8f90a1, type: 3}
m_Name:
m_EditorClassIdentifier:
size: {x: 1.2, y: 0.9}
offsetDistance: 0.7
activeDuration: 0.12
damage: 1
targetLayers:
serializedVersion: 2
m_Bits: 4294967295

View File

@ -0,0 +1,327 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &1208144871472054
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 4520396350849616}
- component: {fileID: 114230831923080080}
- component: {fileID: 212064061491315838}
- component: {fileID: 95391780594314510}
- component: {fileID: 50915408107096866}
- component: {fileID: 8509783501685719824}
- component: {fileID: 6082575806313374961}
- component: {fileID: 1824045667}
m_Layer: 13
m_Name: Player
m_TagString: Player
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &4520396350849616
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1208144871472054}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: -4.14, y: 3.39, z: 1}
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 &114230831923080080
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1208144871472054}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 709ee11994a2f44658e5fc9887c45081, type: 3}
m_Name:
m_EditorClassIdentifier:
minGroundNormalY: 0.9
gravityModifier: 1
velocity: {x: 0, y: 0}
jumpAudio: {fileID: 8300000, guid: 67975c08d56ceb147bad38572d805aa8, type: 3}
respawnAudio: {fileID: 8300000, guid: e18959d46b63f3c4c8cc020a433cf848, type: 3}
ouchAudio: {fileID: 8300000, guid: 6c05149e5a2fba64ba172e25a61074b7, type: 3}
maxSpeed: 3
jumpTakeOffSpeed: 7
jumpState: 0
collider2d: {fileID: 0}
audioSource: {fileID: 0}
health: {fileID: 0}
controlEnabled: 1
InputAsset: {fileID: -944628639613478452, guid: e56a71532e878ca498ffce08f1e0f519,
type: 3}
--- !u!212 &212064061491315838
SpriteRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1208144871472054}
m_Enabled: 1
m_CastShadows: 0
m_ReceiveShadows: 0
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 0
m_ReflectionProbeUsage: 0
m_RayTracingMode: 0
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: 9dfc825aed78fcd4ba02077103263b40, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 0
m_SelectedEditorRenderState: 0
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 1907945055
m_SortingLayer: 0
m_SortingOrder: 5
m_Sprite: {fileID: 21300020, guid: ba86c7b200abe499cb750833482830b3, type: 3}
m_Color: {r: 0.18039216, g: 0.8901961, b: 0.99215686, a: 1}
m_FlipX: 0
m_FlipY: 0
m_DrawMode: 0
m_Size: {x: 1.28, y: 1.26}
m_AdaptiveModeThreshold: 0.5
m_SpriteTileMode: 0
m_WasSpriteAssigned: 1
m_MaskInteraction: 0
m_SpriteSortPoint: 0
--- !u!95 &95391780594314510
Animator:
serializedVersion: 7
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1208144871472054}
m_Enabled: 1
m_Avatar: {fileID: 0}
m_Controller: {fileID: 9100000, guid: b09e60546065cf9448b36ebb7f759a91, type: 2}
m_CullingMode: 0
m_UpdateMode: 0
m_ApplyRootMotion: 0
m_LinearVelocityBlending: 0
m_StabilizeFeet: 0
m_AnimatePhysics: 0
m_WarningMessage:
m_HasTransformHierarchy: 1
m_AllowConstantClipSamplingOptimization: 1
m_KeepAnimatorStateOnDisable: 0
m_WriteDefaultValuesOnDisable: 0
--- !u!50 &50915408107096866
Rigidbody2D:
serializedVersion: 5
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1208144871472054}
m_BodyType: 1
m_Simulated: 1
m_UseFullKinematicContacts: 1
m_UseAutoMass: 0
m_Mass: 1
m_LinearDamping: 0
m_AngularDamping: 0.05
m_GravityScale: 1
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_Interpolate: 1
m_SleepingMode: 0
m_CollisionDetection: 1
m_Constraints: 4
--- !u!82 &8509783501685719824
AudioSource:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1208144871472054}
m_Enabled: 1
serializedVersion: 4
OutputAudioMixerGroup: {fileID: 0}
m_audioClip: {fileID: 0}
m_Resource: {fileID: 0}
m_PlayOnAwake: 0
m_Volume: 1
m_Pitch: 1
Loop: 0
Mute: 0
Spatialize: 0
SpatializePostEffects: 0
Priority: 128
DopplerLevel: 1
MinDistance: 1
MaxDistance: 500
Pan2D: 0
rolloffMode: 0
BypassEffects: 0
BypassListenerEffects: 0
BypassReverbZones: 0
rolloffCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
- serializedVersion: 3
time: 1
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
panLevelCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
spreadCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
reverbZoneMixCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
--- !u!114 &6082575806313374961
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1208144871472054}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: ffad43bb006db4856a9c527b89b48db9, type: 3}
m_Name:
m_EditorClassIdentifier:
maxHP: 1
--- !u!61 &1824045667
BoxCollider2D:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1208144871472054}
m_Enabled: 1
serializedVersion: 3
m_Density: 1
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_ForceSendLayers:
serializedVersion: 2
m_Bits: 4294967295
m_ForceReceiveLayers:
serializedVersion: 2
m_Bits: 4294967295
m_ContactCaptureLayers:
serializedVersion: 2
m_Bits: 4294967295
m_CallbackLayers:
serializedVersion: 2
m_Bits: 4294967295
m_IsTrigger: 0
m_UsedByEffector: 0
m_CompositeOperation: 0
m_CompositeOrder: 0
m_Offset: {x: -0.09, y: -0.14}
m_SpriteTilingProperty:
border: {x: 0, y: 0, z: 0, w: 0}
pivot: {x: 0.5, y: 0.5}
oldSize: {x: 1.28, y: 1.26}
newSize: {x: 1.28, y: 1.26}
adaptiveTilingThreshold: 0.5
drawMode: 0
adaptiveTiling: 0
m_AutoTiling: 0
m_Size: {x: 0.32, y: 0.54}
m_EdgeRadius: 0

View File

@ -4244,7 +4244,7 @@ SpriteRenderer:
m_SortingLayerID: 0 m_SortingLayerID: 0
m_SortingLayer: 0 m_SortingLayer: 0
m_SortingOrder: 0 m_SortingOrder: 0
m_Sprite: {fileID: 21300000, guid: 443583d5024ec0b46889a8bed11f2d5d, type: 3} m_Sprite: {fileID: 21300020, guid: ba86c7b200abe499cb750833482830b3, type: 3}
m_Color: {r: 1, g: 1, b: 1, a: 1} m_Color: {r: 1, g: 1, b: 1, a: 1}
m_FlipX: 0 m_FlipX: 0
m_FlipY: 0 m_FlipY: 0

View File

@ -0,0 +1,43 @@
using Platformer.Core;
using Platformer.Mechanics;
using UnityEngine;
using static Platformer.Core.Simulation;
namespace Platformer.Gameplay
{
/// <summary>
/// Fired when the player triggers an attack (mouse left click / touch attack button).
/// EerieVillage BT5-Dev 2단계 신설 — 기획 04 §5 근거리 공격 1종 파일럿.
/// OnExecute 훅으로 카드 효과·특성 효과가 결합될 확장 지점 (개발 02 §2-1).
/// </summary>
public class PlayerAttack : Simulation.Event<PlayerAttack>
{
public PlayerController player;
public Vector2 direction; // 플레이어 facing 방향 (±1, 0)
public override void Execute()
{
if (player == null) return;
// 공격 애니메이션 트리거 (Animator에 "attack" trigger 있으면 재생 — 정적 스프라이트면 무시)
if (player.animator != null)
{
var attackHash = Animator.StringToHash("attack");
// SetTrigger는 미존재 파라미터여도 예외 없음
player.animator.SetTrigger(attackHash);
}
// 공격 효과음 (존재 시)
if (player.audioSource != null && player.attackAudio != null)
{
player.audioSource.PlayOneShot(player.attackAudio);
}
// 플레이어 앞에 판정 박스 발생
if (player.attackHitbox != null)
{
player.attackHitbox.Fire(direction);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a1b2c3d4e5f60718293a4b5c6d7e8f90
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -9,6 +9,9 @@ namespace Platformer.Gameplay
/// <summary> /// <summary>
/// Fired when a Player collides with an Enemy. /// Fired when a Player collides with an Enemy.
/// BT5-Dev 2단계 개정: 기획 04 §3·§5 준수 — "위에서 밟기" 판정 폐기, i-frame 체크된 피격 로직으로 교체.
/// 적 처치는 PlayerAttack 이벤트 + AttackHitbox 공격 판정 경로로 일원화.
/// 적과 접촉 시 플레이어는 i-frame 내라면 피해 없음, 아니면 라이프 1 → 사망 체인 발동.
/// </summary> /// </summary>
/// <typeparam name="EnemyCollision"></typeparam> /// <typeparam name="EnemyCollision"></typeparam>
public class PlayerEnemyCollision : Simulation.Event<PlayerEnemyCollision> public class PlayerEnemyCollision : Simulation.Event<PlayerEnemyCollision>
@ -20,34 +23,17 @@ namespace Platformer.Gameplay
public override void Execute() public override void Execute()
{ {
var willHurtEnemy = player.Bounds.center.y >= enemy.Bounds.max.y; if (player == null || player.health == null) return;
if (willHurtEnemy) // i-frame 내에는 피해 무효 (Health.Decrement가 내부에서 체크)
{ // — 접촉 지속 동안 연속 히트되는 것을 방지
var enemyHealth = enemy.GetComponent<Health>(); if (player.health.IsInvulnerable) return;
if (enemyHealth != null)
{ // 적과 접촉 → 플레이어 피해 (Health.Decrement가 i-frame 활성화)
enemyHealth.Decrement(); player.health.Decrement();
if (!enemyHealth.IsAlive)
{ // HP 0 도달 시 HealthIsZero 이벤트가 PlayerDeath 체인을 발동 (기존 경로)
Schedule<EnemyDeath>().enemy = enemy; // 여기서 Schedule<PlayerDeath> 직접 호출은 중복이므로 제거 — Health 이벤트 체인 신뢰
player.Bounce(2);
}
else
{
player.Bounce(7);
}
}
else
{
Schedule<EnemyDeath>().enemy = enemy;
player.Bounce(2);
}
}
else
{
Schedule<PlayerDeath>();
}
} }
} }
} }

View File

@ -0,0 +1,93 @@
using System.Collections.Generic;
using Platformer.Gameplay;
using UnityEngine;
using static Platformer.Core.Simulation;
namespace Platformer.Mechanics
{
/// <summary>
/// 플레이어 근거리 공격 판정 박스.
/// PlayerAttack 이벤트에서 Fire(direction)를 호출하면 지정 활성 지속 시간 동안
/// OverlapBox 로 적을 감지하고, Health 보유 적에 Decrement 적용 → EnemyDeath 체인.
/// 기획 04 §5-1 근거리 공격 1종 — 쿨타임·대미지·판정 박스는 Phase 3-B 튠 대상.
/// </summary>
public class AttackHitbox : MonoBehaviour
{
[Header("판정 박스 크기 (플레이어 기준 로컬)")]
public Vector2 size = new Vector2(1.2f, 0.9f);
[Tooltip("플레이어 중심으로부터 공격 방향으로의 오프셋 거리")]
public float offsetDistance = 0.7f;
[Tooltip("판정 활성 지속 시간 (초). 정적 스프라이트 기반이면 짧게 유지")]
public float activeDuration = 0.12f;
[Tooltip("대미지 (Health.Decrement 호출 횟수)")]
public int damage = 1;
[Header("타격 대상 레이어 마스크")]
public LayerMask targetLayers = ~0; // 전 레이어 기본. 실전에서 Enemy 레이어로 제한 권장
float activeUntil = -1f;
Vector2 lastDirection = Vector2.right;
// 같은 스윙으로 동일 Health 중복 타격 방지
readonly HashSet<Health> alreadyHit = new HashSet<Health>();
/// <summary>
/// PlayerAttack.Execute 에서 호출. direction은 플레이어 facing (x축 ±1 or 0).
/// </summary>
public void Fire(Vector2 direction)
{
if (Mathf.Abs(direction.x) > 0.01f) lastDirection = new Vector2(Mathf.Sign(direction.x), 0);
activeUntil = Time.time + activeDuration;
alreadyHit.Clear();
}
void Update()
{
if (Time.time > activeUntil) return;
// 로컬 오프셋: 플레이어 중심 + facing * offsetDistance
var center = (Vector2)transform.position + lastDirection * offsetDistance;
// OverlapBox로 적 검출
var hits = Physics2D.OverlapBoxAll(center, size, 0f, targetLayers);
foreach (var col in hits)
{
if (col == null) continue;
// 자기 자신 collider 제외 (PlayerController 부착 GameObject)
if (col.transform == transform || col.transform.IsChildOf(transform)) continue;
var health = col.GetComponent<Health>();
if (health == null || !health.IsAlive) continue;
if (alreadyHit.Contains(health)) continue;
alreadyHit.Add(health);
for (int i = 0; i < damage; i++)
{
health.Decrement();
if (!health.IsAlive) break;
}
// Enemy 즉사 시 EnemyController 기반 EnemyDeath 체인 발동 (선택)
if (!health.IsAlive)
{
var enemy = col.GetComponent<EnemyController>();
if (enemy != null)
{
Schedule<EnemyDeath>().enemy = enemy;
}
}
}
}
// Scene view 가시화 — 활성 상태에서 박스 표시
void OnDrawGizmos()
{
Gizmos.color = (Application.isPlaying && Time.time <= activeUntil)
? new Color(1f, 0.3f, 0.3f, 0.6f)
: new Color(1f, 1f, 1f, 0.2f);
var dir = Application.isPlaying ? lastDirection : Vector2.right;
var center = (Vector2)transform.position + dir * offsetDistance;
Gizmos.DrawWireCube(center, size);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b2c3d4e5f60718293a4b5c6d7e8f90a1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -15,12 +15,24 @@ namespace Platformer.Mechanics
/// </summary> /// </summary>
public int maxHP = 1; public int maxHP = 1;
/// <summary>
/// 무적 시간 (i-frame) 지속 초 단위. 기획 04 §3-2 후보 0.4~0.8s.
/// 라이프 1 전제에서 연속 히트 인정 금지를 위한 핵심 장치 (BT5-Dev 2단계 신설).
/// </summary>
public float invulnerableDuration = 0.6f;
/// <summary>
/// 현재 무적 상태 여부 (디버그·UX 피드백용 — 깜박임 제어 등에 사용 가능).
/// </summary>
public bool IsInvulnerable => Time.time < invulnerableUntil;
/// <summary> /// <summary>
/// Indicates if the entity should be considered 'alive'. /// Indicates if the entity should be considered 'alive'.
/// </summary> /// </summary>
public bool IsAlive => currentHP > 0; public bool IsAlive => currentHP > 0;
int currentHP; int currentHP;
float invulnerableUntil = -1f;
/// <summary> /// <summary>
/// Increment the HP of the entity. /// Increment the HP of the entity.
@ -32,11 +44,24 @@ namespace Platformer.Mechanics
/// <summary> /// <summary>
/// Decrement the HP of the entity. Will trigger a HealthIsZero event when /// Decrement the HP of the entity. Will trigger a HealthIsZero event when
/// current HP reaches 0. /// current HP reaches 0. 무적 시간 (i-frame) 내에는 호출되어도 스킵된다.
/// </summary> /// </summary>
public void Decrement() public void Decrement()
{ {
// BT5-Dev 2단계: i-frame 보호 — 무적 시간 내 중복 피격 차단
if (Time.time < invulnerableUntil)
{
return;
}
currentHP = Mathf.Clamp(currentHP - 1, 0, maxHP); currentHP = Mathf.Clamp(currentHP - 1, 0, maxHP);
// 피격 성공 시 무적 시간 활성화 (다음 피격 대비)
if (invulnerableDuration > 0f)
{
invulnerableUntil = Time.time + invulnerableDuration;
}
if (currentHP == 0) if (currentHP == 0)
{ {
var ev = Schedule<HealthIsZero>(); var ev = Schedule<HealthIsZero>();
@ -46,10 +71,21 @@ namespace Platformer.Mechanics
/// <summary> /// <summary>
/// Decrement the HP of the entitiy until HP reaches 0. /// Decrement the HP of the entitiy until HP reaches 0.
/// i-frame 우회하여 즉사 처리 (낙사·승리 이탈 등 시스템 강제 사망).
/// </summary> /// </summary>
public void Die() public void Die()
{ {
while (currentHP > 0) Decrement(); invulnerableUntil = -1f; // i-frame 무효화
while (currentHP > 0)
{
currentHP = Mathf.Clamp(currentHP - 1, 0, maxHP);
if (currentHP == 0)
{
var ev = Schedule<HealthIsZero>();
ev.health = this;
break;
}
}
} }
void Awake() void Awake()

View File

@ -18,6 +18,10 @@ namespace Platformer.Mechanics
public AudioClip jumpAudio; public AudioClip jumpAudio;
public AudioClip respawnAudio; public AudioClip respawnAudio;
public AudioClip ouchAudio; public AudioClip ouchAudio;
/// <summary>
/// Attack sound effect (BT5-Dev 2단계 신설). 미지정 시 무음.
/// </summary>
public AudioClip attackAudio;
/// <summary> /// <summary>
/// Max horizontal speed of the player. /// Max horizontal speed of the player.
@ -28,6 +32,16 @@ namespace Platformer.Mechanics
/// </summary> /// </summary>
public float jumpTakeOffSpeed = 7; public float jumpTakeOffSpeed = 7;
/// <summary>
/// Cooldown between attacks in seconds (기획 04 §5-1 Phase 3-B 튠 대상).
/// </summary>
public float attackCooldown = 0.35f;
/// <summary>
/// Attack hitbox component (자동 GetComponent, 없으면 null — PlayerAttack.Execute에서 null 체크).
/// </summary>
public AttackHitbox attackHitbox;
public JumpState jumpState = JumpState.Grounded; public JumpState jumpState = JumpState.Grounded;
private bool stopJump; private bool stopJump;
/*internal new*/ public Collider2D collider2d; /*internal new*/ public Collider2D collider2d;
@ -43,6 +57,12 @@ namespace Platformer.Mechanics
private InputAction m_MoveAction; private InputAction m_MoveAction;
private InputAction m_JumpAction; private InputAction m_JumpAction;
private InputAction m_AttackAction;
// 마지막 공격 시각 — attackCooldown과 비교해 연타 제한
float nextAttackTime = 0f;
// 현재 facing 방향 (마지막 이동 입력 기반, 정지 시 이전 값 유지)
Vector2 facing = Vector2.right;
public Bounds Bounds => collider2d.bounds; public Bounds Bounds => collider2d.bounds;
@ -53,12 +73,15 @@ namespace Platformer.Mechanics
collider2d = GetComponent<Collider2D>(); collider2d = GetComponent<Collider2D>();
spriteRenderer = GetComponent<SpriteRenderer>(); spriteRenderer = GetComponent<SpriteRenderer>();
animator = GetComponent<Animator>(); animator = GetComponent<Animator>();
if (attackHitbox == null) attackHitbox = GetComponent<AttackHitbox>();
m_MoveAction = InputSystem.actions.FindAction("Player/Move"); m_MoveAction = InputSystem.actions.FindAction("Player/Move");
m_JumpAction = InputSystem.actions.FindAction("Player/Jump"); m_JumpAction = InputSystem.actions.FindAction("Player/Jump");
m_AttackAction = InputSystem.actions.FindAction("Player/Attack");
m_MoveAction.Enable(); m_MoveAction.Enable();
m_JumpAction.Enable(); m_JumpAction.Enable();
if (m_AttackAction != null) m_AttackAction.Enable();
} }
protected override void Update() protected override void Update()
@ -73,6 +96,15 @@ namespace Platformer.Mechanics
stopJump = true; stopJump = true;
Schedule<PlayerStopJump>().player = this; Schedule<PlayerStopJump>().player = this;
} }
// 공격 입력 처리 (마우스 좌클릭 / 게임패드 RT / 모바일 터치 — Phase 3-B UX)
if (m_AttackAction != null && m_AttackAction.WasPressedThisFrame() && Time.time >= nextAttackTime)
{
nextAttackTime = Time.time + attackCooldown;
var ev = Schedule<PlayerAttack>();
ev.player = this;
ev.direction = facing;
}
} }
else else
{ {
@ -129,9 +161,15 @@ namespace Platformer.Mechanics
} }
if (move.x > 0.01f) if (move.x > 0.01f)
{
spriteRenderer.flipX = false; spriteRenderer.flipX = false;
facing = Vector2.right;
}
else if (move.x < -0.01f) else if (move.x < -0.01f)
{
spriteRenderer.flipX = true; spriteRenderer.flipX = true;
facing = Vector2.left;
}
animator.SetBool("grounded", IsGrounded); animator.SetBool("grounded", IsGrounded);
animator.SetFloat("velocityX", Mathf.Abs(velocity.x) / maxSpeed); animator.SetFloat("velocityX", Mathf.Abs(velocity.x) / maxSpeed);

View File

@ -24,6 +24,15 @@
"interactions": "", "interactions": "",
"initialStateCheck": false "initialStateCheck": false
}, },
{
"name": "Attack",
"type": "Button",
"id": "c9d8e7f6-a5b4-4c3d-2e1f-0a9b8c7d6e5f",
"expectedControlType": "",
"processors": "",
"interactions": "",
"initialStateCheck": false
},
{ {
"name": "Menu", "name": "Menu",
"type": "Button", "type": "Button",
@ -200,6 +209,28 @@
"isComposite": false, "isComposite": false,
"isPartOfComposite": false "isPartOfComposite": false
}, },
{
"name": "",
"id": "d7e6f5a4-b3c2-4d1e-9f8a-7b6c5d4e3f2a",
"path": "<Mouse>/leftButton",
"interactions": "",
"processors": "",
"groups": ";Keyboard&Mouse",
"action": "Attack",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "e8f7a6b5-c4d3-4e2f-1a9b-8c7d6e5f4a3b",
"path": "<Gamepad>/rightTrigger",
"interactions": "",
"processors": "",
"groups": ";Gamepad",
"action": "Attack",
"isComposite": false,
"isPartOfComposite": false
},
{ {
"name": "", "name": "",
"id": "bf91b079-c2a3-41a7-ab9e-6e5fbc7e9f4b", "id": "bf91b079-c2a3-41a7-ab9e-6e5fbc7e9f4b",

View File

@ -0,0 +1,23 @@
{
"name": "EerieVillage.Tests.Editor",
"rootNamespace": "",
"references": [
"UnityEngine.TestRunner",
"UnityEditor.TestRunner"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": true,
"precompiledReferences": [
"nunit.framework.dll"
],
"autoReferenced": false,
"defineConstraints": [
"UNITY_INCLUDE_TESTS"
],
"versionDefines": [],
"noEngineReferences": false
}

View File

@ -0,0 +1,101 @@
using NUnit.Framework;
using UnityEngine;
using UnityEditor;
/// <summary>
/// EerieVillage BT5-Dev 2단계 — Player 근거리 공격 체계 EditMode 테스트.
/// Prefab 자산의 컴포넌트 구성이 기획 04 §5-1 (근거리 공격 1종) 을 충족하는지 검증.
/// Play 모드 실행 불요 — prefab YAML 직렬화 상태를 직접 검증하여 회귀 방지.
/// </summary>
public class PlayerAttackTests
{
const string PlayerPrefabPath = "Assets/Prefabs/Player.prefab";
const string EnemyPrefabPath = "Assets/Prefabs/Enemy.prefab";
[Test]
public void Player_Prefab_Has_AttackHitbox_Component()
{
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(PlayerPrefabPath);
Assert.IsNotNull(prefab, $"Player.prefab not found at {PlayerPrefabPath}");
var hitbox = prefab.GetComponent<Platformer.Mechanics.AttackHitbox>();
Assert.IsNotNull(hitbox,
"Player.prefab에 AttackHitbox 컴포넌트가 누락. " +
"BT5-Dev 2단계 재위임 집행분 (2026-04-23) 이 prefab YAML 에 반영되어야 함.");
}
[Test]
public void Player_Prefab_Has_Health_Component()
{
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(PlayerPrefabPath);
Assert.IsNotNull(prefab, $"Player.prefab not found at {PlayerPrefabPath}");
var health = prefab.GetComponent<Platformer.Mechanics.Health>();
Assert.IsNotNull(health, "Player.prefab 에 Health 컴포넌트 누락 (템플릿 기본).");
}
[Test]
public void Player_Prefab_Has_PlayerController_Component()
{
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(PlayerPrefabPath);
Assert.IsNotNull(prefab);
var controller = prefab.GetComponent<Platformer.Mechanics.PlayerController>();
Assert.IsNotNull(controller, "Player.prefab 에 PlayerController 컴포넌트 누락.");
}
[Test]
public void AttackHitbox_Default_Damage_Is_One()
{
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(PlayerPrefabPath);
var hitbox = prefab.GetComponent<Platformer.Mechanics.AttackHitbox>();
Assert.IsNotNull(hitbox);
Assert.AreEqual(1, hitbox.damage,
"기본 대미지 1 (기획 04 §5-1, Phase 3-B 튠 전 파일럿 값).");
}
[Test]
public void AttackHitbox_Active_Duration_Is_Positive()
{
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(PlayerPrefabPath);
var hitbox = prefab.GetComponent<Platformer.Mechanics.AttackHitbox>();
Assert.IsNotNull(hitbox);
Assert.Greater(hitbox.activeDuration, 0f,
"activeDuration 가 0 이하면 OverlapBox 판정이 즉시 종료되어 공격 무효.");
}
[Test]
public void Enemy_Prefab_Has_Health_Component()
{
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(EnemyPrefabPath);
Assert.IsNotNull(prefab, $"Enemy.prefab not found at {EnemyPrefabPath}");
var health = prefab.GetComponent<Platformer.Mechanics.Health>();
Assert.IsNotNull(health,
"Enemy.prefab 에 Health 컴포넌트 누락. " +
"BT5-Dev 2단계 재위임 집행분 (2026-04-23) 이 prefab YAML 에 반영되어야 함. " +
"Health 없으면 AttackHitbox.Update 의 Decrement 호출이 불가 → EnemyDeath 체인 미발동.");
}
[Test]
public void Enemy_Prefab_Health_MaxHP_Is_One()
{
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(EnemyPrefabPath);
var health = prefab.GetComponent<Platformer.Mechanics.Health>();
Assert.IsNotNull(health);
Assert.AreEqual(1, health.maxHP,
"일반 적 기본 maxHP 1 (코어 룰 7 정합, 첫 세팅).");
}
[Test]
public void Enemy_Prefab_Has_EnemyController_Component()
{
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(EnemyPrefabPath);
var controller = prefab.GetComponent<Platformer.Mechanics.EnemyController>();
Assert.IsNotNull(controller,
"EnemyDeath 체인에 EnemyController 필수 (AttackHitbox.Update 에서 Schedule<EnemyDeath>().enemy = enemy).");
}
}

8
Assets/_Recovery.meta Normal file
View File

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

256001
Assets/_Recovery/0.unity Normal file

File diff suppressed because it is too large Load Diff

View File

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