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