From 71b9521372046b876e21b48e3c12a42948e728f7 Mon Sep 17 00:00:00 2001 From: user Date: Mon, 30 Mar 2026 21:51:29 +0900 Subject: [PATCH] =?UTF-8?q?Fix=20:=20=EB=B3=B4=EC=8A=A4=20=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EB=93=9C=20=EC=8B=9C=EC=8A=A4=ED=85=9C=20=EC=97=85?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Resources/BossRaidWeb/bossraid_script.txt | 4 +- .../Resources/BossRaidWeb/bossraid_style.txt | 4 +- .../BossRaidWeb/bossraid_template.txt | 4 +- .../cfxr fire circle plain hdr ab.mat | 26 +- .../cfxr fire stepped fade hdr ab.mat | 26 +- .../Graphics/cfxr magic star hdr ab.mat | 23 +- .../cfxr stretch rectangle ray hdr ab.mat | 23 +- .../CFXR Prefabs/Fire/CFXR Fire Breath.prefab | 4 +- .../BossRaid/=== BossRaid System ===.prefab | 4 +- .../Contents/BossRaid/Audio/BossRaidAudio.cs | 12 + .../Contents/BossRaid/Core/BossAttack.cs | 236 ++++++++++++++++++ .../Contents/BossRaid/Core/BossAttack.cs.meta | 2 + .../Contents/BossRaid/Core/BossRaidManager.cs | 137 +++++++++- .../Contents/BossRaid/Core/ParticlePool.cs | 5 +- .../BossRaid/Core/PlayerController.cs | 195 +++++++++++++++ .../BossRaid/Core/PlayerController.cs.meta | 2 + .../Contents/BossRaid/Data/PlayerData.cs | 33 +++ .../Contents/BossRaid/Data/PlayerData.cs.meta | 2 + .../Contents/BossRaid/Editor/BossRaidSetup.cs | 44 +++- .../BossRaid/Resources/Audio/generate_sfx.py | 186 +++++++++++++- .../Resources/Audio/sfx_boss_appear.mp3 | 2 +- .../Resources/Audio/sfx_boss_breath.mp3 | 3 + .../Resources/Audio/sfx_boss_breath.mp3.meta | 23 ++ .../Resources/Audio/sfx_boss_charge.mp3 | 3 + .../Resources/Audio/sfx_boss_charge.mp3.meta | 23 ++ .../Resources/Audio/sfx_boss_death.mp3 | 2 +- .../Resources/Audio/sfx_boss_rage.mp3 | 2 +- .../Resources/Audio/sfx_boss_slam.mp3 | 3 + .../Resources/Audio/sfx_boss_slam.mp3.meta | 23 ++ .../Resources/Audio/sfx_damage_popup.mp3 | 2 +- .../Resources/Audio/sfx_game_over.mp3 | 3 + .../Resources/Audio/sfx_game_over.mp3.meta | 23 ++ .../Resources/Audio/sfx_hit_critical.mp3 | 2 +- .../BossRaid/Resources/Audio/sfx_hit_miss.mp3 | 2 +- .../Resources/Audio/sfx_hit_normal.mp3 | 2 +- .../Resources/Audio/sfx_phase_transition.mp3 | 2 +- .../Resources/Audio/sfx_player_death.mp3 | 3 + .../Resources/Audio/sfx_player_death.mp3.meta | 23 ++ .../Resources/Audio/sfx_player_hit.mp3 | 3 + .../Resources/Audio/sfx_player_hit.mp3.meta | 23 ++ .../BossRaid/Resources/BossData.asset | 3 + ...ossData.asset.meta => BossData.asset.meta} | 0 .../Resources/Particles/BreathFX.prefab | 3 + .../Resources/Particles/BreathFX.prefab.meta | 7 + .../Resources/Particles/PlayerHitFX.prefab | 3 + .../Particles/PlayerHitFX.prefab.meta | 7 + .../Resources/Particles/RageBurstFX.prefab | 3 + .../Particles/RageBurstFX.prefab.meta | 7 + .../Resources/Particles/SlamImpactFX.prefab | 3 + .../Particles/SlamImpactFX.prefab.meta | 7 + .../BossRaid/Resources/PlayerData.asset | 3 + .../BossRaid/Resources/PlayerData.asset.meta | 8 + .../BossRaid/Resources/TestBossData.asset | 3 - .../Contents/BossRaid/UI/ComboCounter.cs | 3 +- .../Contents/BossRaid/UI/GameOverScreen.cs | 139 +++++++++++ .../BossRaid/UI/GameOverScreen.cs.meta | 2 + .../Contents/BossRaid/UI/PlayerHPBar.cs | 166 ++++++++++++ .../Contents/BossRaid/UI/PlayerHPBar.cs.meta | 2 + .../BossRaid/Visual/PlayerHitEffect.cs | 121 +++++++++ .../BossRaid/Visual/PlayerHitEffect.cs.meta | 2 + .../BossRaid/Web/BossRaidWebServer.cs | 59 +++++ streamingle-urp-analysis.md | 3 + 62 files changed, 1658 insertions(+), 40 deletions(-) create mode 100644 Assets/Scripts/Contents/BossRaid/Core/BossAttack.cs create mode 100644 Assets/Scripts/Contents/BossRaid/Core/BossAttack.cs.meta create mode 100644 Assets/Scripts/Contents/BossRaid/Core/PlayerController.cs create mode 100644 Assets/Scripts/Contents/BossRaid/Core/PlayerController.cs.meta create mode 100644 Assets/Scripts/Contents/BossRaid/Data/PlayerData.cs create mode 100644 Assets/Scripts/Contents/BossRaid/Data/PlayerData.cs.meta create mode 100644 Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_boss_breath.mp3 create mode 100644 Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_boss_breath.mp3.meta create mode 100644 Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_boss_charge.mp3 create mode 100644 Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_boss_charge.mp3.meta create mode 100644 Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_boss_slam.mp3 create mode 100644 Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_boss_slam.mp3.meta create mode 100644 Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_game_over.mp3 create mode 100644 Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_game_over.mp3.meta create mode 100644 Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_player_death.mp3 create mode 100644 Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_player_death.mp3.meta create mode 100644 Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_player_hit.mp3 create mode 100644 Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_player_hit.mp3.meta create mode 100644 Assets/Scripts/Contents/BossRaid/Resources/BossData.asset rename Assets/Scripts/Contents/BossRaid/Resources/{TestBossData.asset.meta => BossData.asset.meta} (100%) create mode 100644 Assets/Scripts/Contents/BossRaid/Resources/Particles/BreathFX.prefab create mode 100644 Assets/Scripts/Contents/BossRaid/Resources/Particles/BreathFX.prefab.meta create mode 100644 Assets/Scripts/Contents/BossRaid/Resources/Particles/PlayerHitFX.prefab create mode 100644 Assets/Scripts/Contents/BossRaid/Resources/Particles/PlayerHitFX.prefab.meta create mode 100644 Assets/Scripts/Contents/BossRaid/Resources/Particles/RageBurstFX.prefab create mode 100644 Assets/Scripts/Contents/BossRaid/Resources/Particles/RageBurstFX.prefab.meta create mode 100644 Assets/Scripts/Contents/BossRaid/Resources/Particles/SlamImpactFX.prefab create mode 100644 Assets/Scripts/Contents/BossRaid/Resources/Particles/SlamImpactFX.prefab.meta create mode 100644 Assets/Scripts/Contents/BossRaid/Resources/PlayerData.asset create mode 100644 Assets/Scripts/Contents/BossRaid/Resources/PlayerData.asset.meta delete mode 100644 Assets/Scripts/Contents/BossRaid/Resources/TestBossData.asset create mode 100644 Assets/Scripts/Contents/BossRaid/UI/GameOverScreen.cs create mode 100644 Assets/Scripts/Contents/BossRaid/UI/GameOverScreen.cs.meta create mode 100644 Assets/Scripts/Contents/BossRaid/UI/PlayerHPBar.cs create mode 100644 Assets/Scripts/Contents/BossRaid/UI/PlayerHPBar.cs.meta create mode 100644 Assets/Scripts/Contents/BossRaid/Visual/PlayerHitEffect.cs create mode 100644 Assets/Scripts/Contents/BossRaid/Visual/PlayerHitEffect.cs.meta create mode 100644 streamingle-urp-analysis.md diff --git a/Assets/Resources/BossRaidWeb/bossraid_script.txt b/Assets/Resources/BossRaidWeb/bossraid_script.txt index 8ade3f42c..d9d3db4bc 100644 --- a/Assets/Resources/BossRaidWeb/bossraid_script.txt +++ b/Assets/Resources/BossRaidWeb/bossraid_script.txt @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4755879cebb5b6f10838d20094f0e70fe63e9bfc6a07fe9ab37199f516f0d9fb -size 7760 +oid sha256:bd0f339f24a457a56cd6555f2559a060ab940aa736edc93a7751cb4f18bbb985 +size 9228 diff --git a/Assets/Resources/BossRaidWeb/bossraid_style.txt b/Assets/Resources/BossRaidWeb/bossraid_style.txt index 4c000ee65..4881b0da9 100644 --- a/Assets/Resources/BossRaidWeb/bossraid_style.txt +++ b/Assets/Resources/BossRaidWeb/bossraid_style.txt @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7c5f89e82f35573423c4072a510414a848c8afefaa77b076dad31a21ae8b1812 -size 5476 +oid sha256:0717dfa0da1178ecba6d4ba29d19a364201257c488255b29ca3f0d54a4da751a +size 5547 diff --git a/Assets/Resources/BossRaidWeb/bossraid_template.txt b/Assets/Resources/BossRaidWeb/bossraid_template.txt index 449460f74..6518d15d6 100644 --- a/Assets/Resources/BossRaidWeb/bossraid_template.txt +++ b/Assets/Resources/BossRaidWeb/bossraid_template.txt @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cd029b143f48a12588ebf134cd17b32fcb0935658ee0e92510f81766830254c4 -size 6311 +oid sha256:2cb1f6aa9e366cf7c1b73764fb2e74b19f12b7f994040fe6176d9aefbd2ffd36 +size 8043 diff --git a/Assets/ResourcesData/Etc/Particle/JMO Assets/Cartoon FX Remaster/CFXR Assets/Graphics/cfxr fire circle plain hdr ab.mat b/Assets/ResourcesData/Etc/Particle/JMO Assets/Cartoon FX Remaster/CFXR Assets/Graphics/cfxr fire circle plain hdr ab.mat index f8c73867b..8f9e4636e 100644 --- a/Assets/ResourcesData/Etc/Particle/JMO Assets/Cartoon FX Remaster/CFXR Assets/Graphics/cfxr fire circle plain hdr ab.mat +++ b/Assets/ResourcesData/Etc/Particle/JMO Assets/Cartoon FX Remaster/CFXR Assets/Graphics/cfxr fire circle plain hdr ab.mat @@ -2,21 +2,35 @@ %TAG !u! tag:unity3d.com,2011: --- !u!21 &2100000 Material: - serializedVersion: 6 + serializedVersion: 8 m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_Name: cfxr fire circle plain hdr ab m_Shader: {fileID: 4800000, guid: e198026df304af84a85675be636192ea, type: 3} - m_ShaderKeywords: _ALPHATEST_ON _FADING_ON _CFXR_HDR_BOOST _CFXR_SINGLE_CHANNEL - _CFXR_EDGE_FADING _CFXR_UV_DISTORTION + m_Parent: {fileID: 0} + m_ModifiedSerializedProperties: 0 + m_ValidKeywords: + - _ALPHABLEND_ON + - _ALPHATEST_ON + - _CFXR_EDGE_FADING + - _CFXR_HDR_BOOST + - _CFXR_SINGLE_CHANNEL + - _CFXR_UV_DISTORTION + - _FADING_ON + m_InvalidKeywords: + - _ + - _CFXR_DITHERED_SHADOWS_OFF + - _CFXR_OVERLAYBLEND_RGBA + - _CFXR_OVERLAYTEX_OFF m_LightmapFlags: 0 m_EnableInstancingVariants: 0 m_DoubleSidedGI: 0 m_CustomRenderQueue: -1 stringTagMap: {} disabledShaderPasses: [] + m_LockedProperties: m_SavedProperties: serializedVersion: 3 m_TexEnvs: @@ -52,6 +66,7 @@ Material: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} + m_Ints: [] m_Floats: - _BacklightTransmittance: 1 - _BlendingType: 0 @@ -64,6 +79,7 @@ Material: - _DirectLightingRamp: 1 - _DissolveSmooth: 0.1 - _Distort: 0.3 + - _DoubleDissolve: 0 - _DstBlend: 10 - _EdgeFadePow: 1 - _FadeAlongU: 1 @@ -85,6 +101,7 @@ Material: - _UseAlphaClip: 1 - _UseBackLighting: 0 - _UseDissolve: 0 + - _UseDissolveOffsetUV: 0 - _UseEF: 1 - _UseEmission: 0 - _UseFB: 0 @@ -99,7 +116,10 @@ Material: - _UseUVDistortion: 1 - _ZWrite: 0 m_Colors: + - _DissolveScroll: {r: 0, g: 0, b: 0, a: 0} - _DistortScrolling: {r: 0, g: -1, b: 1, a: 1} - _OverlayTex_Scroll: {r: 0.1, g: 0.1, b: 1, a: 1} - _ShadowColor: {r: 0, g: 0, b: 0, a: 1} - _SoftParticlesFadeDistance: {r: 0, g: 1, b: 0, a: 0} + m_BuildTextureStacks: [] + m_AllowLocking: 1 diff --git a/Assets/ResourcesData/Etc/Particle/JMO Assets/Cartoon FX Remaster/CFXR Assets/Graphics/cfxr fire stepped fade hdr ab.mat b/Assets/ResourcesData/Etc/Particle/JMO Assets/Cartoon FX Remaster/CFXR Assets/Graphics/cfxr fire stepped fade hdr ab.mat index 51c573c4c..ca671874d 100644 --- a/Assets/ResourcesData/Etc/Particle/JMO Assets/Cartoon FX Remaster/CFXR Assets/Graphics/cfxr fire stepped fade hdr ab.mat +++ b/Assets/ResourcesData/Etc/Particle/JMO Assets/Cartoon FX Remaster/CFXR Assets/Graphics/cfxr fire stepped fade hdr ab.mat @@ -2,21 +2,35 @@ %TAG !u! tag:unity3d.com,2011: --- !u!21 &2100000 Material: - serializedVersion: 6 + serializedVersion: 8 m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_Name: cfxr fire stepped fade hdr ab m_Shader: {fileID: 4800000, guid: e198026df304af84a85675be636192ea, type: 3} - m_ShaderKeywords: _ALPHATEST_ON _CFXR_EDGE_FADING _CFXR_HDR_BOOST _CFXR_SINGLE_CHANNEL - _CFXR_UV_DISTORTION _FADING_ON + m_Parent: {fileID: 0} + m_ModifiedSerializedProperties: 0 + m_ValidKeywords: + - _ALPHABLEND_ON + - _ALPHATEST_ON + - _CFXR_EDGE_FADING + - _CFXR_HDR_BOOST + - _CFXR_SINGLE_CHANNEL + - _CFXR_UV_DISTORTION + - _FADING_ON + m_InvalidKeywords: + - _ + - _CFXR_DITHERED_SHADOWS_OFF + - _CFXR_OVERLAYBLEND_RGBA + - _CFXR_OVERLAYTEX_OFF m_LightmapFlags: 0 m_EnableInstancingVariants: 0 m_DoubleSidedGI: 0 m_CustomRenderQueue: -1 stringTagMap: {} disabledShaderPasses: [] + m_LockedProperties: m_SavedProperties: serializedVersion: 3 m_TexEnvs: @@ -52,6 +66,7 @@ Material: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} + m_Ints: [] m_Floats: - _BacklightTransmittance: 1 - _BlendingType: 0 @@ -64,6 +79,7 @@ Material: - _DirectLightingRamp: 1 - _DissolveSmooth: 0.1 - _Distort: 0.75 + - _DoubleDissolve: 0 - _DstBlend: 10 - _EdgeFadePow: 2 - _FadeAlongU: 1 @@ -85,6 +101,7 @@ Material: - _UseAlphaClip: 1 - _UseBackLighting: 0 - _UseDissolve: 0 + - _UseDissolveOffsetUV: 0 - _UseEF: 1 - _UseEmission: 0 - _UseFB: 0 @@ -99,7 +116,10 @@ Material: - _UseUVDistortion: 1 - _ZWrite: 0 m_Colors: + - _DissolveScroll: {r: 0, g: 0, b: 0, a: 0} - _DistortScrolling: {r: 0, g: -1.2, b: 1, a: 1} - _OverlayTex_Scroll: {r: 0.1, g: 0.1, b: 1, a: 1} - _ShadowColor: {r: 0, g: 0, b: 0, a: 1} - _SoftParticlesFadeDistance: {r: 0, g: 1, b: 0, a: 0} + m_BuildTextureStacks: [] + m_AllowLocking: 1 diff --git a/Assets/ResourcesData/Etc/Particle/JMO Assets/Cartoon FX Remaster/CFXR Assets/Graphics/cfxr magic star hdr ab.mat b/Assets/ResourcesData/Etc/Particle/JMO Assets/Cartoon FX Remaster/CFXR Assets/Graphics/cfxr magic star hdr ab.mat index d3a0a93b5..e0033880c 100644 --- a/Assets/ResourcesData/Etc/Particle/JMO Assets/Cartoon FX Remaster/CFXR Assets/Graphics/cfxr magic star hdr ab.mat +++ b/Assets/ResourcesData/Etc/Particle/JMO Assets/Cartoon FX Remaster/CFXR Assets/Graphics/cfxr magic star hdr ab.mat @@ -2,21 +2,32 @@ %TAG !u! tag:unity3d.com,2011: --- !u!21 &2100000 Material: - serializedVersion: 6 + serializedVersion: 8 m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_Name: cfxr magic star hdr ab m_Shader: {fileID: 4800000, guid: e198026df304af84a85675be636192ea, type: 3} - m_ShaderKeywords: _CFXR_DITHERED_SHADOWS_ON _CFXR_HDR_BOOST _CFXR_SINGLE_CHANNEL - _FADING_ON + m_Parent: {fileID: 0} + m_ModifiedSerializedProperties: 0 + m_ValidKeywords: + - _ALPHABLEND_ON + - _CFXR_DITHERED_SHADOWS_ON + - _CFXR_HDR_BOOST + - _CFXR_SINGLE_CHANNEL + - _FADING_ON + m_InvalidKeywords: + - _ + - _CFXR_OVERLAYBLEND_RGBA + - _CFXR_OVERLAYTEX_OFF m_LightmapFlags: 0 m_EnableInstancingVariants: 0 m_DoubleSidedGI: 0 m_CustomRenderQueue: -1 stringTagMap: {} disabledShaderPasses: [] + m_LockedProperties: m_SavedProperties: serializedVersion: 3 m_TexEnvs: @@ -52,6 +63,7 @@ Material: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} + m_Ints: [] m_Floats: - _BacklightTransmittance: 1 - _BlendingType: 0 @@ -64,6 +76,7 @@ Material: - _DirectLightingRamp: 1 - _DissolveSmooth: 0.1 - _Distort: 0.1 + - _DoubleDissolve: 0 - _DstBlend: 10 - _EdgeFadePow: 1 - _FadeAlongU: 0 @@ -84,6 +97,7 @@ Material: - _UseAlphaClip: 0 - _UseBackLighting: 0 - _UseDissolve: 0 + - _UseDissolveOffsetUV: 0 - _UseEF: 0 - _UseEmission: 0 - _UseFB: 0 @@ -99,7 +113,10 @@ Material: - _ZWrite: 0 m_Colors: - _Color: {r: 3.8792846, g: 3.8792846, b: 3.8792846, a: 0} + - _DissolveScroll: {r: 0, g: 0, b: 0, a: 0} - _DistortScrolling: {r: 0, g: 0, b: 1, a: 1} - _EmissionColor: {r: 0, g: 0, b: 0, a: 0} - _OverlayTex_Scroll: {r: 0.1, g: 0.1, b: 1, a: 1} - _ShadowColor: {r: 0, g: 0, b: 0, a: 1} + m_BuildTextureStacks: [] + m_AllowLocking: 1 diff --git a/Assets/ResourcesData/Etc/Particle/JMO Assets/Cartoon FX Remaster/CFXR Assets/Graphics/cfxr stretch rectangle ray hdr ab.mat b/Assets/ResourcesData/Etc/Particle/JMO Assets/Cartoon FX Remaster/CFXR Assets/Graphics/cfxr stretch rectangle ray hdr ab.mat index d74f1507b..4ec1eafeb 100644 --- a/Assets/ResourcesData/Etc/Particle/JMO Assets/Cartoon FX Remaster/CFXR Assets/Graphics/cfxr stretch rectangle ray hdr ab.mat +++ b/Assets/ResourcesData/Etc/Particle/JMO Assets/Cartoon FX Remaster/CFXR Assets/Graphics/cfxr stretch rectangle ray hdr ab.mat @@ -2,21 +2,32 @@ %TAG !u! tag:unity3d.com,2011: --- !u!21 &2100000 Material: - serializedVersion: 6 + serializedVersion: 8 m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_Name: cfxr stretch rectangle ray hdr ab m_Shader: {fileID: 4800000, guid: e198026df304af84a85675be636192ea, type: 3} - m_ShaderKeywords: _ALPHABLEND_ON _CFXR_DITHERED_SHADOWS_ON _CFXR_HDR_BOOST _CFXR_SINGLE_CHANNEL - _FADING_ON + m_Parent: {fileID: 0} + m_ModifiedSerializedProperties: 0 + m_ValidKeywords: + - _ALPHABLEND_ON + - _CFXR_HDR_BOOST + - _CFXR_SINGLE_CHANNEL + - _FADING_ON + m_InvalidKeywords: + - _ + - _CFXR_DITHERED_SHADOWS_OFF + - _CFXR_OVERLAYBLEND_RGBA + - _CFXR_OVERLAYTEX_OFF m_LightmapFlags: 0 m_EnableInstancingVariants: 0 m_DoubleSidedGI: 0 m_CustomRenderQueue: -1 stringTagMap: {} disabledShaderPasses: [] + m_LockedProperties: m_SavedProperties: serializedVersion: 3 m_TexEnvs: @@ -52,6 +63,7 @@ Material: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} + m_Ints: [] m_Floats: - _AmbientColor: 0.5 - _AmbientIntensity: 1 @@ -66,6 +78,7 @@ Material: - _DirectLightingRamp: 1 - _DissolveSmooth: 0.1 - _Distort: 0.1 + - _DoubleDissolve: 0 - _DstBlend: 10 - _EdgeFadePow: 1 - _FadeAlongU: 0 @@ -87,6 +100,7 @@ Material: - _UseAlphaClip: 0 - _UseBackLighting: 0 - _UseDissolve: 0 + - _UseDissolveOffsetUV: 0 - _UseEF: 0 - _UseEmission: 0 - _UseFB: 0 @@ -102,7 +116,10 @@ Material: - _ZWrite: 0 m_Colors: - _Color: {r: 2, g: 2, b: 2, a: 0} + - _DissolveScroll: {r: 0, g: 0, b: 0, a: 0} - _DistortScrolling: {r: 0, g: 0, b: 1, a: 1} - _OverlayTex_Scroll: {r: 0.1, g: 0.1, b: 1, a: 1} - _ShadowColor: {r: 0, g: 0, b: 0, a: 1} - _SoftParticlesFadeDistance: {r: 0, g: 1, b: 0, a: 0} + m_BuildTextureStacks: [] + m_AllowLocking: 1 diff --git a/Assets/ResourcesData/Etc/Particle/JMO Assets/Cartoon FX Remaster/CFXR Prefabs/Fire/CFXR Fire Breath.prefab b/Assets/ResourcesData/Etc/Particle/JMO Assets/Cartoon FX Remaster/CFXR Prefabs/Fire/CFXR Fire Breath.prefab index 97a2d0ef9..81b54a536 100644 --- a/Assets/ResourcesData/Etc/Particle/JMO Assets/Cartoon FX Remaster/CFXR Prefabs/Fire/CFXR Fire Breath.prefab +++ b/Assets/ResourcesData/Etc/Particle/JMO Assets/Cartoon FX Remaster/CFXR Prefabs/Fire/CFXR Fire Breath.prefab @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ce3064f8c7ba36692921d2770728483aa1e04e539058ffeda088f8cda1b1f4b5 -size 333798 +oid sha256:3175ca939cd374298e5d3a49170068b69744cf82b71bffe9f895d3fce0670d3d +size 346224 diff --git a/Assets/Scripts/Contents/BossRaid/=== BossRaid System ===.prefab b/Assets/Scripts/Contents/BossRaid/=== BossRaid System ===.prefab index 8778f48f3..6aa158d0b 100644 --- a/Assets/Scripts/Contents/BossRaid/=== BossRaid System ===.prefab +++ b/Assets/Scripts/Contents/BossRaid/=== BossRaid System ===.prefab @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0e3ad030c7e6ecc9b530e4d249f438c17b54eb0c716cc76fe055e4ea3ec17a59 -size 11966 +oid sha256:b124c93ccd50a1ac284369ceefb365af6136bf1e5780d6b48be911af3c900d92 +size 20643 diff --git a/Assets/Scripts/Contents/BossRaid/Audio/BossRaidAudio.cs b/Assets/Scripts/Contents/BossRaid/Audio/BossRaidAudio.cs index 9f7ce41be..5183925a3 100644 --- a/Assets/Scripts/Contents/BossRaid/Audio/BossRaidAudio.cs +++ b/Assets/Scripts/Contents/BossRaid/Audio/BossRaidAudio.cs @@ -31,6 +31,16 @@ namespace Streamingle.Contents.BossRaid public const string SFX_DAMAGE_POPUP = "sfx_damage_popup"; public const string SFX_PHASE_TRANSITION = "sfx_phase_transition"; + // 보스 공격 + public const string SFX_BOSS_BREATH = "sfx_boss_breath"; + public const string SFX_BOSS_SLAM = "sfx_boss_slam"; + public const string SFX_BOSS_CHARGE = "sfx_boss_charge"; + + // 유저 피격 + public const string SFX_PLAYER_HIT = "sfx_player_hit"; + public const string SFX_PLAYER_DEATH = "sfx_player_death"; + public const string SFX_GAME_OVER = "sfx_game_over"; + // BGM 키 public const string BGM_BATTLE_NORMAL = "bgm_battle_normal"; public const string BGM_BATTLE_RAGE = "bgm_battle_rage"; @@ -182,6 +192,8 @@ namespace Streamingle.Contents.BossRaid SFX_HIT_NORMAL, SFX_HIT_CRITICAL, SFX_HIT_MISS, SFX_BOSS_APPEAR, SFX_BOSS_RAGE, SFX_BOSS_DEATH, SFX_VICTORY, SFX_DAMAGE_POPUP, SFX_PHASE_TRANSITION, + SFX_BOSS_BREATH, SFX_BOSS_SLAM, SFX_BOSS_CHARGE, + SFX_PLAYER_HIT, SFX_PLAYER_DEATH, SFX_GAME_OVER, BGM_BATTLE_NORMAL, BGM_BATTLE_RAGE, BGM_VICTORY, }; diff --git a/Assets/Scripts/Contents/BossRaid/Core/BossAttack.cs b/Assets/Scripts/Contents/BossRaid/Core/BossAttack.cs new file mode 100644 index 000000000..ae630221e --- /dev/null +++ b/Assets/Scripts/Contents/BossRaid/Core/BossAttack.cs @@ -0,0 +1,236 @@ +using System; +using System.Collections; +using UnityEngine; + +namespace Streamingle.Contents.BossRaid +{ + /// + /// 보스 공격 연출을 관리합니다. + /// 웹/인스펙터에서 수동 트리거합니다. + /// + public class BossAttack : MonoBehaviour + { + #region Enums + + public enum AttackType + { + Breath, + Slam, + RageBurst + } + + #endregion + + #region Events + + /// 공격이 유저에게 데미지를 줄 때 (attackType, damage) + public event Action OnAttackHit; + + #endregion + + #region Fields + + [Header("브레스")] + [SerializeField] private int breathDamage = 15; + [SerializeField] private float breathChargeTime = 0.5f; + [SerializeField] private float breathDuration = 2f; + [SerializeField] private int breathTickCount = 4; + + [Header("내려찍기")] + [SerializeField] private int slamDamage = 25; + [SerializeField] private float slamChargeTime = 0.8f; + + [Header("분노 폭발")] + [SerializeField] private int rageDamage = 30; + + [Header("파티클 프리팹 (비어있으면 Resources/Particles에서 자동 로드)")] + public GameObject breathFXPrefab; + public GameObject slamImpactFXPrefab; + public GameObject rageBurstFXPrefab; + public GameObject playerHitFXPrefab; + + private BossRaidAudio _audio; + private ScreenEffect _screenEffect; + private BossVisualEffect _bossVisual; + private Transform _bossTransform; + private ParticlePool _particlePool; + private Coroutine _attackCoroutine; + private bool _isAttacking; + + #endregion + + #region Properties + + public bool IsAttacking => _isAttacking; + public int BreathDamage { get => breathDamage; set => breathDamage = value; } + public int SlamDamage { get => slamDamage; set => slamDamage = value; } + public int RageDamage { get => rageDamage; set => rageDamage = value; } + + #endregion + + #region Public Methods + + public void Initialize(Transform bossTransform, BossRaidAudio audio, ScreenEffect screenEffect, BossVisualEffect bossVisual, ParticlePool particlePool) + { + _bossTransform = bossTransform; + _audio = audio; + _screenEffect = screenEffect; + _bossVisual = bossVisual; + _particlePool = particlePool; + + // 비어있는 파티클은 Resources에서 자동 로드 + if (breathFXPrefab == null) breathFXPrefab = Resources.Load("Particles/BreathFX"); + if (slamImpactFXPrefab == null) slamImpactFXPrefab = Resources.Load("Particles/SlamImpactFX"); + if (rageBurstFXPrefab == null) rageBurstFXPrefab = Resources.Load("Particles/RageBurstFX"); + if (playerHitFXPrefab == null) playerHitFXPrefab = Resources.Load("Particles/PlayerHitFX"); + + int loaded = (breathFXPrefab != null ? 1 : 0) + (slamImpactFXPrefab != null ? 1 : 0) + + (rageBurstFXPrefab != null ? 1 : 0) + (playerHitFXPrefab != null ? 1 : 0); + Debug.Log($"[BossAttack] 파티클 {loaded}/4개 로드 완료"); + + // 파티클 웜업 + if (_particlePool != null) + { + _particlePool.Warmup(breathFXPrefab, 1); + _particlePool.Warmup(slamImpactFXPrefab, 1); + _particlePool.Warmup(rageBurstFXPrefab, 1); + _particlePool.Warmup(playerHitFXPrefab, 2); + } + } + + /// + /// 브레스 공격 실행 + /// + public void ExecuteBreath() + { + if (_isAttacking) return; + if (_attackCoroutine != null) StopCoroutine(_attackCoroutine); + _attackCoroutine = StartCoroutine(BreathCoroutine()); + } + + /// + /// 내려찍기 공격 실행 + /// + public void ExecuteSlam() + { + if (_isAttacking) return; + if (_attackCoroutine != null) StopCoroutine(_attackCoroutine); + _attackCoroutine = StartCoroutine(SlamCoroutine()); + } + + /// + /// 분노 폭발 실행 + /// + public void ExecuteRageBurst() + { + if (_isAttacking) return; + if (_attackCoroutine != null) StopCoroutine(_attackCoroutine); + _attackCoroutine = StartCoroutine(RageBurstCoroutine()); + } + + public void CancelAttack() + { + if (_attackCoroutine != null) + { + StopCoroutine(_attackCoroutine); + _attackCoroutine = null; + } + _isAttacking = false; + } + + #endregion + + #region Private Methods + + private Vector3 BossPos => _bossTransform != null ? _bossTransform.position : Vector3.zero; + + private IEnumerator BreathCoroutine() + { + _isAttacking = true; + + // 차징 + _audio?.PlaySFX(BossRaidAudio.SFX_BOSS_CHARGE); + yield return new WaitForSeconds(breathChargeTime); + + // 브레스 발사 (보스 forward 방향, 프리팹 회전 유지) + _audio?.PlaySFX(BossRaidAudio.SFX_BOSS_BREATH); + Quaternion bossRot = _bossTransform != null ? _bossTransform.rotation : Quaternion.identity; + Vector3 forward = _bossTransform != null ? _bossTransform.forward : Vector3.forward; + Vector3 spawnPos = BossPos + Vector3.up * 1.5f + forward * 1f; + _particlePool?.Spawn(breathFXPrefab, spawnPos, bossRot); + + // 틱 데미지 + float tickInterval = breathDuration / breathTickCount; + for (int i = 0; i < breathTickCount; i++) + { + OnAttackHit?.Invoke(AttackType.Breath, breathDamage / breathTickCount); + _screenEffect?.PlayShake(0.15f, 0.1f); + yield return new WaitForSeconds(tickInterval); + } + + _isAttacking = false; + _attackCoroutine = null; + } + + private IEnumerator SlamCoroutine() + { + _isAttacking = true; + + // 차징 (보스 위로 솟기) + _audio?.PlaySFX(BossRaidAudio.SFX_BOSS_CHARGE); + Vector3 originalPos = BossPos; + + if (_bossTransform != null) + { + float elapsed = 0f; + while (elapsed < slamChargeTime) + { + elapsed += Time.deltaTime; + float t = elapsed / slamChargeTime; + float yOffset = Mathf.Sin(t * Mathf.PI) * 1.5f; + _bossTransform.position = originalPos + Vector3.up * yOffset; + yield return null; + } + _bossTransform.position = originalPos; + } + + // 내려찍기 + _audio?.PlaySFX(BossRaidAudio.SFX_BOSS_SLAM); + Vector3 impactPos = BossPos; + impactPos.y = 0f; + _particlePool?.Spawn(slamImpactFXPrefab, impactPos); + _screenEffect?.PlayShake(0.6f, 0.3f); + _screenEffect?.PlayFlash(new Color(1f, 0.8f, 0.3f), 0.15f); + + OnAttackHit?.Invoke(AttackType.Slam, slamDamage); + + yield return new WaitForSeconds(0.3f); + _isAttacking = false; + _attackCoroutine = null; + } + + private IEnumerator RageBurstCoroutine() + { + _isAttacking = true; + + // 차징 (강한 흔들림) + _audio?.PlaySFX(BossRaidAudio.SFX_BOSS_RAGE); + _screenEffect?.PlayShake(0.3f, 1f); + + yield return new WaitForSeconds(1f); + + // 폭발 + _particlePool?.Spawn(rageBurstFXPrefab, BossPos); + _screenEffect?.PlayFlash(new Color(1f, 0.2f, 0.1f), 0.3f); + _screenEffect?.PlayShake(0.8f, 0.5f); + + OnAttackHit?.Invoke(AttackType.RageBurst, rageDamage); + + yield return new WaitForSeconds(0.5f); + _isAttacking = false; + _attackCoroutine = null; + } + + #endregion + } +} diff --git a/Assets/Scripts/Contents/BossRaid/Core/BossAttack.cs.meta b/Assets/Scripts/Contents/BossRaid/Core/BossAttack.cs.meta new file mode 100644 index 000000000..c21a7305d --- /dev/null +++ b/Assets/Scripts/Contents/BossRaid/Core/BossAttack.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 71e07d6d0d75ea8439d98479b164ce19 \ No newline at end of file diff --git a/Assets/Scripts/Contents/BossRaid/Core/BossRaidManager.cs b/Assets/Scripts/Contents/BossRaid/Core/BossRaidManager.cs index 063f26b2d..d1cccb4f7 100644 --- a/Assets/Scripts/Contents/BossRaid/Core/BossRaidManager.cs +++ b/Assets/Scripts/Contents/BossRaid/Core/BossRaidManager.cs @@ -36,6 +36,12 @@ namespace Streamingle.Contents.BossRaid [SerializeField] private BossData bossData; [SerializeField] private Transform bossSpawnPoint; + [Header("유저")] + [SerializeField] private PlayerController playerController; + [SerializeField] + [Tooltip("유저 HP (0이면 BossData 기본값 사용)")] + private int playerMaxHP = 100; + [Header("설정")] [SerializeField] [Tooltip("보스 등장 연출 시간 (초)")] @@ -62,6 +68,10 @@ namespace Streamingle.Contents.BossRaid private BossRaidAudio _audio; private PhaseTransition _phaseTransition; private ParticlePool _particlePool; + private PlayerHPBar _playerHPBar; + private PlayerHitEffect _playerHitEffect; + private GameOverScreen _gameOverScreen; + private BossAttack _bossAttack; private RaidState _currentState = RaidState.Idle; private GameObject _bossInstance; @@ -82,6 +92,8 @@ namespace Streamingle.Contents.BossRaid public RaidState CurrentState => _currentState; public BossController Boss => _bossController; public BossRaidSafety Safety => _safety; + public PlayerController Player => playerController; + public BossAttack Attack => _bossAttack; #endregion @@ -102,6 +114,10 @@ namespace Streamingle.Contents.BossRaid _victoryScreen = GetComponentInChildren(); _audio = GetComponentInChildren(); _phaseTransition = GetComponentInChildren(); + _playerHPBar = GetComponentInChildren(); + _playerHitEffect = GetComponentInChildren(); + _gameOverScreen = GetComponentInChildren(); + _bossAttack = GetComponentInChildren(); // 파티클 풀 초기화 _particlePool = GetComponentInChildren(); @@ -156,6 +172,14 @@ namespace Streamingle.Contents.BossRaid _hpBar?.Hide(); _comboCounter?.Hide(); _victoryScreen?.Hide(); + _playerHPBar?.Hide(); + _gameOverScreen?.Hide(); + _bossAttack?.CancelAttack(); + UnsubscribePlayerEvents(); + if (_bossAttack != null) + _bossAttack.OnAttackHit -= HandleBossAttackHit; + if (playerController != null) + playerController.RestoreColor(); if (_phaseAuraInstance != null) _phaseAuraInstance.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear); SetState(RaidState.Idle); @@ -233,6 +257,28 @@ namespace Streamingle.Contents.BossRaid yield return new WaitForSeconds(appearDuration); + // === 플레이어 초기화 === + if (playerController != null) + { + playerController.Initialize(playerMaxHP); + playerController.OnDamaged += HandlePlayerDamaged; + playerController.OnDeath += HandlePlayerDeath; + playerController.OnHPChanged += HandlePlayerHPChanged; + + if (_playerHPBar != null && playerController.HeadTransform != null) + { + _playerHPBar.Initialize(playerController.HeadTransform, "", playerController.HPBarOffset); + _playerHPBar.Show(); + } + } + + // === 보스 공격 초기화 === + if (_bossAttack != null && _bossInstance != null) + { + _bossAttack.Initialize(_bossInstance.transform, _audio, _screenEffect, _bossVisual, _particlePool); + _bossAttack.OnAttackHit += HandleBossAttackHit; + } + // === Battle === SetState(RaidState.Battle); _audio?.PlayBGM(BossRaidAudio.BGM_BATTLE_NORMAL); @@ -241,7 +287,7 @@ namespace Streamingle.Contents.BossRaid Debug.Log("[BossRaidManager] 전투 시작!"); - // Battle은 OnBossDeath 이벤트로 종료됨 + // Battle은 OnBossDeath / OnPlayerDeath 이벤트로 종료됨 } private IEnumerator DefeatSequence() @@ -490,6 +536,83 @@ namespace Streamingle.Contents.BossRaid #endregion + #region Private Methods - Player Events + + private void HandleBossAttackHit(BossAttack.AttackType type, int damage) + { + if (playerController == null || playerController.IsDead) return; + + playerController.TakeDamage(damage); + + // 유저 피격 파티클 (HitEffectPoint 우선, 없으면 머리) + if (_bossAttack?.playerHitFXPrefab != null && playerController.HitEffectPoint != null) + { + Vector3 pos = playerController.HitEffectPoint.position; + _particlePool?.Spawn(_bossAttack.playerHitFXPrefab, pos); + } + } + + private void HandlePlayerDamaged(int damage) + { + _audio?.PlaySFX(BossRaidAudio.SFX_PLAYER_HIT); + _playerHitEffect?.Play(playerController); + _screenEffect?.PlayShake(0.2f, 0.15f); + } + + private void HandlePlayerDeath() + { + _audio?.PlaySFX(BossRaidAudio.SFX_PLAYER_DEATH); + _hitDetector.IsActive = false; + _bossAttack?.CancelAttack(); + StartCoroutine(GameOverSequence()); + } + + private void HandlePlayerHPChanged(int currentHP, int maxHP) + { + float ratio = maxHP > 0 ? (float)currentHP / maxHP : 0f; + _playerHPBar?.SetHP(ratio); + } + + private void UnsubscribePlayerEvents() + { + if (playerController != null) + { + playerController.OnDamaged -= HandlePlayerDamaged; + playerController.OnDeath -= HandlePlayerDeath; + playerController.OnHPChanged -= HandlePlayerHPChanged; + } + if (_bossAttack != null) + _bossAttack.OnAttackHit -= HandleBossAttackHit; + } + + private IEnumerator GameOverSequence() + { + yield return new WaitForSeconds(0.5f); + + _audio?.StopBGM(); + _audio?.PlaySFX(BossRaidAudio.SFX_GAME_OVER); + _gameOverScreen?.Show(); + + Debug.Log("[BossRaidManager] GAME OVER!"); + } + + #endregion + + #region Public Methods - Boss Attack (웹/Stream Deck 연동) + + public void ExecuteBreath() => _bossAttack?.ExecuteBreath(); + public void ExecuteSlam() => _bossAttack?.ExecuteSlam(); + public void ExecuteRageBurst() => _bossAttack?.ExecuteRageBurst(); + + public void SetPlayerHP(float ratio) => playerController?.SetHPRatio(ratio); + public void HealPlayer() => playerController?.FullHeal(); + public void SetPlayerGodMode(bool on) + { + if (playerController != null) playerController.GodMode = on; + } + + #endregion + #region Private Methods - Utility private void SetState(RaidState newState) @@ -548,6 +671,18 @@ namespace Streamingle.Contents.BossRaid { if (bossData == null || _particlePool == null) return; + // 비어있는 파티클은 Resources에서 자동 로드 + if (bossData.hitParticlePrefab == null) + bossData.hitParticlePrefab = Resources.Load("Particles/HitSpark"); + if (bossData.criticalParticlePrefab == null) + bossData.criticalParticlePrefab = Resources.Load("Particles/CriticalHit"); + if (bossData.deathParticlePrefab == null) + bossData.deathParticlePrefab = Resources.Load("Particles/BossExplosion"); + if (bossData.victoryParticlePrefab == null) + bossData.victoryParticlePrefab = Resources.Load("Particles/VictoryConfetti"); + if (bossData.phaseAuraParticlePrefab == null) + bossData.phaseAuraParticlePrefab = Resources.Load("Particles/PhaseAura"); + // 히트 파티클은 자주 쓰이므로 넉넉하게 _particlePool.Warmup(bossData.hitParticlePrefab, 5); _particlePool.Warmup(bossData.criticalParticlePrefab, 3); diff --git a/Assets/Scripts/Contents/BossRaid/Core/ParticlePool.cs b/Assets/Scripts/Contents/BossRaid/Core/ParticlePool.cs index 140278aab..ce5563197 100644 --- a/Assets/Scripts/Contents/BossRaid/Core/ParticlePool.cs +++ b/Assets/Scripts/Contents/BossRaid/Core/ParticlePool.cs @@ -56,7 +56,7 @@ namespace Streamingle.Contents.BossRaid /// 풀에서 파티클을 가져와 위치에 배치하고 재생합니다. /// 재생 완료 후 자동으로 풀에 반환됩니다. /// - public GameObject Spawn(GameObject prefab, Vector3 position) + public GameObject Spawn(GameObject prefab, Vector3 position, Quaternion? rotation = null) { if (prefab == null) return null; @@ -69,7 +69,6 @@ namespace Streamingle.Contents.BossRaid if (pool.Count > 0) { go = pool.Dequeue(); - // 풀에서 꺼낸 것이 파괴되었으면 새로 생성 if (go == null) go = CreateInstance(prefab); } @@ -79,7 +78,7 @@ namespace Streamingle.Contents.BossRaid } go.transform.position = position; - go.transform.rotation = Quaternion.identity; + go.transform.rotation = rotation ?? Quaternion.identity; go.SetActive(true); var ps = go.GetComponent(); diff --git a/Assets/Scripts/Contents/BossRaid/Core/PlayerController.cs b/Assets/Scripts/Contents/BossRaid/Core/PlayerController.cs new file mode 100644 index 000000000..25247ebee --- /dev/null +++ b/Assets/Scripts/Contents/BossRaid/Core/PlayerController.cs @@ -0,0 +1,195 @@ +using System; +using UnityEngine; + +namespace Streamingle.Contents.BossRaid +{ + /// + /// 유저(아바타)의 HP, 피격, 무적, 사망을 관리합니다. + /// + public class PlayerController : MonoBehaviour + { + #region Events + + public event Action OnHPChanged; + public event Action OnDamaged; + public event Action OnDeath; + + #endregion + + #region Fields + + [Header("데이터")] + [SerializeField] + [Tooltip("비어있으면 Resources에서 자동 로드")] + private PlayerData playerData; + + [Header("참조")] + [SerializeField] + [Tooltip("아바타의 NiloToonPerCharacterRenderController")] + private MonoBehaviour niloToonController; + + [SerializeField] + [Tooltip("HP바를 띄울 머리 Transform")] + private Transform headTransform; + + [SerializeField] + [Tooltip("HP바 위치 오프셋 (머리 기준)")] + private Vector3 hpBarOffset = new Vector3(0f, 0.3f, 0f); + + [SerializeField] + [Tooltip("피격 이펙트 위치 Transform (비어있으면 머리 사용)")] + private Transform hitEffectPoint; + + private int _currentHP; + private bool _isDead; + private bool _isInvincible; + private float _invincibleTimer; + private bool _godMode; + + // NiloToon 색상 캐시 + private Color _originalTintColor = Color.white; + + #endregion + + #region Properties + + public int CurrentHP => _currentHP; + public int MaxHP => playerData != null ? playerData.maxHP : 100; + public float HPRatio => MaxHP > 0 ? (float)_currentHP / MaxHP : 0f; + public PlayerData Data => playerData; + public bool IsDead => _isDead; + public bool IsInvincible => _isInvincible || _godMode; + public Transform HeadTransform => headTransform; + public Vector3 HPBarOffset => hpBarOffset; + public Transform HitEffectPoint => hitEffectPoint != null ? hitEffectPoint : headTransform; + + public bool GodMode + { + get => _godMode; + set + { + _godMode = value; + Debug.Log($"[PlayerController] 무적 모드: {(_godMode ? "ON" : "OFF")}"); + } + } + + #endregion + + #region Unity Messages + + private void Update() + { + if (_isInvincible && !_godMode) + { + _invincibleTimer -= Time.deltaTime; + if (_invincibleTimer <= 0f) + _isInvincible = false; + } + } + + private float InvincibilityDuration => playerData != null ? playerData.invincibilityDuration : 1.5f; + + #endregion + + #region Public Methods + + public void Initialize(int hp = -1) + { + // PlayerData 자동 로드 + if (playerData == null) + playerData = Resources.Load("PlayerData"); + + if (hp > 0 && playerData != null) + playerData.maxHP = hp; + + _currentHP = MaxHP; + _isDead = false; + _isInvincible = false; + _godMode = false; + + // NiloToon 원본 색상 저장 + if (niloToonController != null) + { + var field = niloToonController.GetType().GetField("perCharacterTintColor", + System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); + if (field != null) + _originalTintColor = (Color)field.GetValue(niloToonController); + } + + OnHPChanged?.Invoke(_currentHP, MaxHP); + Debug.Log($"[PlayerController] 초기화 완료. HP: {_currentHP}/{MaxHP}"); + } + + public void TakeDamage(int damage) + { + if (_isDead || IsInvincible) return; + + _currentHP = Mathf.Max(0, _currentHP - damage); + OnDamaged?.Invoke(damage); + OnHPChanged?.Invoke(_currentHP, MaxHP); + + // 무적 시작 + _isInvincible = true; + _invincibleTimer = InvincibilityDuration; + + Debug.Log($"[PlayerController] 피격! -{damage} | HP: {_currentHP}/{MaxHP}"); + + if (_currentHP <= 0) + { + _isDead = true; + OnDeath?.Invoke(); + Debug.Log("[PlayerController] 사망!"); + } + } + + public void SetHPRatio(float ratio) + { + ratio = Mathf.Clamp01(ratio); + _currentHP = Mathf.RoundToInt(MaxHP * ratio); + _isDead = false; + _isInvincible = false; + OnHPChanged?.Invoke(_currentHP, MaxHP); + } + + public void FullHeal() + { + _currentHP = MaxHP; + _isDead = false; + _isInvincible = false; + OnHPChanged?.Invoke(_currentHP, MaxHP); + Debug.Log("[PlayerController] HP 전체 회복"); + } + + /// + /// NiloToon 피격 색상 플래시 + /// + public void FlashHitColor(Color color) + { + SetNiloToonTint(color); + } + + /// + /// NiloToon 원래 색상 복원 + /// + public void RestoreColor() + { + SetNiloToonTint(_originalTintColor); + } + + #endregion + + #region Private Methods + + private void SetNiloToonTint(Color color) + { + if (niloToonController == null) return; + + var field = niloToonController.GetType().GetField("perCharacterTintColor", + System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); + if (field != null) + field.SetValue(niloToonController, color); + } + + #endregion + } +} diff --git a/Assets/Scripts/Contents/BossRaid/Core/PlayerController.cs.meta b/Assets/Scripts/Contents/BossRaid/Core/PlayerController.cs.meta new file mode 100644 index 000000000..3069688a9 --- /dev/null +++ b/Assets/Scripts/Contents/BossRaid/Core/PlayerController.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 2b316dc2527714246985aaf40f4ba95a \ No newline at end of file diff --git a/Assets/Scripts/Contents/BossRaid/Data/PlayerData.cs b/Assets/Scripts/Contents/BossRaid/Data/PlayerData.cs new file mode 100644 index 000000000..aac5bcbf4 --- /dev/null +++ b/Assets/Scripts/Contents/BossRaid/Data/PlayerData.cs @@ -0,0 +1,33 @@ +using System; +using UnityEngine; + +namespace Streamingle.Contents.BossRaid +{ + [CreateAssetMenu(fileName = "PlayerData", menuName = "Streamingle/Contents/BossRaid/Player Data")] + public class PlayerData : ScriptableObject + { + [Header("스탯")] + [Min(1)] + public int maxHP = 100; + + [Header("무적")] + [Tooltip("피격 후 무적 시간 (초)")] + [Range(0f, 5f)] + public float invincibilityDuration = 1.5f; + + [Header("피격 연출")] + [Tooltip("NiloToon 피격 틴트 색상")] + public Color hitTintColor = new Color(1f, 0.3f, 0.3f); + + [Tooltip("피격 틴트 지속 시간")] + [Range(0.05f, 1f)] + public float hitTintDuration = 0.3f; + + [Tooltip("비네트 색상")] + public Color vignetteColor = new Color(1f, 0f, 0f, 0.4f); + + [Tooltip("비네트 지속 시간")] + [Range(0.1f, 1f)] + public float vignetteDuration = 0.4f; + } +} diff --git a/Assets/Scripts/Contents/BossRaid/Data/PlayerData.cs.meta b/Assets/Scripts/Contents/BossRaid/Data/PlayerData.cs.meta new file mode 100644 index 000000000..e10dc6f0f --- /dev/null +++ b/Assets/Scripts/Contents/BossRaid/Data/PlayerData.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: ff44b8d33db660441900bc40df940114 \ No newline at end of file diff --git a/Assets/Scripts/Contents/BossRaid/Editor/BossRaidSetup.cs b/Assets/Scripts/Contents/BossRaid/Editor/BossRaidSetup.cs index 03153bd3c..de73604b0 100644 --- a/Assets/Scripts/Contents/BossRaid/Editor/BossRaidSetup.cs +++ b/Assets/Scripts/Contents/BossRaid/Editor/BossRaidSetup.cs @@ -62,7 +62,24 @@ namespace Streamingle.Contents.BossRaid.Editor webObj.transform.SetParent(root.transform); webObj.AddComponent(); - // === 11. 파티클 프리팹 연결 === + // === 11. 유저 시스템 === + PlayerData playerData = FindOrCreatePlayerData(); + + var playerObj = new GameObject("Player"); + playerObj.transform.SetParent(root.transform); + playerObj.AddComponent(); + playerObj.AddComponent(); + playerObj.AddComponent(); + + Debug.Log("[BossRaid] PlayerController는 아바타에 직접 부착 후 BossRaidManager의 Player Controller에 할당하세요."); + Debug.Log("[BossRaid] PlayerController에 Head Transform(머리)과 NiloToonController를 할당하세요."); + + // === 12. 보스 공격 === + var attackObj = new GameObject("BossAttack"); + attackObj.transform.SetParent(root.transform); + attackObj.AddComponent(); + + // === 13. 파티클 프리팹 연결 === AssignParticlePrefabs(bossData); // === 완료 === @@ -107,6 +124,31 @@ namespace Streamingle.Contents.BossRaid.Editor return data; } + static PlayerData FindOrCreatePlayerData() + { + string[] guids = AssetDatabase.FindAssets("t:PlayerData"); + if (guids.Length > 0) + { + string path = AssetDatabase.GUIDToAssetPath(guids[0]); + var existing = AssetDatabase.LoadAssetAtPath(path); + if (existing != null) + { + Debug.Log($"[BossRaid] 기존 PlayerData 사용: {path}"); + return existing; + } + } + + string dataPath = "Assets/Scripts/Contents/BossRaid/Resources"; + EnsureFolder(dataPath); + + var data = ScriptableObject.CreateInstance(); + string assetPath = dataPath + "/PlayerData.asset"; + AssetDatabase.CreateAsset(data, assetPath); + AssetDatabase.SaveAssets(); + Debug.Log($"[BossRaid] PlayerData 생성: {assetPath}"); + return data; + } + static void EnsureParticlePrefabs() { string particlePath = "Assets/Scripts/Contents/BossRaid/Resources/Particles"; diff --git a/Assets/Scripts/Contents/BossRaid/Resources/Audio/generate_sfx.py b/Assets/Scripts/Contents/BossRaid/Resources/Audio/generate_sfx.py index b1fc6d037..8c834e67b 100644 --- a/Assets/Scripts/Contents/BossRaid/Resources/Audio/generate_sfx.py +++ b/Assets/Scripts/Contents/BossRaid/Resources/Audio/generate_sfx.py @@ -512,6 +512,184 @@ def gen_phase_transition(): save_wav("sfx_phase_transition.wav", result) +def gen_boss_breath(): + """보스 브레스 발사 - 에너지 차징 → 발사""" + # 차징 (고주파 수렴) + charge = pitch_sweep(1500, 400, 0.3, exponential=True) + t_ch = np.linspace(0, 1, len(charge)) + charge = charge * (0.3 + 0.7 * np.power(t_ch, 0.5)) + charge = adsr(charge, attack=0.05, decay=0.05, sustain=0.9, release=0.02) * 0.4 + + charge_noise = periodic_noise(3000, 0.3) * 0.2 + charge_noise = charge_noise * np.power(t_ch, 1.5) + + charge_part = mix_layers((charge, 1.0), (charge_noise, 0.5)) + + # 발사 (폭발적 방출) + blast = pitch_sweep(300, 60, 0.4, exponential=True) + blast = adsr(blast, attack=0.001, decay=0.25, sustain=0.1, release=0.15) * 0.8 + + blast_noise = noise(0.35) * 0.6 + blast_noise = adsr(blast_noise, attack=0.001, decay=0.2, sustain=0.1, release=0.15) + + sizzle = periodic_noise(5000, 0.3) * 0.3 + sizzle = adsr(sizzle, attack=0.001, decay=0.15, sustain=0.1, release=0.15) + + blast_part = mix_layers((blast, 1.0), (blast_noise, 0.6), (sizzle, 0.4)) + blast_part = delay_effect(blast_part, delay_time=0.06, feedback=0.3, mix_amount=0.2) + + result = concat(charge_part, blast_part) + result = quantize_8bit(result, levels=64) + save_wav("sfx_boss_breath.wav", result) + + +def gen_boss_slam(): + """보스 내려찍기 - 무거운 임팩트""" + windup = pitch_sweep(200, 600, 0.08, exponential=True) + windup = adsr(windup, attack=0.01, decay=0.04, sustain=0.3, release=0.03) * 0.3 + + impact = pitch_sweep(150, 20, 0.25, exponential=True) + impact = adsr(impact, attack=0.001, decay=0.18, sustain=0.05, release=0.07) * 0.9 + + sub = triangle_wave(35, 0.2) * 0.6 + sub = adsr(sub, attack=0.001, decay=0.15, sustain=0.0, release=0.05) + + crumble = noise(0.3) * 0.7 + crumble = adsr(crumble, attack=0.001, decay=0.15, sustain=0.15, release=0.15) + + debris1 = pitch_sweep(400, 150, 0.06, exponential=True) * 0.2 + debris1 = adsr(debris1, attack=0.001, decay=0.04, sustain=0.0, release=0.02) + debris2 = pitch_sweep(500, 200, 0.05, exponential=True) * 0.15 + debris2 = adsr(debris2, attack=0.001, decay=0.03, sustain=0.0, release=0.02) + + impact_part = mix_layers((impact, 1.0), (sub, 0.7), (crumble, 0.5)) + impact_part = delay_effect(impact_part, delay_time=0.08, feedback=0.35, mix_amount=0.25) + + result = concat(windup, impact_part, silence(0.05), debris1, silence(0.03), debris2) + result = quantize_8bit(result, levels=64) + save_wav("sfx_boss_slam.wav", result) + + +def gen_boss_charge(): + """보스 차징 예고 - 위협적인 경고음""" + beep1 = square_wave(880, 0.06) * 0.5 + beep1 = adsr(beep1, attack=0.003, decay=0.02, sustain=0.4, release=0.02) + beep2 = square_wave(880, 0.06) * 0.5 + beep2 = adsr(beep2, attack=0.003, decay=0.02, sustain=0.4, release=0.02) + + charge = pitch_sweep(100, 900, 0.4, exponential=True) + t_ch = np.linspace(0, 1, len(charge)) + trem_freq = np.linspace(4, 25, len(charge)) + trem_env = 0.5 + 0.5 * np.sin(np.cumsum(2 * np.pi * trem_freq / SAMPLE_RATE)) + charge = charge * trem_env + charge = adsr(charge, attack=0.05, decay=0.05, sustain=0.8, release=0.05) * 0.5 + + ch_noise = periodic_noise(2000, 0.4) * 0.25 + ch_noise = ch_noise * np.power(t_ch, 1.5) + + hi = pulse_wave(1200, 0.4, duty=0.125) * 0.15 + hi = hi * np.power(t_ch, 2) + hi = adsr(hi, attack=0.1, decay=0.1, sustain=0.7, release=0.05) + + charge_part = mix_layers((charge, 1.0), (ch_noise, 0.5), (hi, 0.4)) + + result = concat(beep1, silence(0.04), beep2, silence(0.06), charge_part) + result = quantize_8bit(result, levels=64) + result = delay_effect(result, delay_time=0.05, feedback=0.2, mix_amount=0.15) + save_wav("sfx_boss_charge.wav", result) + + +def gen_player_hit(): + """아바타 피격 - 플레이어가 맞는 소리""" + shock = pitch_sweep(1800, 400, 0.08, exponential=True) + shock = adsr(shock, attack=0.001, decay=0.05, sustain=0.1, release=0.03) * 0.6 + + hit = square_wave(300, 0.03) * 0.7 + hit = adsr(hit, attack=0.001, decay=0.02, sustain=0.0, release=0.01) + + n = noise(0.06) * 0.5 + n = adsr(n, attack=0.001, decay=0.04, sustain=0.0, release=0.02) + + wobble = vibrato(250, 0.12, vib_freq=20, vib_depth=0.08) + wobble = adsr(wobble, attack=0.01, decay=0.06, sustain=0.15, release=0.05) * 0.3 + + sub = triangle_wave(80, 0.06) * 0.3 + sub = adsr(sub, attack=0.001, decay=0.04, sustain=0.0, release=0.02) + + first = mix_layers((shock, 0.8), (hit, 1.0), (n, 0.6), (sub, 0.5)) + result = concat(first, wobble) + result = quantize_8bit(result, levels=64) + save_wav("sfx_player_hit.wav", result) + + +def gen_player_death(): + """아바타 사망 - 슬픈 하강음""" + def sad_note(freq, dur): + w = square_wave(freq, dur) * 0.45 + h = triangle_wave(freq, dur) * 0.2 + combined = mix_layers((w, 1.0), (h, 0.5)) + return adsr(combined, attack=0.005, decay=0.05, sustain=0.5, release=0.05) + + p = silence(0.03) + n1 = sad_note(784, 0.15) # G5 + n2 = sad_note(659, 0.15) # E5 + n3 = sad_note(523, 0.15) # C5 + n4 = sad_note(415, 0.35) # Ab4 + + melody = concat(n1, p, n2, p, n3, p, n4) + + bass = pitch_sweep(200, 60, 0.8, wave_type='triangle', exponential=True) + bass = adsr(bass, attack=0.01, decay=0.4, sustain=0.2, release=0.3) * 0.25 + + max_len = max(len(melody), len(bass)) + if len(melody) < max_len: + melody = np.pad(melody, (0, max_len - len(melody))) + if len(bass) < max_len: + bass = np.pad(bass, (0, max_len - len(bass))) + + result = mix_layers((melody, 1.0), (bass, 0.5)) + result = quantize_8bit(result, levels=64) + result = delay_effect(result, delay_time=0.1, feedback=0.4, mix_amount=0.3) + save_wav("sfx_player_death.wav", result) + + +def gen_game_over(): + """게임 오버 - 무거운 패배 징글""" + doom1 = chord([196, 233, 294], 0.25) # Gm + doom1 = adsr(doom1, attack=0.01, decay=0.08, sustain=0.6, release=0.06) * 0.5 + + doom2 = chord([175, 220, 262], 0.25) # F + doom2 = adsr(doom2, attack=0.01, decay=0.08, sustain=0.6, release=0.06) * 0.5 + + doom3 = chord([165, 196, 247], 0.25) # Em + doom3 = adsr(doom3, attack=0.01, decay=0.08, sustain=0.6, release=0.06) * 0.5 + + final = chord([131, 156, 196], 0.6) # Cm + final = adsr(final, attack=0.01, decay=0.2, sustain=0.4, release=0.35) * 0.55 + + p = silence(0.04) + melody = concat(doom1, p, doom2, p, doom3, p, final) + + bass_drone = pitch_sweep(80, 40, 1.5, wave_type='triangle', exponential=True) + bass_drone = adsr(bass_drone, attack=0.05, decay=0.5, sustain=0.3, release=0.5) * 0.3 + + dark_noise = noise(1.5) * 0.1 + dark_noise = adsr(dark_noise, attack=0.1, decay=0.5, sustain=0.15, release=0.5) + + max_len = max(len(melody), len(bass_drone), len(dark_noise)) + if len(melody) < max_len: + melody = np.pad(melody, (0, max_len - len(melody))) + if len(bass_drone) < max_len: + bass_drone = np.pad(bass_drone, (0, max_len - len(bass_drone))) + if len(dark_noise) < max_len: + dark_noise = np.pad(dark_noise, (0, max_len - len(dark_noise))) + + result = mix_layers((melody, 1.0), (bass_drone, 0.5), (dark_noise, 0.4)) + result = quantize_8bit(result, levels=64) + result = delay_effect(result, delay_time=0.12, feedback=0.4, mix_amount=0.3) + save_wav("sfx_game_over.wav", result) + + # ============================================================ if __name__ == "__main__": print("Generating 8-bit SFX v2 (enhanced quality)...") @@ -526,5 +704,11 @@ if __name__ == "__main__": gen_victory() gen_damage_popup() gen_phase_transition() + gen_boss_breath() + gen_boss_slam() + gen_boss_charge() + gen_player_hit() + gen_player_death() + gen_game_over() - print("\nDone! 9 SFX files generated (v2).") + print("\nDone! 15 SFX files generated (v2).") diff --git a/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_boss_appear.mp3 b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_boss_appear.mp3 index 98c2cf0a0..32e2a4dae 100644 --- a/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_boss_appear.mp3 +++ b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_boss_appear.mp3 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1fac6327b7d87a34ee3260be83f69549711f067e81b7b322b3b495f6891c56bb +oid sha256:e5b7acbf5fdf68cb0775eb374072276a2117dd8272892472e1e2d6761cc4fb23 size 33897 diff --git a/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_boss_breath.mp3 b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_boss_breath.mp3 new file mode 100644 index 000000000..4e08de2c4 --- /dev/null +++ b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_boss_breath.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:30925f402f709f113c32efc342d82b7065e702773de88ad65aa4a88347407de3 +size 18224 diff --git a/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_boss_breath.mp3.meta b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_boss_breath.mp3.meta new file mode 100644 index 000000000..5abc91acd --- /dev/null +++ b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_boss_breath.mp3.meta @@ -0,0 +1,23 @@ +fileFormatVersion: 2 +guid: cd4815834216deb458eb99d267bdbb05 +AudioImporter: + externalObjects: {} + serializedVersion: 8 + defaultSettings: + serializedVersion: 2 + loadType: 0 + sampleRateSetting: 0 + sampleRateOverride: 44100 + compressionFormat: 1 + quality: 1 + conversionMode: 0 + preloadAudioData: 0 + platformSettingOverrides: {} + forceToMono: 0 + normalize: 1 + loadInBackground: 0 + ambisonic: 0 + 3D: 1 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_boss_charge.mp3 b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_boss_charge.mp3 new file mode 100644 index 000000000..961003389 --- /dev/null +++ b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_boss_charge.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1f5392ede0507ab0337b435cb1c43e5efa7faaffc502dda40cdd30d5f9f1c068 +size 16343 diff --git a/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_boss_charge.mp3.meta b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_boss_charge.mp3.meta new file mode 100644 index 000000000..2792ae07a --- /dev/null +++ b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_boss_charge.mp3.meta @@ -0,0 +1,23 @@ +fileFormatVersion: 2 +guid: b8f77e6e668266546bb9d1bccf93af50 +AudioImporter: + externalObjects: {} + serializedVersion: 8 + defaultSettings: + serializedVersion: 2 + loadType: 0 + sampleRateSetting: 0 + sampleRateOverride: 44100 + compressionFormat: 1 + quality: 1 + conversionMode: 0 + preloadAudioData: 0 + platformSettingOverrides: {} + forceToMono: 0 + normalize: 1 + loadInBackground: 0 + ambisonic: 0 + 3D: 1 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_boss_death.mp3 b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_boss_death.mp3 index 5231528e2..67491a0e0 100644 --- a/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_boss_death.mp3 +++ b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_boss_death.mp3 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:72e253b8f24f9e78485b9beb4b34b3b85d590a20b325d54c76ad94024b1a20ac +oid sha256:cdeabb55c3a431315b2f84d0a1e7e7238f07c30ff0219559cb512089c0ed4136 size 30136 diff --git a/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_boss_rage.mp3 b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_boss_rage.mp3 index f9a4b8c94..80e0a2042 100644 --- a/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_boss_rage.mp3 +++ b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_boss_rage.mp3 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c13615df8a9b2a6bce9ad10816c509bd9cd0d798c1b4dfd3f5058d5c82d49967 +oid sha256:c737e5e1d6a33633a22b07a1df7ce881530b19d30293d50250deed5b83b1d224 size 16343 diff --git a/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_boss_slam.mp3 b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_boss_slam.mp3 new file mode 100644 index 000000000..8089972ec --- /dev/null +++ b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_boss_slam.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bfd1265c4a2e19254bd4b17f5ab84ee049cfb63d974b3768d87cc0a2325ba893 +size 15089 diff --git a/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_boss_slam.mp3.meta b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_boss_slam.mp3.meta new file mode 100644 index 000000000..cfb2c881f --- /dev/null +++ b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_boss_slam.mp3.meta @@ -0,0 +1,23 @@ +fileFormatVersion: 2 +guid: 955407f9973c3b74386bdb0e3db3301f +AudioImporter: + externalObjects: {} + serializedVersion: 8 + defaultSettings: + serializedVersion: 2 + loadType: 0 + sampleRateSetting: 0 + sampleRateOverride: 44100 + compressionFormat: 1 + quality: 1 + conversionMode: 0 + preloadAudioData: 0 + platformSettingOverrides: {} + forceToMono: 0 + normalize: 1 + loadInBackground: 0 + ambisonic: 0 + 3D: 1 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_damage_popup.mp3 b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_damage_popup.mp3 index 4ba5a0739..f1b658795 100644 --- a/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_damage_popup.mp3 +++ b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_damage_popup.mp3 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3d8599be57c815a8afe3609eb530187a202bd32661699f44040660ace39c3f9c +oid sha256:9d630588a40212beaa21a5204c1139b03de4b1e816ce5d3587126aa0b597f270 size 3177 diff --git a/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_game_over.mp3 b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_game_over.mp3 new file mode 100644 index 000000000..7ff5e9c5b --- /dev/null +++ b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_game_over.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7a7ab5ea5b62ac1f9e286e2e08f42e12d80055455f4cd5adb2e66650bbfd06b2 +size 37659 diff --git a/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_game_over.mp3.meta b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_game_over.mp3.meta new file mode 100644 index 000000000..4bca6c656 --- /dev/null +++ b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_game_over.mp3.meta @@ -0,0 +1,23 @@ +fileFormatVersion: 2 +guid: 27d7dcce9905f6f46b1455f4e5b820dc +AudioImporter: + externalObjects: {} + serializedVersion: 8 + defaultSettings: + serializedVersion: 2 + loadType: 0 + sampleRateSetting: 0 + sampleRateOverride: 44100 + compressionFormat: 1 + quality: 1 + conversionMode: 0 + preloadAudioData: 0 + platformSettingOverrides: {} + forceToMono: 0 + normalize: 1 + loadInBackground: 0 + ambisonic: 0 + 3D: 1 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_hit_critical.mp3 b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_hit_critical.mp3 index 7e51a6f2a..fcf61bf9e 100644 --- a/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_hit_critical.mp3 +++ b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_hit_critical.mp3 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cd2789b4b5817f578af4aec16de64db951aad66fa2f8785d9bc39dff0a1e6cf5 +oid sha256:b29580072f3436c08f830b1785b121fafe16901b4ed28fa1d71fc61ac0fa8a55 size 7566 diff --git a/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_hit_miss.mp3 b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_hit_miss.mp3 index 5fbc0dd33..1fe3848d7 100644 --- a/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_hit_miss.mp3 +++ b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_hit_miss.mp3 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ccf724d1622dce508292e9437969d083d162d372f411182f60c4edea6129efd7 +oid sha256:86d29848d22bac296f67be43ff9f0e06aedbe23d654538425bb29fe3849c5bbe size 6312 diff --git a/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_hit_normal.mp3 b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_hit_normal.mp3 index 53c340e48..08166f3c0 100644 --- a/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_hit_normal.mp3 +++ b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_hit_normal.mp3 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e5b5676aa1821b7d8f9e950c245fda0264541159d1c6aabbe67363a3ebd96368 +oid sha256:383c3614e77b2b9aa61a9046cbc0f852b19bb7414f4f8fcc726346aad076eaca size 3804 diff --git a/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_phase_transition.mp3 b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_phase_transition.mp3 index 8367581f1..fb83f5bba 100644 --- a/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_phase_transition.mp3 +++ b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_phase_transition.mp3 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e5d58947ff9bff707c89552a3bb64fcaa6ca2a7cb90962c89d505aec132084e0 +oid sha256:9401fd9ecdd8e18ba407e580f60629b8dadf6f6253c7248c908c72fea4dfeae6 size 25747 diff --git a/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_player_death.mp3 b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_player_death.mp3 new file mode 100644 index 000000000..071252f00 --- /dev/null +++ b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_player_death.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9348fd5f569c1d431bc0391e5cb6ad1fbb122de096b1f51e29bb41c30f5e135a +size 23239 diff --git a/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_player_death.mp3.meta b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_player_death.mp3.meta new file mode 100644 index 000000000..6ee2e8212 --- /dev/null +++ b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_player_death.mp3.meta @@ -0,0 +1,23 @@ +fileFormatVersion: 2 +guid: 914d0a76cfde63d4da4a8671fb6c8b21 +AudioImporter: + externalObjects: {} + serializedVersion: 8 + defaultSettings: + serializedVersion: 2 + loadType: 0 + sampleRateSetting: 0 + sampleRateOverride: 44100 + compressionFormat: 1 + quality: 1 + conversionMode: 0 + preloadAudioData: 0 + platformSettingOverrides: {} + forceToMono: 0 + normalize: 1 + loadInBackground: 0 + ambisonic: 0 + 3D: 1 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_player_hit.mp3 b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_player_hit.mp3 new file mode 100644 index 000000000..0ae4e6913 --- /dev/null +++ b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_player_hit.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e52c14112c25ca0eb07395fcb9427a4f2e6f15933cdf0deeac1ce93eddc5cc3d +size 6312 diff --git a/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_player_hit.mp3.meta b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_player_hit.mp3.meta new file mode 100644 index 000000000..6fdb8e3a7 --- /dev/null +++ b/Assets/Scripts/Contents/BossRaid/Resources/Audio/sfx_player_hit.mp3.meta @@ -0,0 +1,23 @@ +fileFormatVersion: 2 +guid: 29176d805e336a34991f09a62a0e77d1 +AudioImporter: + externalObjects: {} + serializedVersion: 8 + defaultSettings: + serializedVersion: 2 + loadType: 0 + sampleRateSetting: 0 + sampleRateOverride: 44100 + compressionFormat: 1 + quality: 1 + conversionMode: 0 + preloadAudioData: 0 + platformSettingOverrides: {} + forceToMono: 0 + normalize: 1 + loadInBackground: 0 + ambisonic: 0 + 3D: 1 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Contents/BossRaid/Resources/BossData.asset b/Assets/Scripts/Contents/BossRaid/Resources/BossData.asset new file mode 100644 index 000000000..16efa3b4b --- /dev/null +++ b/Assets/Scripts/Contents/BossRaid/Resources/BossData.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c40bbd1ffc6af8a97cf5652488febd33e0e55135433f756b8c9b4e0d911bffa8 +size 1647 diff --git a/Assets/Scripts/Contents/BossRaid/Resources/TestBossData.asset.meta b/Assets/Scripts/Contents/BossRaid/Resources/BossData.asset.meta similarity index 100% rename from Assets/Scripts/Contents/BossRaid/Resources/TestBossData.asset.meta rename to Assets/Scripts/Contents/BossRaid/Resources/BossData.asset.meta diff --git a/Assets/Scripts/Contents/BossRaid/Resources/Particles/BreathFX.prefab b/Assets/Scripts/Contents/BossRaid/Resources/Particles/BreathFX.prefab new file mode 100644 index 000000000..70ed5247c --- /dev/null +++ b/Assets/Scripts/Contents/BossRaid/Resources/Particles/BreathFX.prefab @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d6f200faddc4649b9dc8a56aca4c1ecaf2cdb71454e45ca3a62fa4a5d51f197c +size 346257 diff --git a/Assets/Scripts/Contents/BossRaid/Resources/Particles/BreathFX.prefab.meta b/Assets/Scripts/Contents/BossRaid/Resources/Particles/BreathFX.prefab.meta new file mode 100644 index 000000000..6db1157ee --- /dev/null +++ b/Assets/Scripts/Contents/BossRaid/Resources/Particles/BreathFX.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: dab8858c439e1ce44b241189a90bfdae +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Contents/BossRaid/Resources/Particles/PlayerHitFX.prefab b/Assets/Scripts/Contents/BossRaid/Resources/Particles/PlayerHitFX.prefab new file mode 100644 index 000000000..8f47e522f --- /dev/null +++ b/Assets/Scripts/Contents/BossRaid/Resources/Particles/PlayerHitFX.prefab @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e510d2277f04f19410288183d07a26acff1dee10d41fa546f991da8a0a898c74 +size 351267 diff --git a/Assets/Scripts/Contents/BossRaid/Resources/Particles/PlayerHitFX.prefab.meta b/Assets/Scripts/Contents/BossRaid/Resources/Particles/PlayerHitFX.prefab.meta new file mode 100644 index 000000000..168e31697 --- /dev/null +++ b/Assets/Scripts/Contents/BossRaid/Resources/Particles/PlayerHitFX.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: a00fb5f394dc72145bdb0da9fae2e036 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Contents/BossRaid/Resources/Particles/RageBurstFX.prefab b/Assets/Scripts/Contents/BossRaid/Resources/Particles/RageBurstFX.prefab new file mode 100644 index 000000000..8049e4507 --- /dev/null +++ b/Assets/Scripts/Contents/BossRaid/Resources/Particles/RageBurstFX.prefab @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b0797b04508be454c59767f4468336d466fe354efc21f1bb157582fc156310c9 +size 703823 diff --git a/Assets/Scripts/Contents/BossRaid/Resources/Particles/RageBurstFX.prefab.meta b/Assets/Scripts/Contents/BossRaid/Resources/Particles/RageBurstFX.prefab.meta new file mode 100644 index 000000000..bc5f18194 --- /dev/null +++ b/Assets/Scripts/Contents/BossRaid/Resources/Particles/RageBurstFX.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 3ce6e1aea4cdaf244b71dbaf1750aad2 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Contents/BossRaid/Resources/Particles/SlamImpactFX.prefab b/Assets/Scripts/Contents/BossRaid/Resources/Particles/SlamImpactFX.prefab new file mode 100644 index 000000000..bc2c59bba --- /dev/null +++ b/Assets/Scripts/Contents/BossRaid/Resources/Particles/SlamImpactFX.prefab @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a028f223da70845886e2f2c3618190cb32de1ea5957f367cc556e8c8da0a61d1 +size 479018 diff --git a/Assets/Scripts/Contents/BossRaid/Resources/Particles/SlamImpactFX.prefab.meta b/Assets/Scripts/Contents/BossRaid/Resources/Particles/SlamImpactFX.prefab.meta new file mode 100644 index 000000000..0909a4811 --- /dev/null +++ b/Assets/Scripts/Contents/BossRaid/Resources/Particles/SlamImpactFX.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 95bbbe340f352a044bb8682c9a7ac460 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Contents/BossRaid/Resources/PlayerData.asset b/Assets/Scripts/Contents/BossRaid/Resources/PlayerData.asset new file mode 100644 index 000000000..6ff884f3d --- /dev/null +++ b/Assets/Scripts/Contents/BossRaid/Resources/PlayerData.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:587eafa923a4df7a3ecc361d67226c24e636ba69302e5319e13d79ad6b4f746c +size 634 diff --git a/Assets/Scripts/Contents/BossRaid/Resources/PlayerData.asset.meta b/Assets/Scripts/Contents/BossRaid/Resources/PlayerData.asset.meta new file mode 100644 index 000000000..0756f0525 --- /dev/null +++ b/Assets/Scripts/Contents/BossRaid/Resources/PlayerData.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3c93755ab47a5cf4ba387129fc315eb4 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Contents/BossRaid/Resources/TestBossData.asset b/Assets/Scripts/Contents/BossRaid/Resources/TestBossData.asset deleted file mode 100644 index 5e4b1a3cb..000000000 --- a/Assets/Scripts/Contents/BossRaid/Resources/TestBossData.asset +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7263568757b09fd9a2dc0799d1a1ea70fb0a2fb3b7931a17eb4af05226be0fa3 -size 1651 diff --git a/Assets/Scripts/Contents/BossRaid/UI/ComboCounter.cs b/Assets/Scripts/Contents/BossRaid/UI/ComboCounter.cs index ffa81f868..bf83c970f 100644 --- a/Assets/Scripts/Contents/BossRaid/UI/ComboCounter.cs +++ b/Assets/Scripts/Contents/BossRaid/UI/ComboCounter.cs @@ -186,7 +186,7 @@ namespace Streamingle.Contents.BossRaid _rootRect.anchorMin = new Vector2(1f, 0.5f); _rootRect.anchorMax = new Vector2(1f, 0.5f); _rootRect.pivot = new Vector2(1f, 0.5f); - _rootRect.sizeDelta = new Vector2(160f, 110f); + _rootRect.sizeDelta = new Vector2(300f, 200f); _rootRect.anchoredPosition = new Vector2(OffScreenX, 0f); // 콤보 숫자 @@ -204,6 +204,7 @@ namespace Streamingle.Contents.BossRaid _comboText.alignment = TextAnchor.MiddleCenter; _comboText.fontStyle = FontStyle.Bold; _comboText.horizontalOverflow = HorizontalWrapMode.Overflow; + _comboText.verticalOverflow = VerticalWrapMode.Overflow; _comboText.raycastTarget = false; if (_font != null) _comboText.font = _font; var outline = comboObj.AddComponent(); diff --git a/Assets/Scripts/Contents/BossRaid/UI/GameOverScreen.cs b/Assets/Scripts/Contents/BossRaid/UI/GameOverScreen.cs new file mode 100644 index 000000000..588cb49fa --- /dev/null +++ b/Assets/Scripts/Contents/BossRaid/UI/GameOverScreen.cs @@ -0,0 +1,139 @@ +using System.Collections; +using UnityEngine; +using UnityEngine.UI; + +namespace Streamingle.Contents.BossRaid +{ + /// + /// GAME OVER 화면. 유저 HP 0 시 표시됩니다. + /// + public class GameOverScreen : MonoBehaviour + { + #region Fields + + [Header("비주얼")] + [SerializeField] private Color titleColor = new Color(0.9f, 0.15f, 0.1f); + [SerializeField] private Color bgColor = new Color(0f, 0f, 0f, 0.6f); + + private Canvas _canvas; + private CanvasGroup _canvasGroup; + private Text _titleText; + private Coroutine _showCoroutine; + private Font _font; + + #endregion + + #region Unity Messages + + private void Awake() + { + _font = BossRaidFontLoader.Load(); + CreateUI(); + _canvasGroup.alpha = 0f; + } + + #endregion + + #region Public Methods + + public void Show() + { + if (_showCoroutine != null) StopCoroutine(_showCoroutine); + _showCoroutine = StartCoroutine(ShowCoroutine()); + } + + public void Hide() + { + if (_showCoroutine != null) + { + StopCoroutine(_showCoroutine); + _showCoroutine = null; + } + _canvasGroup.alpha = 0f; + } + + #endregion + + #region Private Methods + + private IEnumerator ShowCoroutine() + { + _titleText.text = ""; + _canvasGroup.alpha = 0f; + + // 페이드 인 + float elapsed = 0f; + while (elapsed < 0.5f) + { + elapsed += Time.deltaTime; + _canvasGroup.alpha = elapsed / 0.5f; + yield return null; + } + _canvasGroup.alpha = 1f; + + // 텍스트 등장 (펀치 스케일) + _titleText.text = "GAME OVER"; + var rect = _titleText.rectTransform; + rect.localScale = Vector3.one * 2f; + + elapsed = 0f; + while (elapsed < 0.4f) + { + elapsed += Time.deltaTime; + float t = elapsed / 0.4f; + float ease = 1f + 2.7f * Mathf.Pow(t - 1f, 3f) + 1.7f * Mathf.Pow(t - 1f, 2f); + rect.localScale = Vector3.one * Mathf.LerpUnclamped(2f, 1f, ease); + yield return null; + } + rect.localScale = Vector3.one; + } + + private void CreateUI() + { + var canvasObj = new GameObject("BossRaid_GameOver"); + canvasObj.transform.SetParent(transform); + _canvas = canvasObj.AddComponent(); + _canvas.renderMode = RenderMode.ScreenSpaceOverlay; + _canvas.sortingOrder = 201; + _canvasGroup = canvasObj.AddComponent(); + _canvasGroup.interactable = false; + _canvasGroup.blocksRaycasts = false; + var scaler = canvasObj.AddComponent(); + scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize; + scaler.referenceResolution = new Vector2(1920, 1080); + + // 배경 + var bgObj = new GameObject("BG"); + bgObj.transform.SetParent(canvasObj.transform, false); + var bgRect = bgObj.AddComponent(); + bgRect.anchorMin = Vector2.zero; + bgRect.anchorMax = Vector2.one; + bgRect.offsetMin = Vector2.zero; + bgRect.offsetMax = Vector2.zero; + var bgImg = bgObj.AddComponent(); + bgImg.color = bgColor; + bgImg.raycastTarget = false; + + // 타이틀 + var titleObj = new GameObject("Title"); + titleObj.transform.SetParent(canvasObj.transform, false); + var titleRect = titleObj.AddComponent(); + titleRect.anchorMin = new Vector2(0.1f, 0.3f); + titleRect.anchorMax = new Vector2(0.9f, 0.7f); + titleRect.offsetMin = Vector2.zero; + titleRect.offsetMax = Vector2.zero; + _titleText = titleObj.AddComponent(); + _titleText.fontSize = 90; + _titleText.color = titleColor; + _titleText.alignment = TextAnchor.MiddleCenter; + _titleText.fontStyle = FontStyle.Bold; + _titleText.raycastTarget = false; + if (_font != null) _titleText.font = _font; + var outline = titleObj.AddComponent(); + outline.effectColor = Color.black; + outline.effectDistance = new Vector2(3f, -3f); + } + + #endregion + } +} diff --git a/Assets/Scripts/Contents/BossRaid/UI/GameOverScreen.cs.meta b/Assets/Scripts/Contents/BossRaid/UI/GameOverScreen.cs.meta new file mode 100644 index 000000000..0417ae5f4 --- /dev/null +++ b/Assets/Scripts/Contents/BossRaid/UI/GameOverScreen.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 77c10ab592aba794bb52d7cf2c6047f7 \ No newline at end of file diff --git a/Assets/Scripts/Contents/BossRaid/UI/PlayerHPBar.cs b/Assets/Scripts/Contents/BossRaid/UI/PlayerHPBar.cs new file mode 100644 index 000000000..f60e12a35 --- /dev/null +++ b/Assets/Scripts/Contents/BossRaid/UI/PlayerHPBar.cs @@ -0,0 +1,166 @@ +using UnityEngine; +using UnityEngine.UI; + +namespace Streamingle.Contents.BossRaid +{ + /// + /// 유저 HP바. 머리 위 월드스페이스 캔버스로 표시됩니다. + /// + public class PlayerHPBar : MonoBehaviour + { + #region Fields + + [Header("설정")] + [SerializeField] private Vector3 offset = new Vector3(0f, 0.3f, 0f); + [SerializeField] private float barWidth = 1.2f; + [SerializeField] private float barHeight = 0.12f; + [SerializeField] private float smoothSpeed = 8f; + + [Header("색상")] + [SerializeField] private Color hpColor = new Color(0.2f, 0.85f, 0.3f); + [SerializeField] private Color hpColorLow = new Color(0.95f, 0.2f, 0.15f); + [SerializeField] private Color bgColor = new Color(0.1f, 0.1f, 0.1f, 0.85f); + [SerializeField] private Color frameColor = new Color(0.3f, 0.3f, 0.35f); + + private Canvas _canvas; + private Image _hpFill; + private RectTransform _fillRect; + private Transform _followTarget; + private float _targetRatio = 1f; + private float _displayRatio = 1f; + private bool _isVisible; + private Font _font; + private Text _nameText; + + #endregion + + #region Unity Messages + + private void LateUpdate() + { + if (!_isVisible || _followTarget == null) return; + + // 머리 위 따라다님 + transform.position = _followTarget.position + offset; + + // 카메라를 향하도록 + if (Camera.main != null) + _canvas.transform.rotation = Camera.main.transform.rotation; + + // 부드러운 HP 감소 + _displayRatio = Mathf.Lerp(_displayRatio, _targetRatio, Time.deltaTime * smoothSpeed); + _fillRect.anchorMax = new Vector2(Mathf.Clamp01(_displayRatio), 1f); + _hpFill.color = _displayRatio > 0.3f ? hpColor : hpColorLow; + } + + #endregion + + #region Public Methods + + public void Initialize(Transform headTarget, string playerName = "", Vector3? customOffset = null) + { + _font = BossRaidFontLoader.Load(); + _followTarget = headTarget; + if (customOffset.HasValue) + offset = customOffset.Value; + CreateUI(); + _isVisible = true; + + if (_nameText != null && !string.IsNullOrEmpty(playerName)) + _nameText.text = playerName; + } + + public void SetHP(float ratio) + { + _targetRatio = Mathf.Clamp01(ratio); + } + + public void Show() + { + _isVisible = true; + if (_canvas != null) _canvas.gameObject.SetActive(true); + } + + public void Hide() + { + _isVisible = false; + if (_canvas != null) _canvas.gameObject.SetActive(false); + } + + #endregion + + #region Private Methods + + private void CreateUI() + { + var canvasObj = new GameObject("PlayerHPBar_Canvas"); + canvasObj.transform.SetParent(transform); + canvasObj.transform.localPosition = Vector3.zero; + _canvas = canvasObj.AddComponent(); + _canvas.renderMode = RenderMode.WorldSpace; + _canvas.sortingOrder = 90; + + var rt = canvasObj.GetComponent(); + rt.sizeDelta = new Vector2(barWidth, barHeight + 0.15f); + rt.localScale = Vector3.one; + + // 프레임 + var frameObj = new GameObject("Frame"); + frameObj.transform.SetParent(canvasObj.transform, false); + var frameRect = frameObj.AddComponent(); + frameRect.anchorMin = new Vector2(0f, 0f); + frameRect.anchorMax = new Vector2(1f, 0.7f); + frameRect.offsetMin = Vector2.zero; + frameRect.offsetMax = Vector2.zero; + var frameImg = frameObj.AddComponent(); + frameImg.color = frameColor; + frameImg.raycastTarget = false; + + // 배경 + var bgObj = new GameObject("BG"); + bgObj.transform.SetParent(frameObj.transform, false); + var bgRect = bgObj.AddComponent(); + bgRect.anchorMin = Vector2.zero; + bgRect.anchorMax = Vector2.one; + bgRect.offsetMin = new Vector2(0.01f, 0.01f); + bgRect.offsetMax = new Vector2(-0.01f, -0.01f); + var bgImg = bgObj.AddComponent(); + bgImg.color = bgColor; + bgImg.raycastTarget = false; + + // HP Fill + var fillObj = new GameObject("Fill"); + fillObj.transform.SetParent(bgObj.transform, false); + _fillRect = fillObj.AddComponent(); + _fillRect.anchorMin = Vector2.zero; + _fillRect.anchorMax = Vector2.one; + _fillRect.offsetMin = Vector2.zero; + _fillRect.offsetMax = Vector2.zero; + _hpFill = fillObj.AddComponent(); + _hpFill.color = hpColor; + _hpFill.raycastTarget = false; + + // 이름 텍스트 (바 위) + var nameObj = new GameObject("Name"); + nameObj.transform.SetParent(canvasObj.transform, false); + var nameRect = nameObj.AddComponent(); + nameRect.anchorMin = new Vector2(0f, 0.7f); + nameRect.anchorMax = Vector2.one; + nameRect.offsetMin = Vector2.zero; + nameRect.offsetMax = Vector2.zero; + _nameText = nameObj.AddComponent(); + _nameText.text = ""; + _nameText.fontSize = 3; + _nameText.color = Color.white; + _nameText.alignment = TextAnchor.MiddleCenter; + _nameText.raycastTarget = false; + if (_font != null) _nameText.font = _font; + + var outline = nameObj.AddComponent(); + outline.effectColor = Color.black; + outline.effectDistance = new Vector2(0.3f, -0.3f); + } + + #endregion + } +} diff --git a/Assets/Scripts/Contents/BossRaid/UI/PlayerHPBar.cs.meta b/Assets/Scripts/Contents/BossRaid/UI/PlayerHPBar.cs.meta new file mode 100644 index 000000000..902a827b0 --- /dev/null +++ b/Assets/Scripts/Contents/BossRaid/UI/PlayerHPBar.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 6b9275b0b06b5094bb7bedc8038afc2a \ No newline at end of file diff --git a/Assets/Scripts/Contents/BossRaid/Visual/PlayerHitEffect.cs b/Assets/Scripts/Contents/BossRaid/Visual/PlayerHitEffect.cs new file mode 100644 index 000000000..a6c16aa00 --- /dev/null +++ b/Assets/Scripts/Contents/BossRaid/Visual/PlayerHitEffect.cs @@ -0,0 +1,121 @@ +using System.Collections; +using UnityEngine; +using UnityEngine.UI; + +namespace Streamingle.Contents.BossRaid +{ + /// + /// 유저 피격 시 빨간 비네트 오버레이 + NiloToon 색상 플래시. + /// + public class PlayerHitEffect : MonoBehaviour + { + #region Fields + + // PlayerData에서 값을 읽되, 없으면 기본값 사용 + private Color vignetteColor = new Color(1f, 0f, 0f, 0.4f); + private float vignetteDuration = 0.4f; + private Color hitTintColor = new Color(1f, 0.3f, 0.3f); + private float tintDuration = 0.3f; + + private Canvas _canvas; + private Image _vignetteImage; + private Coroutine _vignetteCoroutine; + private Coroutine _tintCoroutine; + private PlayerController _player; + + #endregion + + #region Unity Messages + + private void Awake() + { + CreateVignetteUI(); + } + + #endregion + + #region Public Methods + + public void Play(PlayerController player) + { + _player = player; + + // PlayerData에서 값 읽기 + if (player?.Data != null) + { + vignetteColor = player.Data.vignetteColor; + vignetteDuration = player.Data.vignetteDuration; + hitTintColor = player.Data.hitTintColor; + tintDuration = player.Data.hitTintDuration; + } + + if (_vignetteCoroutine != null) StopCoroutine(_vignetteCoroutine); + _vignetteCoroutine = StartCoroutine(VignetteCoroutine()); + + if (_tintCoroutine != null) StopCoroutine(_tintCoroutine); + _tintCoroutine = StartCoroutine(TintCoroutine()); + } + + #endregion + + #region Private Methods + + private IEnumerator VignetteCoroutine() + { + _vignetteImage.color = vignetteColor; + + float elapsed = 0f; + while (elapsed < vignetteDuration) + { + elapsed += Time.deltaTime; + float t = elapsed / vignetteDuration; + var c = vignetteColor; + c.a = vignetteColor.a * (1f - t); + _vignetteImage.color = c; + yield return null; + } + + _vignetteImage.color = Color.clear; + _vignetteCoroutine = null; + } + + private IEnumerator TintCoroutine() + { + if (_player == null) yield break; + + _player.FlashHitColor(hitTintColor); + + yield return new WaitForSeconds(tintDuration * 0.3f); + + // 부드럽게 원래 색상으로 + float elapsed = 0f; + float fadeDur = tintDuration * 0.7f; + while (elapsed < fadeDur) + { + elapsed += Time.deltaTime; + float t = elapsed / fadeDur; + Color c = Color.Lerp(hitTintColor, Color.white, t); + _player.FlashHitColor(c); + yield return null; + } + + _player.RestoreColor(); + _tintCoroutine = null; + } + + private void CreateVignetteUI() + { + var canvasObj = new GameObject("BossRaid_PlayerVignette"); + canvasObj.transform.SetParent(transform); + _canvas = canvasObj.AddComponent(); + _canvas.renderMode = RenderMode.ScreenSpaceOverlay; + _canvas.sortingOrder = 997; + + _vignetteImage = canvasObj.AddComponent(); + _vignetteImage.color = Color.clear; + _vignetteImage.raycastTarget = false; + } + + #endregion + } +} diff --git a/Assets/Scripts/Contents/BossRaid/Visual/PlayerHitEffect.cs.meta b/Assets/Scripts/Contents/BossRaid/Visual/PlayerHitEffect.cs.meta new file mode 100644 index 000000000..d6ee5c3b0 --- /dev/null +++ b/Assets/Scripts/Contents/BossRaid/Visual/PlayerHitEffect.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: d0dc1f4dbc9ce184586f621b3705c8bd \ No newline at end of file diff --git a/Assets/Scripts/Contents/BossRaid/Web/BossRaidWebServer.cs b/Assets/Scripts/Contents/BossRaid/Web/BossRaidWebServer.cs index 8299664a0..4e4c6e85a 100644 --- a/Assets/Scripts/Contents/BossRaid/Web/BossRaidWebServer.cs +++ b/Assets/Scripts/Contents/BossRaid/Web/BossRaidWebServer.cs @@ -32,6 +32,7 @@ namespace Streamingle.Contents.BossRaid private BossController _bossController; private BossRaidSafety _safety; private BossRaidAudio _audio; + private PlayerController _playerController; private readonly ConcurrentQueue _mainThreadActions = new ConcurrentQueue(); @@ -288,6 +289,7 @@ namespace Streamingle.Contents.BossRaid CancelInvoke(nameof(CheckBossEvents)); UnsubscribeBossEvents(); + UnsubscribePlayerEvents(); } private void CheckBossEvents() @@ -303,6 +305,17 @@ namespace Streamingle.Contents.BossRaid _bossController.OnPhaseChanged += OnPhaseChanged; _bossController.OnDeath += OnBossDeath; } + + // 플레이어 이벤트 + var newPlayer = _raidManager?.Player; + if (newPlayer != null && newPlayer != _playerController) + { + UnsubscribePlayerEvents(); + _playerController = newPlayer; + _playerController.OnHPChanged += OnPlayerHPChanged; + _playerController.OnDamaged += OnPlayerDamaged; + _playerController.OnDeath += OnPlayerDeath; + } } private void UnsubscribeBossEvents() @@ -317,10 +330,24 @@ namespace Streamingle.Contents.BossRaid } } + private void UnsubscribePlayerEvents() + { + if (_playerController != null) + { + _playerController.OnHPChanged -= OnPlayerHPChanged; + _playerController.OnDamaged -= OnPlayerDamaged; + _playerController.OnDeath -= OnPlayerDeath; + _playerController = null; + } + } + private void OnRaidStateChanged(BossRaidManager.RaidState state) => BroadcastState(); private void OnHPChanged(int current, int max) => BroadcastState(); private void OnDamaged(int dmg, bool crit, int hp, int max) => BroadcastState(); private void OnPhaseChanged(int idx, BossData.PhaseData phase) => BroadcastState(); + private void OnPlayerHPChanged(int current, int max) => BroadcastState(); + private void OnPlayerDamaged(int dmg) => BroadcastState(); + private void OnPlayerDeath() => BroadcastState(); private void OnBossDeath() => BroadcastState(); #endregion @@ -345,6 +372,13 @@ namespace Streamingle.Contents.BossRaid hpLockRatio = _safety?.HPLockRatio ?? 0f, bgmVolume = _audio?.BGMVolume ?? 0.5f, sfxVolume = _audio?.SFXVolume ?? 1f, + // 유저 + playerHP = _raidManager?.Player?.CurrentHP ?? 0, + playerMaxHP = _raidManager?.Player?.MaxHP ?? 0, + playerHPRatio = _raidManager?.Player?.HPRatio ?? 0f, + playerDead = _raidManager?.Player?.IsDead ?? false, + playerGodMode = _raidManager?.Player?.GodMode ?? false, + bossAttacking = _raidManager?.Attack?.IsAttacking ?? false, }; } @@ -414,6 +448,31 @@ namespace Streamingle.Contents.BossRaid if (_audio != null) _audio.SFXVolume = sfxVol; BroadcastState(); break; + // 보스 공격 + case "boss_breath": + _raidManager?.ExecuteBreath(); + break; + case "boss_slam": + _raidManager?.ExecuteSlam(); + break; + case "boss_rage_burst": + _raidManager?.ExecuteRageBurst(); + break; + // 유저 제어 + case "set_player_hp": + float pRatio = msg["ratio"]?.Value() ?? 1f; + _raidManager?.SetPlayerHP(pRatio); + BroadcastState(); + break; + case "heal_player": + _raidManager?.HealPlayer(); + BroadcastState(); + break; + case "toggle_god_mode": + bool god = msg["value"]?.Value() ?? false; + _raidManager?.SetPlayerGodMode(god); + BroadcastState(); + break; case "get_state": BroadcastState(); break; diff --git a/streamingle-urp-analysis.md b/streamingle-urp-analysis.md new file mode 100644 index 000000000..4c3f79755 --- /dev/null +++ b/streamingle-urp-analysis.md @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5ead2579ffd2b263e17da4e91bd8be836e9d1005db1ae7839c569b2c38145839 +size 3416