diff --git a/Assets/JYY/Animator/PldDogControl.controller b/Assets/JYY/Animator/PldDogControl.controller
index 9d61f13d..7b80e503 100644
--- a/Assets/JYY/Animator/PldDogControl.controller
+++ b/Assets/JYY/Animator/PldDogControl.controller
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:481922b21d2de476145ad4fefaccad66d344052ffc0a2808e577b717e59be4e9
-size 18696
+oid sha256:9473c357cb74d4228523205a76d0a1a1cc71d82baf736270906c82a171efe7e6
+size 16154
diff --git a/Assets/JYY/Models.meta b/Assets/JYY/Models.meta
new file mode 100644
index 00000000..7143abc3
--- /dev/null
+++ b/Assets/JYY/Models.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: ac3af37988877784e91ab90c4ade37d9
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/JYY/Models/Y Bot.fbx b/Assets/JYY/Models/Y Bot.fbx
new file mode 100644
index 00000000..a5c4d7be
--- /dev/null
+++ b/Assets/JYY/Models/Y Bot.fbx
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:470fcb658dd731d414d635615d6232be394e7ea23915fb92ad41a0fca4d592bc
+size 1982368
diff --git a/Assets/JYY/Models/Y Bot.fbx.meta b/Assets/JYY/Models/Y Bot.fbx.meta
new file mode 100644
index 00000000..2e914645
--- /dev/null
+++ b/Assets/JYY/Models/Y Bot.fbx.meta
@@ -0,0 +1,109 @@
+fileFormatVersion: 2
+guid: decb96f75d6a2344baa18f902f21a131
+ModelImporter:
+ serializedVersion: 22200
+ internalIDToNameTable: []
+ externalObjects: {}
+ materials:
+ materialImportMode: 2
+ materialName: 0
+ materialSearch: 1
+ materialLocation: 1
+ animations:
+ legacyGenerateAnimations: 4
+ bakeSimulation: 0
+ resampleCurves: 1
+ optimizeGameObjects: 0
+ removeConstantScaleCurves: 0
+ motionNodeName:
+ rigImportErrors:
+ rigImportWarnings:
+ animationImportErrors:
+ animationImportWarnings:
+ animationRetargetingWarnings:
+ animationDoRetargetingWarnings: 0
+ importAnimatedCustomProperties: 0
+ importConstraints: 0
+ animationCompression: 3
+ animationRotationError: 0.5
+ animationPositionError: 0.5
+ animationScaleError: 0.5
+ animationWrapMode: 0
+ extraExposedTransformPaths: []
+ extraUserProperties: []
+ clipAnimations: []
+ isReadable: 0
+ meshes:
+ lODScreenPercentages: []
+ globalScale: 1
+ meshCompression: 0
+ addColliders: 0
+ useSRGBMaterialColor: 1
+ sortHierarchyByName: 1
+ importPhysicalCameras: 1
+ importVisibility: 1
+ importBlendShapes: 1
+ importCameras: 1
+ importLights: 1
+ nodeNameCollisionStrategy: 1
+ fileIdsGeneration: 2
+ swapUVChannels: 0
+ generateSecondaryUV: 0
+ useFileUnits: 1
+ keepQuads: 0
+ weldVertices: 1
+ bakeAxisConversion: 0
+ preserveHierarchy: 0
+ skinWeightsMode: 0
+ maxBonesPerVertex: 4
+ minBoneWeight: 0.001
+ optimizeBones: 1
+ meshOptimizationFlags: -1
+ indexFormat: 0
+ secondaryUVAngleDistortion: 8
+ secondaryUVAreaDistortion: 15.000001
+ secondaryUVHardAngle: 88
+ secondaryUVMarginMethod: 1
+ secondaryUVMinLightmapResolution: 40
+ secondaryUVMinObjectScale: 1
+ secondaryUVPackMargin: 4
+ useFileScale: 1
+ strictVertexDataChecks: 0
+ tangentSpace:
+ normalSmoothAngle: 60
+ normalImportMode: 0
+ tangentImportMode: 3
+ normalCalculationMode: 4
+ legacyComputeAllNormalsFromSmoothingGroupsWhenMeshHasBlendShapes: 0
+ blendShapeNormalImportMode: 1
+ normalSmoothingSource: 0
+ referencedClips: []
+ importAnimation: 1
+ humanDescription:
+ serializedVersion: 3
+ human: []
+ skeleton: []
+ armTwist: 0.5
+ foreArmTwist: 0.5
+ upperLegTwist: 0.5
+ legTwist: 0.5
+ armStretch: 0.05
+ legStretch: 0.05
+ feetSpacing: 0
+ globalScale: 1
+ rootMotionBoneName:
+ hasTranslationDoF: 0
+ hasExtraRoot: 1
+ skeletonHasParents: 1
+ lastHumanDescriptionAvatarSource: {instanceID: 0}
+ autoGenerateAvatarMappingIfUnspecified: 1
+ animationType: 3
+ humanoidOversampling: 1
+ avatarSetup: 1
+ addHumanoidExtraRootOnlyWhenUsingAvatar: 1
+ importBlendShapeDeformPercent: 1
+ remapMaterialsIfMaterialImportModeIsNone: 0
+ additionalBone: 0
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/JYY/Prefabs/AoE Indicator Chariot.prefab b/Assets/JYY/Prefabs/AoE Indicator Chariot.prefab
index 36214cb6..20a9ce49 100644
--- a/Assets/JYY/Prefabs/AoE Indicator Chariot.prefab
+++ b/Assets/JYY/Prefabs/AoE Indicator Chariot.prefab
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:acaadac3ed9caf040e05fc0f05648346d053813c3be9a3062dd7eeed0a8730b6
-size 4760
+oid sha256:da22007db35be49ea0f412048256923f214ff6be182768aba94ea8137146c067
+size 5237
diff --git a/Assets/JYY/Prefabs/AoE Indicator Horizontal.prefab b/Assets/JYY/Prefabs/AoE Indicator Horizontal.prefab
new file mode 100644
index 00000000..3b79cddf
--- /dev/null
+++ b/Assets/JYY/Prefabs/AoE Indicator Horizontal.prefab
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:27404af56e20a87bf6555fc1bbad80f7756f8412d15bccca896ea8c98a221ff7
+size 4474
diff --git a/Assets/JYY/Prefabs/AOEIndicatorHorizontal.prefab.meta b/Assets/JYY/Prefabs/AoE Indicator Horizontal.prefab.meta
similarity index 100%
rename from Assets/JYY/Prefabs/AOEIndicatorHorizontal.prefab.meta
rename to Assets/JYY/Prefabs/AoE Indicator Horizontal.prefab.meta
diff --git a/Assets/JYY/Prefabs/AoE Indicator Vertical.prefab b/Assets/JYY/Prefabs/AoE Indicator Vertical.prefab
new file mode 100644
index 00000000..438c2522
--- /dev/null
+++ b/Assets/JYY/Prefabs/AoE Indicator Vertical.prefab
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e6f2c2d8b80d4562e9a72bfc313a716717ae8bf33192053e85b12aeea9b9b9ff
+size 4459
diff --git a/Assets/JYY/Prefabs/AOEIndicatorVertical.prefab.meta b/Assets/JYY/Prefabs/AoE Indicator Vertical.prefab.meta
similarity index 100%
rename from Assets/JYY/Prefabs/AOEIndicatorVertical.prefab.meta
rename to Assets/JYY/Prefabs/AoE Indicator Vertical.prefab.meta
diff --git a/Assets/JYY/Prefabs/Charge slash red.prefab b/Assets/JYY/Prefabs/Charge slash red.prefab
new file mode 100644
index 00000000..3f562bef
--- /dev/null
+++ b/Assets/JYY/Prefabs/Charge slash red.prefab
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:401068a261779945bc03fed98b231b07965d7520470f19f5b1ff2b6bf73b4f59
+size 710841
diff --git a/Assets/JYY/Prefabs/Charge slash red.prefab.meta b/Assets/JYY/Prefabs/Charge slash red.prefab.meta
new file mode 100644
index 00000000..bf5f20d3
--- /dev/null
+++ b/Assets/JYY/Prefabs/Charge slash red.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 8a0ddc8dd6d760e4e93999c4d49dc16c
+PrefabImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/JYY/Prefabs/Explosion.prefab b/Assets/JYY/Prefabs/Explosion.prefab
new file mode 100644
index 00000000..780b51ec
--- /dev/null
+++ b/Assets/JYY/Prefabs/Explosion.prefab
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a95da2a8bdc43724d48bc36adc162d419090185d28b9df881af7de481a53c4f3
+size 473754
diff --git a/Assets/JYY/Prefabs/Explosion.prefab.meta b/Assets/JYY/Prefabs/Explosion.prefab.meta
new file mode 100644
index 00000000..5f3ac223
--- /dev/null
+++ b/Assets/JYY/Prefabs/Explosion.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 2ccd5acc2f6d74d4bb687fd2ee94b9de
+PrefabImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/JYY/Prefabs/Red energy explosion 1.prefab b/Assets/JYY/Prefabs/Red energy explosion 1.prefab
new file mode 100644
index 00000000..39f7c17d
--- /dev/null
+++ b/Assets/JYY/Prefabs/Red energy explosion 1.prefab
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1364f3853992b0997dcd5fb86204334c243e4b464bf6d96da6374dcb2e98f8d9
+size 1305159
diff --git a/Assets/JYY/Prefabs/Red energy explosion 1.prefab.meta b/Assets/JYY/Prefabs/Red energy explosion 1.prefab.meta
new file mode 100644
index 00000000..a8bb01f0
--- /dev/null
+++ b/Assets/JYY/Prefabs/Red energy explosion 1.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 49ada3fea2be23f4a988d4bc21dcaa32
+PrefabImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/JYY/Prefabs/Snow slash 1.prefab b/Assets/JYY/Prefabs/Snow slash 1.prefab
new file mode 100644
index 00000000..4b1759bb
--- /dev/null
+++ b/Assets/JYY/Prefabs/Snow slash 1.prefab
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:72a13e9bb38f18954e603c863b4b610882f8b43d55ba27499daf3bcfe42830b8
+size 474114
diff --git a/Assets/JYY/Prefabs/Snow slash 1.prefab.meta b/Assets/JYY/Prefabs/Snow slash 1.prefab.meta
new file mode 100644
index 00000000..a886fdef
--- /dev/null
+++ b/Assets/JYY/Prefabs/Snow slash 1.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 03a874e2d684ee04a9729b8363fdbb9c
+PrefabImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/JYY/Scenes/MonsterTest.unity b/Assets/JYY/Scenes/MonsterTest.unity
index 9e0ad0dc..f315080d 100644
--- a/Assets/JYY/Scenes/MonsterTest.unity
+++ b/Assets/JYY/Scenes/MonsterTest.unity
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:6a0de3e23ef1211a5b26d9fb80ba8911d8000a07e40a8b67cf4b33cb106539e6
-size 19722
+oid sha256:70e2e15667c9821fdb140d6ca8987d4caf3436e36abc139ba9da1192a5428331
+size 21018
diff --git a/Assets/Scripts/Character/Enemy/BossPattern/AoeControllerBase.cs b/Assets/Scripts/Character/Enemy/BossPattern/AoeControllerBase.cs
new file mode 100644
index 00000000..bf689d3f
--- /dev/null
+++ b/Assets/Scripts/Character/Enemy/BossPattern/AoeControllerBase.cs
@@ -0,0 +1,109 @@
+using System;
+using System.Collections;
+using UnityEngine;
+
+
+[Serializable]
+public struct DamageEffectData
+{
+ public int damage;
+ public float radius;
+ public float delay;
+ public LayerMask targetLayer;
+ public GameObject explosionEffectPrefab;
+}
+
+///
+/// AOE 범위 공격의 공통 로직을 처리하는 추상 베이스 클래스입니다.
+///
+public abstract class AoeControllerBase : MonoBehaviour
+{
+ [Header("경고 이펙트")]
+ [SerializeField] protected GameObject warningEffectInstance;
+
+ protected DamageEffectData _data;
+ private Action _slashAction;
+ private Action _destroyAction;
+
+ ///
+ /// 범위 공격 이펙트를 설정하고, 딜레이 후 폭발을 실행합니다.
+ ///
+ public void SetEffect(DamageEffectData data, Action slashAction, Action destroyAction)
+ {
+ _data = data;
+ _slashAction = slashAction;
+ _destroyAction = destroyAction;
+
+ ShowWarningEffect();
+ StartCoroutine(ExplodeAfterDelay());
+ }
+
+ protected virtual void ShowWarningEffect()
+ {
+ if (warningEffectInstance != null)
+ warningEffectInstance.SetActive(true);
+
+ float diameter = _data.radius * 2f;
+ transform.localScale = new Vector3(diameter, 1f, diameter);
+ }
+
+ private IEnumerator ExplodeAfterDelay()
+ {
+ yield return new WaitForSeconds(_data.delay);
+ Explode();
+ }
+
+ ///
+ /// 폭발 이펙트 생성, 데미지 처리, 콜백 호출 순서로 실행합니다.
+ ///
+ protected virtual void Explode()
+ {
+ // 경고 이펙트 숨기기
+ if (warningEffectInstance != null)
+ warningEffectInstance.SetActive(false);
+
+ ShowDamageEffect();
+
+ // 슬래시 액션(애니메이션 트리거) 호출
+ _slashAction?.Invoke();
+
+ // 범위 내 데미지 처리
+ HitCheck();
+
+ // 패턴 클리어 콜백
+ _destroyAction?.Invoke();
+
+ // 자기 자신 제거
+ Destroy(gameObject, 2f);
+ }
+
+ protected virtual void HitCheck()
+ {
+ var hits = Physics.OverlapSphere(transform.position, _data.radius, _data.targetLayer);
+ foreach (var hit in hits)
+ {
+ if (hit.CompareTag("Player"))
+ {
+ Debug.Log($"{hit.name}에게 {_data.damage} 데미지 적용");
+ // TODO: 실제 데미지 처리 로직 호출
+ }
+ }
+ }
+
+ protected virtual void ShowDamageEffect()
+ {
+ // 폭발 이펙트 생성
+ if (_data.explosionEffectPrefab != null)
+ {
+ var effect = Instantiate(_data.explosionEffectPrefab, transform.position, transform.rotation);
+ effect.transform.localScale = new Vector3(_data.radius, _data.radius, _data.radius);
+ Destroy(effect, 2f);
+ }
+ }
+
+ protected virtual void OnDrawGizmosSelected()
+ {
+ Gizmos.color = Color.red;
+ Gizmos.DrawWireSphere(transform.position, _data.radius);
+ }
+}
\ No newline at end of file
diff --git a/Assets/Scripts/Character/Enemy/BossPattern/AoeControllerBase.cs.meta b/Assets/Scripts/Character/Enemy/BossPattern/AoeControllerBase.cs.meta
new file mode 100644
index 00000000..532cedaa
--- /dev/null
+++ b/Assets/Scripts/Character/Enemy/BossPattern/AoeControllerBase.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 1dd40dca214a4038be21f64ce645e5d9
+timeCreated: 1745393630
\ No newline at end of file
diff --git a/Assets/Scripts/Character/Enemy/BossPattern/BoomAoeController.cs b/Assets/Scripts/Character/Enemy/BossPattern/BoomAoeController.cs
new file mode 100644
index 00000000..560b885d
--- /dev/null
+++ b/Assets/Scripts/Character/Enemy/BossPattern/BoomAoeController.cs
@@ -0,0 +1,4 @@
+public class BoomAoeController : AoeControllerBase
+{
+
+}
\ No newline at end of file
diff --git a/Assets/Scripts/Character/Enemy/BossPattern/BoomAoeController.cs.meta b/Assets/Scripts/Character/Enemy/BossPattern/BoomAoeController.cs.meta
new file mode 100644
index 00000000..8bf641f6
--- /dev/null
+++ b/Assets/Scripts/Character/Enemy/BossPattern/BoomAoeController.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: e32c2002701d49df83faa36b3da55436
+timeCreated: 1745395178
\ No newline at end of file
diff --git a/Assets/Scripts/Character/Enemy/BossPattern/ChariotAoeController.cs b/Assets/Scripts/Character/Enemy/BossPattern/ChariotAoeController.cs
index ed686c4d..e658d92a 100644
--- a/Assets/Scripts/Character/Enemy/BossPattern/ChariotAoeController.cs
+++ b/Assets/Scripts/Character/Enemy/BossPattern/ChariotAoeController.cs
@@ -1,85 +1,19 @@
using System;
-using System.Collections.Generic;
+using System.Collections;
using UnityEngine;
-using UnityEngine.Serialization;
-[Serializable]
-public struct DamageEffectData
+
+public class ChariotAoeController : AoeControllerBase
{
- public int damage;
- public float radius;
- public float delay;
- public LayerMask targetLayer;
-
- public GameObject explosionEffectPrefab;
-}
-
-public class ChariotAoeController : MonoBehaviour
-{
- [SerializeField] private GameObject warningEffectInstance;
-
- private DamageEffectData _data;
- private Action _destroyAction;
- private Action _slashAction;
-
- public void SetEffect(DamageEffectData data, Action slashAction, Action destroyAction)
+ protected override void ShowDamageEffect()
{
- _data = data;
- _slashAction = slashAction;
- _destroyAction = destroyAction;
-
- ShowWarningEffect();
- Invoke(nameof(Explode), _data.delay);
- }
-
- private void ShowWarningEffect()
- {
- warningEffectInstance.SetActive(true);
- float diameter = _data.radius * 2f;
- gameObject.transform.localScale = new Vector3(diameter, 1f, diameter);
- }
-
- private void Explode()
- {
- var effect = Instantiate(_data.explosionEffectPrefab, transform.position, Quaternion.identity);
-
- // 공격 전조 제거
- warningEffectInstance.SetActive(false);
-
- // 공격 애니메이션 실행
- _slashAction.Invoke();
-
- effect.transform.localScale = new Vector3(_data.radius, _data.radius, _data.radius);
-
- // 폭발 반경 내의 모든 콜라이더 가져오기
- Collider[] hitColliders = Physics.OverlapSphere(transform.position, _data.radius, _data.targetLayer);
- foreach (Collider hit in hitColliders)
+ // 폭발 이펙트 생성
+ if (_data.explosionEffectPrefab != null)
{
- if (hit.CompareTag("Player"))
- {
- // TODO : 데미지 부여
- Debug.Log(hit.name +"에게 공격 적중");
- }
+ var effect = Instantiate(_data.explosionEffectPrefab, transform.position, transform.rotation);
+ effect.transform.localScale = new Vector3(2f, 2f, 2f);
+ Destroy(effect, 2f);
}
-
- Exit(effect);
}
-
- private void Exit(GameObject effect)
- {
- Destroy(effect, 2f);
- Destroy(gameObject, 2f);
- }
-
- private void OnDestroy()
- {
- _destroyAction.Invoke();
- }
-
- private void OnDrawGizmosSelected()
- {
- Gizmos.color = Color.red;
- Gizmos.DrawWireSphere(transform.position, _data.radius);
- }
-}
\ No newline at end of file
+}
diff --git a/Assets/Scripts/Character/Enemy/BossPattern/HorizontalAoeController.cs b/Assets/Scripts/Character/Enemy/BossPattern/HorizontalAoeController.cs
new file mode 100644
index 00000000..6ef4c7c0
--- /dev/null
+++ b/Assets/Scripts/Character/Enemy/BossPattern/HorizontalAoeController.cs
@@ -0,0 +1,52 @@
+using UnityEngine;
+
+public class HorizontalAoeController : AoeControllerBase
+{
+ private float slashAngle = 270f;
+ private int gizmoSegments = 20;
+ protected override void HitCheck()
+ {
+ var hits = Physics.OverlapSphere(transform.position, _data.radius, _data.targetLayer);
+ foreach (var hit in hits)
+ {
+ if (!hit.CompareTag("Player")) continue;
+
+ Vector3 dir = hit.transform.position - transform.position;
+ dir.y = 0;
+ float angleToForward = Vector3.Angle(transform.forward, dir);
+
+ if (angleToForward <= slashAngle * 0.5f)
+ {
+ Debug.Log($"{hit.name}이(가) 횡적 슬래시 데미지 범위에 있습니다.");
+ Debug.Log($"{hit.name}에게 {_data.damage} 데미지 적용");
+ // TODO: 실제 데미지 처리 로직 호출
+ }
+ }
+ }
+
+ private void OnDrawGizmosSelected()
+ {
+ // 부채꼴 형태 기즈모 드로잉
+ Gizmos.color = Color.red;
+ Vector3 origin = transform.position;
+ float halfAngle = slashAngle * 0.5f;
+ float step = slashAngle / gizmoSegments;
+
+ // 시작 포인트
+ Vector3 prevDir = Quaternion.AngleAxis(-halfAngle, Vector3.up) * transform.forward;
+ Vector3 prevPoint = origin + prevDir.normalized * _data.radius;
+ Gizmos.DrawLine(origin, prevPoint);
+
+ for (int i = 1; i <= gizmoSegments; i++)
+ {
+ float currentAngle = -halfAngle + step * i;
+ Vector3 currDir = Quaternion.AngleAxis(currentAngle, Vector3.up) * transform.forward;
+ Vector3 currPoint = origin + currDir.normalized * _data.radius;
+ Gizmos.DrawLine(prevPoint, currPoint);
+ prevPoint = currPoint;
+ }
+
+ // 마지막 라인
+ Gizmos.DrawLine(origin, prevPoint);
+ }
+}
\ No newline at end of file
diff --git a/Assets/Scripts/Character/Enemy/BossPattern/HorizontalAoeController.cs.meta b/Assets/Scripts/Character/Enemy/BossPattern/HorizontalAoeController.cs.meta
new file mode 100644
index 00000000..0fc4ea79
--- /dev/null
+++ b/Assets/Scripts/Character/Enemy/BossPattern/HorizontalAoeController.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: e77da766410d47f9a7effebd30fd33f6
+timeCreated: 1745394588
\ No newline at end of file
diff --git a/Assets/Scripts/Character/Enemy/BossPattern/VerticalAoeController.cs b/Assets/Scripts/Character/Enemy/BossPattern/VerticalAoeController.cs
new file mode 100644
index 00000000..37b3fbb0
--- /dev/null
+++ b/Assets/Scripts/Character/Enemy/BossPattern/VerticalAoeController.cs
@@ -0,0 +1,51 @@
+using UnityEngine;
+
+public class VerticalAoeController : AoeControllerBase
+{
+
+ protected override void ShowWarningEffect()
+ {
+ if (warningEffectInstance != null)
+ warningEffectInstance.SetActive(true);
+
+ var centerCap = Vector3.forward * _data.radius;
+ float diameter = _data.radius * 2f;
+ transform.localScale = new Vector3(_data.radius, 1f, diameter);
+ transform.Translate(centerCap, Space.Self);
+ }
+
+ protected override void ShowDamageEffect()
+ {
+ // 폭발 이펙트 생성
+ if (_data.explosionEffectPrefab != null)
+ {
+ var effect = Instantiate(_data.explosionEffectPrefab, transform.position, transform.rotation);
+ effect.transform.localScale = new Vector3(_data.radius, _data.radius, _data.radius);
+ Destroy(effect, 2f);
+ }
+ }
+
+ protected override void HitCheck()
+ {
+ // 박스 판정 (사각형 직선)
+ Vector3 halfExtents = new Vector3(_data.radius, 1f, _data.radius * 2f);
+ Collider[] hits = Physics.OverlapBox(transform.position, halfExtents, transform.rotation, _data.targetLayer);
+
+ foreach (var hit in hits)
+ {
+ if (!hit.CompareTag("Player")) continue;
+ Debug.Log($"{hit.name} 사각형 범위에 있어 데미지 적용");
+ // TODO: 데미지 로직
+ }
+ }
+
+ private void OnDrawGizmosSelected()
+ {
+ Gizmos.color = Color.red;
+ Vector3 center = transform.position;
+ Vector3 size = new Vector3(_data.radius, 1f, _data.radius * 2f);
+ Gizmos.matrix = Matrix4x4.TRS(center, transform.rotation, Vector3.one);
+ Gizmos.DrawWireCube(Vector3.zero, size);
+ Gizmos.matrix = Matrix4x4.identity;
+ }
+}
\ No newline at end of file
diff --git a/Assets/Scripts/Character/Enemy/BossPattern/VerticalAoeController.cs.meta b/Assets/Scripts/Character/Enemy/BossPattern/VerticalAoeController.cs.meta
new file mode 100644
index 00000000..9fe09225
--- /dev/null
+++ b/Assets/Scripts/Character/Enemy/BossPattern/VerticalAoeController.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 8eb40ee5f0d943469026d2b6522dbe46
+timeCreated: 1745393484
\ No newline at end of file
diff --git a/Assets/Scripts/Character/Enemy/EnemyController.cs b/Assets/Scripts/Character/Enemy/EnemyController.cs
index 0cdca95a..56a57950 100644
--- a/Assets/Scripts/Character/Enemy/EnemyController.cs
+++ b/Assets/Scripts/Character/Enemy/EnemyController.cs
@@ -18,6 +18,16 @@ public abstract class EnemyController : CharacterBase
public EnemyState CurrentState {get; private set;}
public LayerMask TargetLayerMask => targetLayerMask;
public float MoveSpeed => moveSpeed;
+ public bool IsMeleeCombat { get; protected set; }
+
+ // -----
+ // 애니메이션 관련
+ private int _currentAnimationTrigger = -1;
+
+ // 애니메이션 파라미터 해시값
+ public static readonly int Idle = Animator.StringToHash("Idle");
+ public static readonly int Dead = Animator.StringToHash("Dead");
+ public static readonly int Trace = Animator.StringToHash("Trace");
// -----
// 상태 변수
@@ -55,7 +65,7 @@ public abstract class EnemyController : CharacterBase
SetState(EnemyState.Idle);
}
- protected void Update()
+ protected virtual void Update()
{
if (CurrentState != EnemyState.None)
{
@@ -98,5 +108,38 @@ public abstract class EnemyController : CharacterBase
#endregion
+ #region 애니메이션 제어
+
+ // Trigger
+ public void SetAnimation(int hashName)
+ {
+ if (_currentAnimationTrigger != -1)
+ {
+ EnemyAnimator.ResetTrigger(_currentAnimationTrigger);
+ }
+
+ EnemyAnimator.SetTrigger(hashName);
+ _currentAnimationTrigger = hashName;
+ }
+
+ // Bool
+ public void SetAnimation(int hashName, bool value)
+ {
+ EnemyAnimator.SetBool(hashName, value);
+ }
+
+ // Float
+ public void SetAnimation(int hashName, float value)
+ {
+ EnemyAnimator.SetFloat(hashName, value);
+ }
+
+ // Integer
+ public void SetAnimation(int hashName, int value)
+ {
+ EnemyAnimator.SetInteger(hashName, value);
+ }
+
+ #endregion
}
diff --git a/Assets/Scripts/Character/Enemy/EnemyState/EnemyStateAttack.cs b/Assets/Scripts/Character/Enemy/EnemyState/EnemyStateAttack.cs
index b102ce4e..194ce30c 100644
--- a/Assets/Scripts/Character/Enemy/EnemyState/EnemyStateAttack.cs
+++ b/Assets/Scripts/Character/Enemy/EnemyState/EnemyStateAttack.cs
@@ -5,12 +5,11 @@ using UnityEngine;
public class EnemyStateAttack : IEnemyState
{
private EnemyController _enemyController;
- private Animator _animator;
+
public void Enter(EnemyController enemyController)
{
_enemyController = enemyController;
- _animator = _enemyController.EnemyAnimator;
}
public void Update()
@@ -20,7 +19,6 @@ public class EnemyStateAttack : IEnemyState
public void Exit()
{
- _animator = null;
_enemyController = null;
}
}
\ No newline at end of file
diff --git a/Assets/Scripts/Character/Enemy/EnemyState/EnemyStateDead.cs b/Assets/Scripts/Character/Enemy/EnemyState/EnemyStateDead.cs
index 158b9eab..ba34bb17 100644
--- a/Assets/Scripts/Character/Enemy/EnemyState/EnemyStateDead.cs
+++ b/Assets/Scripts/Character/Enemy/EnemyState/EnemyStateDead.cs
@@ -5,7 +5,7 @@
public void Enter(EnemyController enemyController)
{
_enemyController = enemyController;
- _enemyController.EnemyAnimator.SetTrigger("Dead");
+ _enemyController.SetAnimation(EnemyController.Dead);
}
public void Update()
diff --git a/Assets/Scripts/Character/Enemy/EnemyState/EnemyStateIdle.cs b/Assets/Scripts/Character/Enemy/EnemyState/EnemyStateIdle.cs
index b8714ef6..a0fbbc52 100644
--- a/Assets/Scripts/Character/Enemy/EnemyState/EnemyStateIdle.cs
+++ b/Assets/Scripts/Character/Enemy/EnemyState/EnemyStateIdle.cs
@@ -2,13 +2,14 @@
public class EnemyStateIdle: IEnemyState
{
- private static readonly int Idle = Animator.StringToHash("Idle");
+
private EnemyController _enemyController;
public void Enter(EnemyController enemyController)
{
_enemyController = enemyController;
- _enemyController.EnemyAnimator.SetBool(Idle, true);
+ Debug.Log("## Idle 상태 진입");
+ _enemyController.SetAnimation(EnemyController.Idle, true);
}
public void Update()
@@ -22,7 +23,7 @@ public class EnemyStateIdle: IEnemyState
public void Exit()
{
- _enemyController.EnemyAnimator.SetBool(Idle, false);
+ _enemyController.SetAnimation(EnemyController.Idle, false);
_enemyController = null;
}
}
\ No newline at end of file
diff --git a/Assets/Scripts/Character/Enemy/EnemyState/EnemyStateTrace.cs b/Assets/Scripts/Character/Enemy/EnemyState/EnemyStateTrace.cs
index 4d9dce94..b301d19a 100644
--- a/Assets/Scripts/Character/Enemy/EnemyState/EnemyStateTrace.cs
+++ b/Assets/Scripts/Character/Enemy/EnemyState/EnemyStateTrace.cs
@@ -2,9 +2,6 @@
public class EnemyStateTrace : IEnemyState
{
- private static readonly int MoveSpeed = Animator.StringToHash("MoveSpeed");
- private static readonly int Trace = Animator.StringToHash("Trace");
-
private EnemyController _enemyController;
private Transform _detectPlayerTransform;
@@ -14,7 +11,7 @@ public class EnemyStateTrace : IEnemyState
public void Enter(EnemyController enemyController)
{
_enemyController = enemyController;
-
+ Debug.Log("## Trace 상태 진입");
_detectPlayerTransform = _enemyController.TraceTargetTransform;
if (!_detectPlayerTransform)
{
@@ -28,25 +25,31 @@ public class EnemyStateTrace : IEnemyState
_enemyController.Agent.SetDestination(_detectPlayerTransform.position);
}
- _enemyController.EnemyAnimator.SetBool(Trace, true);
+ _enemyController.SetAnimation(EnemyController.Trace, true);
}
public void Update()
{
+ if(_enemyController.IsMeleeCombat) return;
if (_enemyController.Agent.enabled != true) return;
- // 일정 주기로 찾은 플레이어의 위치를 갱신해서 갱신된 위치로 이동
- FindTargetPosition();
-
PlayerTracking();
if (_enemyController.Agent.remainingDistance <= _enemyController.Agent.stoppingDistance)
{
// TODO: 타겟에 도착함 -> 공격 준비
- _enemyController.SetState(EnemyState.Attack);
+ // _enemyController.SetState(EnemyState.Attack);
}
}
+ public void Exit()
+ {
+ _detectPlayerTransform = null;
+ _enemyController.SetAnimation(EnemyController.Trace, false);
+ _enemyController = null;
+ }
+
+ // 일정 주기로 찾은 플레이어의 위치를 갱신해서 갱신된 위치로 이동
private void FindTargetPosition()
{
if (_detectPlayerInCircleWaitTime > MaxDetectPlayerInCircleWaitTime)
@@ -68,6 +71,8 @@ public class EnemyStateTrace : IEnemyState
// 플레이어를 추적하는 속도를 제어하는 함수
private void PlayerTracking()
{
+ FindTargetPosition();
+
float distance = (_detectPlayerTransform.position - _enemyController.transform.position).magnitude;
if (distance > 2f)
@@ -97,17 +102,6 @@ public class EnemyStateTrace : IEnemyState
);
}
}
-
- // 실제 속도 기반으로 애니메이션 제어
- float currentSpeed = _enemyController.Agent.velocity.magnitude;
- _enemyController.EnemyAnimator.SetFloat(MoveSpeed, currentSpeed);
- }
-
- public void Exit()
- {
- _detectPlayerTransform = null;
- _enemyController.EnemyAnimator.SetBool(Trace, false);
- _enemyController = null;
}
}
diff --git a/Assets/Scripts/Character/Enemy/PldDogController.cs b/Assets/Scripts/Character/Enemy/PldDogController.cs
index 9ca31e90..2655b9e8 100644
--- a/Assets/Scripts/Character/Enemy/PldDogController.cs
+++ b/Assets/Scripts/Character/Enemy/PldDogController.cs
@@ -1,24 +1,30 @@
using System;
using System.Collections;
+using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
+using UnityEngine.Serialization;
using Random = UnityEngine.Random;
public class PldDogController : EnemyController
{
private static readonly int WindUp = Animator.StringToHash("WindUp");
- private static readonly int VertiSlash = Animator.StringToHash("VertiSlash");
+ private static readonly int Slash = Animator.StringToHash("Slash");
+ private static readonly int BoomShot = Animator.StringToHash("BoomShot");
[Header("공격 패턴 관련")]
[SerializeField] private float patternInterval = 3f;
+ [SerializeField] private float meleeRange = 2f;
+ [SerializeField] private float bombTriggerDelay = 1.5f;
- private float _patternTimer = 0f;
- private int _currentPatternIndex = 0;
- private bool _isPatternRunning = false;
+ [Header("폭탄 패턴 설정")]
+ [SerializeField] private int bombCount = 1;
+ [SerializeField] private Vector3 bombScale = new Vector3(5f, 5f, 5f);
[Header("각종 데미지 이펙트 세트")]
[SerializeField] private GameObject chariotSlashWarning;
[SerializeField] private GameObject chariotSlash;
+ [SerializeField] private GameObject boomExplosion;
[Space(10)]
[SerializeField] private GameObject verticalWarning;
@@ -28,127 +34,109 @@ public class PldDogController : EnemyController
[SerializeField] private GameObject horizontalWarning;
[SerializeField] private GameObject horizontalSlash;
- private bool _isInTrace;
- private bool _isInAttack;
+ private float _patternTimer = 0f;
+ private int _currentPatternIndex = 0;
+ private bool _isPatternRunning = false;
private bool _isFirstAttack = true;
- private void Update()
+ private List _patternActions;
+
+ protected override void Awake()
+ {
+ base.Awake();
+
+ _patternActions = new List
+ {
+ ChariotSlashPattern,
+ VerticalSlashPattern,
+ HorizontalSlashPattern
+ };
+ }
+
+ protected override void Update()
{
base.Update();
- CheckIsInBattle();
+ if (CurrentState != EnemyState.Trace || _isPatternRunning)
+ return;
- if (_isInAttack)
+ float distanceToPlayer = Vector3.Distance(transform.position, TraceTargetTransform.position);
+
+ if (distanceToPlayer <= meleeRange) // 근접 범위
{
+ if (!Agent.isStopped) Agent.isStopped = true;
_patternTimer += Time.deltaTime;
if (!_isPatternRunning && (_isFirstAttack || _patternTimer >= patternInterval))
{
- Agent.enabled = false;
-
- // TODO: 순서대로 패턴 실행
ExecutePattern(_currentPatternIndex);
- // _currentPatternIndex = (_currentPatternIndex + 1) % 3; // 패턴 순환
-
_isFirstAttack = false;
}
}
-
- if (_isInTrace)
+ else
{
+ if (Agent.isStopped) Agent.isStopped = false;
+ Agent.SetDestination(TraceTargetTransform.position);
_patternTimer += Time.deltaTime;
if (!_isPatternRunning && _patternTimer >= patternInterval)
{
- _isPatternRunning = true;
-
- float distanceToPlayer = Vector3.Distance(transform.position, TraceTargetTransform.position);
-
- if (distanceToPlayer > 3f)
- {
- BombThrowPattern();
- }
+ BombThrowPattern();
}
}
}
- private void CheckIsInBattle()
- {
- switch (CurrentState)
- {
- case EnemyState.Attack:
- _isInAttack = true;
- _isInTrace = false;
- break;
- case EnemyState.Trace:
- _isInTrace = true;
- _isInAttack = false;
- break;
- }
- }
-
private void ExecutePattern(int patternIndex)
{
_isPatternRunning = true;
+ Agent.isStopped = true;
+ IsMeleeCombat = true;
- switch (patternIndex)
- {
- case 0:
- {
- ChariotSlashPattern();
- break;
- }
- case 1:
- {
- break;
- }
- case 2:
- {
- break;
- }
- }
+ _patternActions[_currentPatternIndex]?.Invoke();
+
+ _currentPatternIndex = (_currentPatternIndex + 1) % _patternActions.Count; // 패턴 순환
}
+ // 순환 패턴과 별개로 동작하는 특수 패턴
private void BombThrowPattern()
{
Debug.Log("BombThrowPattern: 보스가 폭탄을 던집니다.");
-
- int bombCount = 1; // 한 번에 몇 개 던질지
- float radius = 2f; // 무작위로 던질 경우 범위
+ SetAnimation(BoomShot);
+ _isPatternRunning = true;
+ Agent.isStopped = true;
for (int i = 0; i < bombCount; i++)
{
- // Vector3 randomPos = TraceTargetTransform.position + (Random.insideUnitSphere * radius);
Vector3 targetPos = TraceTargetTransform.position;
- targetPos.y = 0.1f; // 지면에 맞추기
+ targetPos.y = 0.1f; // 지면에 맞춤
- var boomObj = Instantiate(chariotSlashWarning, targetPos, Quaternion.identity);
- boomObj.transform.localScale = new Vector3(5f, 5f, 5f); // TODO : 하드 코딩됨 | 개선... 해야겠지??
- var boom = boomObj.GetComponent();
+ var warning = Instantiate(chariotSlashWarning, targetPos, Quaternion.identity);
+ warning.transform.localScale = bombScale;
+ var aoe = warning.GetComponent();
- DamageEffectData effectData = new DamageEffectData()
+ var effectData = new DamageEffectData
{
damage = (int)attackPower,
- radius = 5f,
- delay = 1.5f,
+ radius = bombScale.x,
+ delay = bombTriggerDelay,
targetLayer = TargetLayerMask,
- explosionEffectPrefab = chariotSlash
+ explosionEffectPrefab = boomExplosion
};
- boom.SetEffect(effectData, ()=>{ }, PatternClear);
+ aoe.SetEffect(effectData, null, PatternClear);
}
}
private void ChariotSlashPattern()
{
Debug.Log("ChariotSlashPattern: 보스가 차지 슬래시를 사용합니다.");
- WindUpAnimationStart();
+ WindUpAnimation();
- var slash = Instantiate(chariotSlashWarning, transform.position, Quaternion.identity)
+ var warning = Instantiate(chariotSlashWarning, transform.position, Quaternion.identity)
.GetComponent();
- DamageEffectData effectData = new DamageEffectData()
+ var effectData = new DamageEffectData
{
damage = (int)attackPower,
radius = 7.5f,
@@ -157,35 +145,64 @@ public class PldDogController : EnemyController
explosionEffectPrefab = chariotSlash
};
- slash.SetEffect(effectData, SlashAnimationPlay,
- () =>
- {
- PatternClear();
- WindUpAnimationEnd();
- SetState(EnemyState.Trace);
- }
- );
+ warning.SetEffect(effectData, SlashAnimationPlay, PatternClear);
}
- private void WindUpAnimationStart()
+ private void VerticalSlashPattern()
{
- EnemyAnimator.SetBool(WindUp, true);
+ Debug.Log("VerticalSlashPattern: 보스가 수직 슬래시를 사용합니다.");
+ WindUpAnimation();
+
+ var warning = Instantiate(verticalWarning, transform.position, transform.rotation)
+ .GetComponent();
+
+ var effectData = new DamageEffectData
+ {
+ damage = (int)attackPower,
+ radius = 5f,
+ delay = 2f,
+ targetLayer = TargetLayerMask,
+ explosionEffectPrefab = verticalSlash
+ };
+
+ warning.SetEffect(effectData, SlashAnimationPlay, PatternClear);
}
- private void WindUpAnimationEnd()
+ private void HorizontalSlashPattern()
{
- EnemyAnimator.SetBool(WindUp, false);
+ Debug.Log("HorizontalSlashPattern: 보스가 횡적 슬래시를 사용합니다.");
+ WindUpAnimation();
+
+ var warning = Instantiate(horizontalWarning, transform.position, transform.rotation)
+ .GetComponent();
+
+ var effectData = new DamageEffectData
+ {
+ damage = (int)attackPower,
+ radius = 15f,
+ delay = 2f,
+ targetLayer = TargetLayerMask,
+ explosionEffectPrefab = horizontalSlash
+ };
+
+ warning.SetEffect(effectData, SlashAnimationPlay, PatternClear);
+ }
+
+ private void WindUpAnimation()
+ {
+ SetAnimation(WindUp);
}
private void SlashAnimationPlay()
{
- EnemyAnimator.SetTrigger(VertiSlash);
+ SetAnimation(Slash);
}
private void PatternClear()
{
_isPatternRunning = false;
+ IsMeleeCombat = false;
_patternTimer = 0f;
- Agent.enabled = true;
+ Agent.isStopped = false;
}
}