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