From a4a9316e9c2ad60f4fe9d82267eaa234df5e6f29 Mon Sep 17 00:00:00 2001 From: "DESKTOP-VVOCIJO\\PC" Date: Thu, 21 May 2026 15:22:07 +0900 Subject: [PATCH] =?UTF-8?q?2026-05-21=20=EB=B3=B4=EC=8A=A4=20=ED=8A=B9?= =?UTF-8?q?=EC=88=98=EC=8A=A4=ED=82=AC1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/01_Scenes/BossScene.unity | 4 +- Assets/02_Scripts/Combat/HazardHitbox.cs | 32 ++++ Assets/02_Scripts/Combat/HazardHitbox.cs.meta | 2 + Assets/02_Scripts/Enemy/BossAI.cs | 172 ++++++++++++++---- Assets/02_Scripts/Enemy/Skills.meta | 8 + Assets/02_Scripts/Enemy/Skills/BossSkill.cs | 58 ++++++ .../02_Scripts/Enemy/Skills/BossSkill.cs.meta | 2 + Assets/02_Scripts/Enemy/Skills/HazardSkill.cs | 84 +++++++++ .../Enemy/Skills/HazardSkill.cs.meta | 2 + .../ColorMan/Prefabs/BossMan.prefab | 4 +- Assets/08_Objects/BossSkills.meta | 8 + .../08_Objects/BossSkills/BossSkills.prefab | 3 + .../BossSkills/BossSkills.prefab.meta | 7 + Assets/08_Objects/BossSkills/HazardSkill.meta | 8 + .../BossSkills/HazardSkill/Animations.meta | 8 + .../HazardSkillAnimController.controller | 130 +++++++++++++ .../HazardSkillAnimController.controller.meta | 8 + .../Animations/HazardSkillDown.anim | 3 + .../Animations/HazardSkillDown.anim.meta | 8 + .../Animations/HazardSkillRight.anim | 3 + .../Animations/HazardSkillRight.anim.meta | 8 + .../HazardSkill/HazardHitBox.prefab | 3 + .../HazardSkill/HazardHitBox.prefab.meta | 7 + 23 files changed, 533 insertions(+), 39 deletions(-) create mode 100644 Assets/02_Scripts/Combat/HazardHitbox.cs create mode 100644 Assets/02_Scripts/Combat/HazardHitbox.cs.meta create mode 100644 Assets/02_Scripts/Enemy/Skills.meta create mode 100644 Assets/02_Scripts/Enemy/Skills/BossSkill.cs create mode 100644 Assets/02_Scripts/Enemy/Skills/BossSkill.cs.meta create mode 100644 Assets/02_Scripts/Enemy/Skills/HazardSkill.cs create mode 100644 Assets/02_Scripts/Enemy/Skills/HazardSkill.cs.meta create mode 100644 Assets/08_Objects/BossSkills.meta create mode 100644 Assets/08_Objects/BossSkills/BossSkills.prefab create mode 100644 Assets/08_Objects/BossSkills/BossSkills.prefab.meta create mode 100644 Assets/08_Objects/BossSkills/HazardSkill.meta create mode 100644 Assets/08_Objects/BossSkills/HazardSkill/Animations.meta create mode 100644 Assets/08_Objects/BossSkills/HazardSkill/Animations/HazardSkillAnimController.controller create mode 100644 Assets/08_Objects/BossSkills/HazardSkill/Animations/HazardSkillAnimController.controller.meta create mode 100644 Assets/08_Objects/BossSkills/HazardSkill/Animations/HazardSkillDown.anim create mode 100644 Assets/08_Objects/BossSkills/HazardSkill/Animations/HazardSkillDown.anim.meta create mode 100644 Assets/08_Objects/BossSkills/HazardSkill/Animations/HazardSkillRight.anim create mode 100644 Assets/08_Objects/BossSkills/HazardSkill/Animations/HazardSkillRight.anim.meta create mode 100644 Assets/08_Objects/BossSkills/HazardSkill/HazardHitBox.prefab create mode 100644 Assets/08_Objects/BossSkills/HazardSkill/HazardHitBox.prefab.meta diff --git a/Assets/01_Scenes/BossScene.unity b/Assets/01_Scenes/BossScene.unity index f770721..8706f5b 100644 --- a/Assets/01_Scenes/BossScene.unity +++ b/Assets/01_Scenes/BossScene.unity @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d62d949614848e721322d14e510710b31069c38ef14364f5772e9ce96ee59d0c -size 82715 +oid sha256:b56811750a76aa8ba83f6c3c9a33794a4c6f9112b205849f5dfd5068b64eba0f +size 85971 diff --git a/Assets/02_Scripts/Combat/HazardHitbox.cs b/Assets/02_Scripts/Combat/HazardHitbox.cs new file mode 100644 index 0000000..032f967 --- /dev/null +++ b/Assets/02_Scripts/Combat/HazardHitbox.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using UnityEngine; + +[RequireComponent(typeof(Collider2D))] +public class HazardHitbox : MonoBehaviour +{ + [SerializeField] private LayerMask _targetLayer; + [SerializeField] private LayerMask _blockLayer; // 피해를 줄 레이어 (보통 Player) + private int _damage = 0; + private Collider2D _collider; + private void Awake() + { + _collider = GetComponent(); + _collider.isTrigger = true; + _collider.enabled = false; + } + + private void OnTriggerStay2D(Collider2D other) + { + TryDamage(other); + } + + private void TryDamage(Collider2D other) + { + if ((_targetLayer.value & (1 << other.gameObject.layer)) == 0) return; + if (!other.TryGetComponent(out var target)) + target = other.GetComponentInParent(); + if (target == null) return; + + target.TakeDamage(_damage); + } +} diff --git a/Assets/02_Scripts/Combat/HazardHitbox.cs.meta b/Assets/02_Scripts/Combat/HazardHitbox.cs.meta new file mode 100644 index 0000000..4abe0ca --- /dev/null +++ b/Assets/02_Scripts/Combat/HazardHitbox.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 906fae86c8ab8ea4faa1855e18a573d0 \ No newline at end of file diff --git a/Assets/02_Scripts/Enemy/BossAI.cs b/Assets/02_Scripts/Enemy/BossAI.cs index 3ca5b67..856930d 100644 --- a/Assets/02_Scripts/Enemy/BossAI.cs +++ b/Assets/02_Scripts/Enemy/BossAI.cs @@ -1,11 +1,7 @@ using System; using UnityEngine; -// ============================================================================ -// BossAttack -// ---------------------------------------------------------------------------- // 보스 공격 하나의 설정. 별도 .asset 없이 BossAI 인스펙터에서 배열로 편집. -// ============================================================================ [Serializable] public class BossAttack { @@ -14,7 +10,7 @@ public class BossAttack public float Range = 1.5f; // 타겟이 이 X거리 안일 때만 사용 public float Windup = 0.4f; // 공격 시작 ~ 데미지 발생 (선딜) public float Duration = 0.9f; // 공격 동작 전체 길이 (이 동안 이동 정지) - public float Cooldown = 2f; // 이 공격 자체의 재사용 대기 + public float Cooldown = 10f; // 이 공격 자체의 재사용 대기 public int Damage = 15; public Vector2 HitVelocity = new Vector2(5f, 2f); // 피격자 넉백 (X는 보스 facing 방향으로 부호 적용) public string AnimationState = "BossAttack"; @@ -23,10 +19,10 @@ public class BossAttack // ============================================================================ // BossAI // ---------------------------------------------------------------------------- -// 보스의 두뇌. EnemyAI와 골격(감지→추격→공격)은 같지만: -// - 여러 공격(BossAttack 배열)을 보유하고, 사거리·쿨다운·페이즈 조건을 -// 만족하는 것 중 무작위로 골라 사용 -// - 각 공격의 MinPhase로 페이즈 잠금 → Boss.CurrentPhase가 오르면 신규 공격 해금 +// 보스의 두뇌. 감지 → 추격 → 공격 상태머신. +// - 단순 공격: BossAttack 데이터 배열 (사거리·쿨다운·페이즈 조건) +// - 특별 스킬: BossSkill 프리팹 배열. 시전 시 Instantiate → 끝나면 스스로 파괴 +// - 행동 결정: 특별 스킬 → 단순 공격 → 추격 순으로 시도 // 같은 GameObject의 Enemy(몸)·Boss(페이즈)와 함께 동작. // ============================================================================ [RequireComponent(typeof(Enemy))] @@ -47,8 +43,9 @@ private enum AIState { Idle, Chase, Attack } [Header("Attacks")] [SerializeField] private BossAttack[] _attacks; - [SerializeField] private float _attackVerticalTolerance = 1.5f; // 공격 적중 인정 세로 오차 - [SerializeField] private float _globalAttackCooldown = 0.6f; // 공격 종료 후 다음 공격까지 최소 간격 + [SerializeField] private BossSkill[] _skillPrefabs; // 특별 스킬 프리팹 (시전 시 Instantiate) + [SerializeField] private float _attackVerticalTolerance = 1.5f; // 공격 적중 인정 세로 오차 + [SerializeField] private float _globalAttackCooldown = 0.6f; // 공격/스킬 종료 후 다음 행동까지 최소 간격 [Header("Animation")] [SerializeField] private string _idleAnimationState = "Idle"; @@ -65,13 +62,18 @@ private enum AIState { Idle, Chase, Attack } private AIState _state = AIState.Idle; private bool _hasAggro; - private float[] _attackCooldownTimers; // _attacks와 1:1 — 각 공격의 남은 쿨다운 - private int[] _attackPickBuffer; // PickAttackIndex 후보 수집용 (GC 회피) - private float _globalCooldownTimer; // 공격 사이 전역 대기 + private float[] _attackCooldownTimers; // _attacks와 1:1 — 각 공격의 남은 쿨다운 + private int[] _attackPickBuffer; // PickAttackIndex 후보 수집용 (GC 회피) + private float[] _skillCooldownTimers; // _skillPrefabs와 1:1 — 보스별 스킬 쿨다운 + private int[] _skillPickBuffer; // PickSkillIndex 후보 수집용 (GC 회피) + private float _globalCooldownTimer; // 공격/스킬 사이 전역 대기 - private BossAttack _currentAttack; // 진행 중인 공격 + private BossSkill _runningSkillInstance; // 진행 중인 스킬 인스턴스 (없거나 끝나면 null) + private bool _skillInProgress; // 스킬 시전 중 (인스턴스 생성 ~ 파괴) + + private BossAttack _currentAttack; // 진행 중인 단순 공격 private bool _isAttacking; - private bool _attackDamageApplied; // 이번 공격에서 데미지를 이미 줬는지 + private bool _attackDamageApplied; // 이번 공격에서 데미지를 이미 줬는지 private float _attackTimer; private float _facingDirection = 1f; @@ -86,9 +88,13 @@ private void Awake() _anim = GetComponentInChildren(); _spriteRenderer = GetComponentInChildren(); - int count = _attacks != null ? _attacks.Length : 0; - _attackCooldownTimers = new float[count]; - _attackPickBuffer = new int[count]; + int attackCount = _attacks != null ? _attacks.Length : 0; + _attackCooldownTimers = new float[attackCount]; + _attackPickBuffer = new int[attackCount]; + + int skillCount = _skillPrefabs != null ? _skillPrefabs.Length : 0; + _skillCooldownTimers = new float[skillCount]; + _skillPickBuffer = new int[skillCount]; } private void Update() @@ -96,16 +102,29 @@ private void Update() TickCooldowns(); ResolveTarget(); - // 사망 / 행동 불가(피격 경직·잡힘) / 페이즈 전환 중 / 타겟 없음 → 정지. + // 사망 / 행동 불가(피격 경직·잡힘) / 페이즈 전환 중 / 타겟 없음 → 정지 + 진행 중 행동 취소. if (_health.IsDead || !_enemy.CanUseAI || (_boss != null && _boss.IsTransitioning) || _target == null) { if (_isAttacking) CancelAttack(); + CancelRunningSkill(); StopMoving(); SetState(AIState.Idle); return; } + // 특별 스킬 진행 중이면 스킬에 전부 맡기고 대기. 인스턴스가 사라지면(종료) 후딜 부여. + if (_skillInProgress) + { + if (_runningSkillInstance != null) + { + StopMoving(); + return; + } + _skillInProgress = false; + _globalCooldownTimer = _globalAttackCooldown; + } + RefreshAggro(); // 공격 중이면 facing을 고정한 채 공격 진행만 처리. @@ -124,12 +143,25 @@ private void Update() return; } - // 사용 가능한 공격이 있으면 공격 시작, 없으면 추격. - int attackIndex = _globalCooldownTimer <= 0f ? PickAttackIndex() : -1; - if (attackIndex >= 0) - BeginAttack(attackIndex); - else - SetState(AIState.Chase); + // 전역 쿨다운이 끝났으면 특별 스킬 → 단순 공격 순으로 시도. 둘 다 안 되면 추격. + if (_globalCooldownTimer <= 0f) + { + int skillIndex = PickSkillIndex(); + if (skillIndex >= 0) + { + BeginSkill(skillIndex); + return; + } + + int attackIndex = PickAttackIndex(); + if (attackIndex >= 0) + { + BeginAttack(attackIndex); + return; + } + } + + SetState(AIState.Chase); } private void FixedUpdate() @@ -146,7 +178,7 @@ private void FixedUpdate() _rb.linearVelocity = v; } - // ─── 공격 ──────────────────────────────────────────────────────────── + // ─── 단순 공격 ──────────────────────────────────────────────────────── // 현재 사용 가능한 공격 후보 중 무작위 1개의 인덱스 반환. 없으면 -1. // 조건: 페이즈 해금 + 쿨다운 종료 + 사거리 안. @@ -181,7 +213,7 @@ private void BeginAttack(int index) _attackDamageApplied = false; _attackTimer = 0f; _attackCooldownTimers[index] = _currentAttack.Cooldown; - // 공격 동작 길이 + 후딜만큼 다음 공격을 막는다. + // 공격 동작 길이 + 후딜만큼 다음 행동을 막는다. _globalCooldownTimer = _currentAttack.Duration + _globalAttackCooldown; _state = AIState.Attack; @@ -234,16 +266,80 @@ private void ApplyAttackDamage() _targetDamageable.TakeDamage(_currentAttack.Damage, knockback); } + // ─── 특별 스킬 ─────────────────────────────────────────────────────── + + // 사용 가능한 스킬 프리팹 후보 중 무작위 1개의 인덱스 반환. 없으면 -1. + // 조건은 프리팹에서 직접 읽는다 (MinPhase/Range). 쿨다운은 보스별 타이머로. + private int PickSkillIndex() + { + if (_skillPrefabs == null || _skillPrefabs.Length == 0 || _target == null) return -1; + + float distanceX = Mathf.Abs(_target.position.x - transform.position.x); + int phase = _boss != null ? _boss.CurrentPhase : 0; + + int candidateCount = 0; + for (int i = 0; i < _skillPrefabs.Length; i++) + { + BossSkill prefab = _skillPrefabs[i]; + if (prefab == null) continue; + if (phase < prefab.MinPhase) continue; // 페이즈 미해금 + if (_skillCooldownTimers[i] > 0f) continue; // 쿨다운 중 + if (distanceX > prefab.Range) continue; // 사거리 밖 + _skillPickBuffer[candidateCount++] = i; + } + + if (candidateCount == 0) return -1; + return _skillPickBuffer[UnityEngine.Random.Range(0, candidateCount)]; + } + + private void BeginSkill(int index) + { + BossSkill prefab = _skillPrefabs[index]; + _skillCooldownTimers[index] = prefab.Cooldown; + + _state = AIState.Idle; + StopMoving(); + UpdateFacing(); // _facingDirection을 타겟 방향으로 갱신 + + // 스킬 프리팹을 보스 위치에 생성하고 보스의 자식으로 둔다 (보스 파괴 시 함께 정리). + BossSkill instance = Instantiate(prefab, transform.position, Quaternion.identity, transform); + + // 보스가 왼쪽을 보면 스킬도 X 반전 (콜라이더 패턴이 시전 방향을 따르게). + if (_facingDirection < 0f) + { + Vector3 scale = instance.transform.localScale; + scale.x = -Mathf.Abs(scale.x); + instance.transform.localScale = scale; + } + + _runningSkillInstance = instance; + _skillInProgress = true; + + PlayAnim(prefab.CasterAnimationState); // 보스는 시전 애니메이션 재생 + instance.Begin(); + } + + // 진행 중인 스킬 인스턴스를 파괴해 즉시 중단 (콜라이더도 함께 사라짐). + private void CancelRunningSkill() + { + if (_runningSkillInstance != null) + Destroy(_runningSkillInstance.gameObject); + _runningSkillInstance = null; + _skillInProgress = false; + } + private void TickCooldowns() { if (_globalCooldownTimer > 0f) _globalCooldownTimer -= Time.deltaTime; for (int i = 0; i < _attackCooldownTimers.Length; i++) - { if (_attackCooldownTimers[i] > 0f) _attackCooldownTimers[i] -= Time.deltaTime; - } + + for (int i = 0; i < _skillCooldownTimers.Length; i++) + if (_skillCooldownTimers[i] > 0f) + _skillCooldownTimers[i] -= Time.deltaTime; } // ─── 타겟 / 인지 / 방향 ────────────────────────────────────────────── @@ -306,7 +402,7 @@ private void SetState(AIState state) _state = state; if (state == AIState.Idle) PlayAnim(_idleAnimationState); else if (state == AIState.Chase) PlayAnim(_runAnimationState); - // Attack 상태의 애니메이션은 BeginAttack에서 직접 재생. + // Attack 상태 애니메이션은 BeginAttack, 스킬 시전 애니메이션은 BeginSkill에서 재생. } // 같은 State 중복 재생 방지 (Play를 매 프레임 호출하면 애니가 처음으로 리셋됨). @@ -324,10 +420,16 @@ private void OnDrawGizmosSelected() Gizmos.color = Color.yellow; Gizmos.DrawWireSphere(transform.position, _detectRange); - if (_attacks == null) return; Gizmos.color = Color.red; - for (int i = 0; i < _attacks.Length; i++) - if (_attacks[i] != null) - Gizmos.DrawWireSphere(transform.position, _attacks[i].Range); + if (_attacks != null) + for (int i = 0; i < _attacks.Length; i++) + if (_attacks[i] != null) + Gizmos.DrawWireSphere(transform.position, _attacks[i].Range); + + Gizmos.color = new Color(1f, 0.4f, 1f); // 스킬 사거리 = 보라색 + if (_skillPrefabs != null) + for (int i = 0; i < _skillPrefabs.Length; i++) + if (_skillPrefabs[i] != null) + Gizmos.DrawWireSphere(transform.position, _skillPrefabs[i].Range); } } diff --git a/Assets/02_Scripts/Enemy/Skills.meta b/Assets/02_Scripts/Enemy/Skills.meta new file mode 100644 index 0000000..6bded5d --- /dev/null +++ b/Assets/02_Scripts/Enemy/Skills.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: daba49596fb3b8249b974bfbcb13e8a6 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/02_Scripts/Enemy/Skills/BossSkill.cs b/Assets/02_Scripts/Enemy/Skills/BossSkill.cs new file mode 100644 index 0000000..1453e2c --- /dev/null +++ b/Assets/02_Scripts/Enemy/Skills/BossSkill.cs @@ -0,0 +1,58 @@ +using System; +using System.Threading; +using UnityEngine; + +// ============================================================================ +// BossSkill +// ---------------------------------------------------------------------------- +// 보스 "특별 스킬"의 베이스 클래스. 스킬 하나 = 자기완결적 프리팹 하나. +// +// [스킬 프리팹] = BossSkill(상속) 컴포넌트 + 자체 Animator + HazardHitbox 자식들 +// +// 동작 흐름: +// - BossAI._skillPrefabs에 프리팹을 등록 (씬 인스턴스가 아닌 에셋 참조) +// - 시전 시 BossAI가 프리팹을 Instantiate → Begin() 호출 +// - 스킬은 시퀀스가 끝나면 스스로 Destroy +// - 취소(페이즈 전환·사망)는 BossAI가 인스턴스를 Destroy → 콜라이더도 함께 사라짐 +// +// MinPhase/Range/Cooldown은 BossAI가 프리팹에서 직접 읽어 사용 가능 여부를 판단. +// 쿨다운 "타이머"는 보스별 상태라 BossAI가 추적한다. +// +// 새 스킬 추가: BossSkill을 상속한 컴포넌트로 프리팹을 만들고 RunSkill만 구현. +// ============================================================================ +public abstract class BossSkill : MonoBehaviour +{ + [Header("BossSkill")] + [SerializeField] private int _minPhase; // 이 페이즈(0부터) 이상에서만 사용 가능 + [SerializeField] private float _range = 4f; // 타겟이 이 X거리 안일 때만 사용 + [SerializeField] private float _cooldown = 8f; // 재사용 대기 (타이머는 BossAI가 보스별로 추적) + [SerializeField] private string _casterAnimationState = "BossCast"; // 시전 중 보스가 재생할 애니메이션 + + public int MinPhase => _minPhase; + public float Range => _range; + public float Cooldown => _cooldown; + public string CasterAnimationState => _casterAnimationState; + + // BossAI가 인스턴스를 만든 직후 호출. 시퀀스를 끝까지 실행하고 스스로 파괴한다. + // destroyCancellationToken: BossAI가 이 인스턴스를 Destroy하면 시퀀스가 취소된다. + public async void Begin() + { + try + { + await RunSkill(destroyCancellationToken); + } + catch (OperationCanceledException) + { + return; // 외부에서 Destroy됨 → GameObject 이미 파괴 중이므로 그대로 종료 + } + catch (Exception e) + { + Debug.LogException(e, this); // 스킬 버그가 나도 아래 Destroy로 정리는 보장 + } + + Destroy(gameObject); + } + + // 서브클래스가 실제 스킬 시퀀스를 구현. token이 취소되면 finally에서 정리할 것. + protected abstract Awaitable RunSkill(CancellationToken token); +} diff --git a/Assets/02_Scripts/Enemy/Skills/BossSkill.cs.meta b/Assets/02_Scripts/Enemy/Skills/BossSkill.cs.meta new file mode 100644 index 0000000..ca9ce13 --- /dev/null +++ b/Assets/02_Scripts/Enemy/Skills/BossSkill.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: b8622c72961cf4d4a83c6a904e3cedea \ No newline at end of file diff --git a/Assets/02_Scripts/Enemy/Skills/HazardSkill.cs b/Assets/02_Scripts/Enemy/Skills/HazardSkill.cs new file mode 100644 index 0000000..0e4f367 --- /dev/null +++ b/Assets/02_Scripts/Enemy/Skills/HazardSkill.cs @@ -0,0 +1,84 @@ +using System.Threading; +using Unity.VisualScripting; +using UnityEngine; + +// ============================================================================ +// HazardSkill +// ---------------------------------------------------------------------------- +// 보스 특별 스킬의 한 종류 (BossSkill 상속). 스킬 프리팹의 루트에 부착. +// +// 시퀀스: 자체 애니메이션 재생 → 선딜 → 피해 판정 ON → 유지 → OFF → 후딜 → 자동 파괴 +// +// 콜라이더의 "움직임"은 이 프리팹의 자체 Animator/클립이 담당한다 (보스와 무관). +// 이 스크립트는 타이밍에 맞춰 HazardHitbox들의 피해 판정만 ON/OFF 한다. +// ============================================================================ +public class HazardSkill : BossSkill +{ + [Header("HazardSkill")] + [SerializeField] private Animator _animator; // 이 프리팹의 Animator (콜라이더 이동 담당) + [SerializeField] private string _downAnimationState = "HazardSkillDown"; + [SerializeField] private string _rightAnimationState = "HazardSkillRight"; + [SerializeField] private float _windup = 0.5f; // 애니 시작 ~ 피해 판정 ON + [SerializeField] private float _activeDuration = 1.5f; // 피해 판정 유지 시간 + [SerializeField] private float _recovery = 0.6f; // 판정 OFF 후 후딜 + [SerializeField] private Transform[] _downHazardFirePos; // 위에서 아래로 포격 + [SerializeField] private Transform[] _rightHazardFirePos; // 왼쪽에서 오른쪽으로 포격 + [SerializeField] private GameObject _hazardHitboxOrigin; + + private void Awake() + { + if (_animator == null) + _animator = GetComponentInChildren(); + } + + protected override async Awaitable RunSkill(CancellationToken token) + { + try + { + int dirChoice = Random.Range(0,2); //0,1 + + if(dirChoice == 0) // Down 포격 + { + if (_animator != null && !string.IsNullOrEmpty(_downAnimationState)) + _animator.Play(_downAnimationState); + } + else if(dirChoice == 1) //Right 포격 + { + if (_animator != null && !string.IsNullOrEmpty(_rightAnimationState)) + _animator.Play(_rightAnimationState); + } + + // 선딜 + await Awaitable.WaitForSecondsAsync(_windup, token); + + FireHazards(dirChoice); + + // 후딜 + await Awaitable.WaitForSecondsAsync(_recovery, token); + } + finally + { + + } + } + + private void FireHazards(int dirChoice) + { + Transform[] _hazardPoss = dirChoice switch { 0 => _downHazardFirePos, 1=>_rightHazardFirePos, _=> null}; + Vector2 attackDir = dirChoice switch {0 => Vector2.down,1=>Vector2.right,_=>Vector2.left}; + + if (_hazardPoss == null) return; + + for (int i = 0; i < _hazardPoss.Length; i++) + { + GameObject hitbox = Instantiate(_hazardHitboxOrigin,_hazardPoss[i].transform); + Rigidbody2D hitbox_rb = hitbox.GetComponent(); + if(hitbox_rb != null) + { + hitbox_rb.linearVelocity = attackDir*5.0f; + } + + Destroy(hitbox,5f); + } + } +} diff --git a/Assets/02_Scripts/Enemy/Skills/HazardSkill.cs.meta b/Assets/02_Scripts/Enemy/Skills/HazardSkill.cs.meta new file mode 100644 index 0000000..1e1d689 --- /dev/null +++ b/Assets/02_Scripts/Enemy/Skills/HazardSkill.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: e20ef4137f5c87f4ca4500445779b574 \ No newline at end of file diff --git a/Assets/03_Character/ColorMan/Prefabs/BossMan.prefab b/Assets/03_Character/ColorMan/Prefabs/BossMan.prefab index 870ba18..9b720ef 100644 --- a/Assets/03_Character/ColorMan/Prefabs/BossMan.prefab +++ b/Assets/03_Character/ColorMan/Prefabs/BossMan.prefab @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2ea607652ebfee29770c24295fecc63e9449fc614565b1d4cdeec3c4771e63ca -size 14640 +oid sha256:7e783b1bab5b52965c400c5a50d9cc4d63c14a93a15a660ce9be5ccb11fada4f +size 14680 diff --git a/Assets/08_Objects/BossSkills.meta b/Assets/08_Objects/BossSkills.meta new file mode 100644 index 0000000..c311b67 --- /dev/null +++ b/Assets/08_Objects/BossSkills.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7af62a125d51a0f4f98439dc6b587479 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/08_Objects/BossSkills/BossSkills.prefab b/Assets/08_Objects/BossSkills/BossSkills.prefab new file mode 100644 index 0000000..f5c9031 --- /dev/null +++ b/Assets/08_Objects/BossSkills/BossSkills.prefab @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ed305b2622b1631f246f9dc6cf90c586768af0278ba70c4407123206559f74da +size 30431 diff --git a/Assets/08_Objects/BossSkills/BossSkills.prefab.meta b/Assets/08_Objects/BossSkills/BossSkills.prefab.meta new file mode 100644 index 0000000..d32c367 --- /dev/null +++ b/Assets/08_Objects/BossSkills/BossSkills.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 2661c3fd528b72b46b2236fd0f53c4a6 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/08_Objects/BossSkills/HazardSkill.meta b/Assets/08_Objects/BossSkills/HazardSkill.meta new file mode 100644 index 0000000..40741f3 --- /dev/null +++ b/Assets/08_Objects/BossSkills/HazardSkill.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 74a8f5678f5d2734997ee7ad66d3ff6e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/08_Objects/BossSkills/HazardSkill/Animations.meta b/Assets/08_Objects/BossSkills/HazardSkill/Animations.meta new file mode 100644 index 0000000..2a246af --- /dev/null +++ b/Assets/08_Objects/BossSkills/HazardSkill/Animations.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fe26ef5211fb5ab4e8b55cec6420947d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/08_Objects/BossSkills/HazardSkill/Animations/HazardSkillAnimController.controller b/Assets/08_Objects/BossSkills/HazardSkill/Animations/HazardSkillAnimController.controller new file mode 100644 index 0000000..0f28517 --- /dev/null +++ b/Assets/08_Objects/BossSkills/HazardSkill/Animations/HazardSkillAnimController.controller @@ -0,0 +1,130 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1102 &-2043269259346761627 +AnimatorState: + serializedVersion: 6 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: New State + m_Speed: 1 + m_CycleOffset: 0 + m_Transitions: [] + m_StateMachineBehaviours: [] + m_Position: {x: 50, y: 50, z: 0} + m_IKOnFeet: 0 + m_WriteDefaultValues: 1 + m_Mirror: 0 + m_SpeedParameterActive: 0 + m_MirrorParameterActive: 0 + m_CycleOffsetParameterActive: 0 + m_TimeParameterActive: 0 + m_Motion: {fileID: 0} + m_Tag: + m_SpeedParameter: + m_MirrorParameter: + m_CycleOffsetParameter: + m_TimeParameter: +--- !u!1107 &-1748260263048877923 +AnimatorStateMachine: + serializedVersion: 6 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Base Layer + m_ChildStates: + - serializedVersion: 1 + m_State: {fileID: -2043269259346761627} + m_Position: {x: 60, y: 270, z: 0} + - serializedVersion: 1 + m_State: {fileID: 943741232334775837} + m_Position: {x: 60, y: 360, z: 0} + - serializedVersion: 1 + m_State: {fileID: 8862701103019185420} + m_Position: {x: 60, y: 440, z: 0} + m_ChildStateMachines: [] + m_AnyStateTransitions: [] + m_EntryTransitions: [] + m_StateMachineTransitions: {} + m_StateMachineBehaviours: [] + m_AnyStatePosition: {x: -170, y: 180, z: 0} + m_EntryPosition: {x: -170, y: 250, z: 0} + m_ExitPosition: {x: 80, y: 180, z: 0} + m_ParentStateMachinePosition: {x: 800, y: 20, z: 0} + m_DefaultState: {fileID: -2043269259346761627} +--- !u!91 &9100000 +AnimatorController: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: HazardSkillAnimController + serializedVersion: 5 + m_AnimatorParameters: [] + m_AnimatorLayers: + - serializedVersion: 5 + m_Name: Base Layer + m_StateMachine: {fileID: -1748260263048877923} + m_Mask: {fileID: 0} + m_Motions: [] + m_Behaviours: [] + m_BlendingMode: 0 + m_SyncedLayerIndex: -1 + m_DefaultWeight: 0 + m_IKPass: 0 + m_SyncedLayerAffectsTiming: 0 + m_Controller: {fileID: 9100000} +--- !u!1102 &943741232334775837 +AnimatorState: + serializedVersion: 6 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: HazardSkillDown + m_Speed: 1 + m_CycleOffset: 0 + m_Transitions: [] + m_StateMachineBehaviours: [] + m_Position: {x: 50, y: 50, z: 0} + m_IKOnFeet: 0 + m_WriteDefaultValues: 1 + m_Mirror: 0 + m_SpeedParameterActive: 0 + m_MirrorParameterActive: 0 + m_CycleOffsetParameterActive: 0 + m_TimeParameterActive: 0 + m_Motion: {fileID: 7400000, guid: 10b9535854db9a946a94fec5a86a31e2, type: 2} + m_Tag: + m_SpeedParameter: + m_MirrorParameter: + m_CycleOffsetParameter: + m_TimeParameter: +--- !u!1102 &8862701103019185420 +AnimatorState: + serializedVersion: 6 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: HazardSkillRight + m_Speed: 1 + m_CycleOffset: 0 + m_Transitions: [] + m_StateMachineBehaviours: [] + m_Position: {x: 50, y: 50, z: 0} + m_IKOnFeet: 0 + m_WriteDefaultValues: 1 + m_Mirror: 0 + m_SpeedParameterActive: 0 + m_MirrorParameterActive: 0 + m_CycleOffsetParameterActive: 0 + m_TimeParameterActive: 0 + m_Motion: {fileID: 7400000, guid: 23c9b965b45afa4419d45e40dac523a5, type: 2} + m_Tag: + m_SpeedParameter: + m_MirrorParameter: + m_CycleOffsetParameter: + m_TimeParameter: diff --git a/Assets/08_Objects/BossSkills/HazardSkill/Animations/HazardSkillAnimController.controller.meta b/Assets/08_Objects/BossSkills/HazardSkill/Animations/HazardSkillAnimController.controller.meta new file mode 100644 index 0000000..6bf1e8e --- /dev/null +++ b/Assets/08_Objects/BossSkills/HazardSkill/Animations/HazardSkillAnimController.controller.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f811209764ecbdc469cb01175e73343e +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 9100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/08_Objects/BossSkills/HazardSkill/Animations/HazardSkillDown.anim b/Assets/08_Objects/BossSkills/HazardSkill/Animations/HazardSkillDown.anim new file mode 100644 index 0000000..a899ab4 --- /dev/null +++ b/Assets/08_Objects/BossSkills/HazardSkill/Animations/HazardSkillDown.anim @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:94d19c19e8222fdb52d2048d5f80bd7aaa19c0762f6dbc3686368b2cfc1747b1 +size 1303 diff --git a/Assets/08_Objects/BossSkills/HazardSkill/Animations/HazardSkillDown.anim.meta b/Assets/08_Objects/BossSkills/HazardSkill/Animations/HazardSkillDown.anim.meta new file mode 100644 index 0000000..3d91041 --- /dev/null +++ b/Assets/08_Objects/BossSkills/HazardSkill/Animations/HazardSkillDown.anim.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 10b9535854db9a946a94fec5a86a31e2 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 7400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/08_Objects/BossSkills/HazardSkill/Animations/HazardSkillRight.anim b/Assets/08_Objects/BossSkills/HazardSkill/Animations/HazardSkillRight.anim new file mode 100644 index 0000000..317f363 --- /dev/null +++ b/Assets/08_Objects/BossSkills/HazardSkill/Animations/HazardSkillRight.anim @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:442552e8b305ae93bd8ab07d2000a650889e458d6e57ce7f4e43fc75741b7534 +size 1304 diff --git a/Assets/08_Objects/BossSkills/HazardSkill/Animations/HazardSkillRight.anim.meta b/Assets/08_Objects/BossSkills/HazardSkill/Animations/HazardSkillRight.anim.meta new file mode 100644 index 0000000..12205ae --- /dev/null +++ b/Assets/08_Objects/BossSkills/HazardSkill/Animations/HazardSkillRight.anim.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 23c9b965b45afa4419d45e40dac523a5 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 7400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/08_Objects/BossSkills/HazardSkill/HazardHitBox.prefab b/Assets/08_Objects/BossSkills/HazardSkill/HazardHitBox.prefab new file mode 100644 index 0000000..32f8efb --- /dev/null +++ b/Assets/08_Objects/BossSkills/HazardSkill/HazardHitBox.prefab @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:80da75f70158f78e9741f89ce6406ae1153fb46fe65463716d4d43f5e8a86f1c +size 5249 diff --git a/Assets/08_Objects/BossSkills/HazardSkill/HazardHitBox.prefab.meta b/Assets/08_Objects/BossSkills/HazardSkill/HazardHitBox.prefab.meta new file mode 100644 index 0000000..6478f77 --- /dev/null +++ b/Assets/08_Objects/BossSkills/HazardSkill/HazardHitBox.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 80c778713f233a2469e08d15655cbb7f +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: