EerieVillage/Assets/Scripts/Skills/Effectors/Projectile.cs

88 lines
3.2 KiB
C#
Raw Normal View History

using System.Collections.Generic;
using UnityEngine;
using Platformer.Mechanics;
namespace EerieVillage.Skills.Effectors
{
/// <summary>
/// 투사체 기본 컴포넌트. Line 궤적 직선 이동·단일 적 타격 후 소멸.
/// BT12-Dev Phase 2-B §4-2.
/// 파생: <see cref="HomingProjectile"/> (A15 추적 화염구).
/// </summary>
public class Projectile : MonoBehaviour
{
protected ActiveSkillData _data;
protected ActiveSkillRuntime _runtime;
protected PlayerSkillInventory _inventory;
protected Vector2 _direction;
protected float _speed = 12f;
protected float _lifetime = 3f;
// 동일 투사체로 동일 Collider 중복 타격 방지
protected readonly HashSet<Collider2D> _hitTargets = new HashSet<Collider2D>();
/// <summary>
/// ProjectileSpawner.Trigger 에서 Instantiate 직후 호출.
/// </summary>
public virtual void Initialize(ActiveSkillRuntime runtime, PlayerSkillInventory inventory, Vector2 direction)
{
_runtime = runtime;
_data = runtime.ActiveData;
_inventory = inventory;
_direction = direction.normalized;
_hitTargets.Clear();
// Phase 2-B: 풀링 미도입 — Invoke 기반 자동 소멸
Invoke(nameof(SelfDestruct), _lifetime);
}
protected virtual void Update()
{
transform.position += (Vector3)(_direction * _speed * Time.deltaTime);
}
protected virtual void OnTriggerEnter2D(Collider2D other)
{
if (_hitTargets.Contains(other)) return;
// PD 지시 2026-05-09 후속 방어 — 자기(Player) hit·자기 자신·hit 방어.
if (other.GetComponent<PlayerController>() != null) return;
feat(BT12-Dev Phase 2-D): BT12-MVP-A 통합 정정 (placeholder → 정식 ActiveSkillData) + Phase 2-B .meta 보충 C49 Phase 2-D — Sonnet 위임 (코드 Write·검증만·git 본 PM 처리 정합·feedback_pm_sonnet_subagent_unauthorized_push 정합). 수정 6 파일: - LevelUpManager.cs (Phase 2-D 정정·_pool 제거·SkillRuntimeFactory.RandomDraw3·HandleCardConfirmed(ActiveSkillData)·PlayerSkillInventory.AddSkillByCardId) - SkillSelectionUI.cs (Show(List<ActiveSkillData>)·_selected·BindSlot·OnCardSelected ActiveSkillData 전환) - SkillCardSlot.cs (Bind(ActiveSkillData)·DisplayName/Description/Icon PascalCase·rarity 배너 갈색 고정) - PlayerController.cs (PlayerSkillInventory 자동 부착·line 100) - Projectile.cs (Layer Enemy 미등재 fallback — EnemyController 컴포넌트 검사·proxy) - SkillRuntimeFactory.cs (RandomDraw3 메서드·Active 카테고리 무작위 3장) 신규 9 .meta (Phase 2-B Sonnet 자율 push 영역 영역 영역 영역 X·Unity Editor Refresh 후 자동 생성·본 commit 보충): - Skills.meta + Effectors.meta + 7 Effectors/*.cs.meta Layer Enemy 영역 = proxy 개선 신호 (C2-2): - 현 시점 = Projectile.OnTriggerEnter2D 영역 EnemyController 컴포넌트 fallback (proxy) - 근본 해결안 = Layer "Enemy" 정식 등재 (별도 PD 안건·후속) 기능: - 적 처치 → EXP +1 → 즉시 레벨업 → 카드 3장 노출 → 선택 → PlayerSkillInventory 등록 → ActiveSkillRuntime Tick → 1.5s 영역 자동 발사 + 부가 효과 (DoT·Stun·Slow·DebuffStack) 기존 영역 변경 X (BT5-Dev·BT7-Dev·Phase 2-A·2-B·2-C·BT12-MVP-A asset 5장·Scene·SkillCardPlaceholder·SkillCardPlaceholderPool·deprecate 차후) Compile error 0건 (read_console·도메인 리로드 정합) C49 — Phase 2-D Sonnet 위임 + Phase 3 본 PM 직접 (단순 반복 카탈로그 v1) C50 — ~95K (PD 사전 승인 70~95K 영역 상한 정합) C19-2 — Sonnet 자율 git X·본 PM 직접 commit·push (feedback 정합) pm-auditor 사전 감사 = Pass + Minor 1 (Layer fallback proxy 명시·본 commit + 대화로그 영역 정정 적용) 후속: - Phase 2-A·2-B·2-C·2-D 영역 PD Play 검증 (자동 발동·레벨업·카드 선택·등록·Tick) - Layer "Enemy" 정식 등재 (별도 PD 안건·근본 해결안) - Phase 2-E EditMode 테스트 - 다른 카테고리 (B·C·D·E·F) 영역 - BT12-MVP-A asset 5장 deprecate (차기) - Screenshots·_Recovery .gitignore (별도)
2026-05-09 11:57:28 +00:00
// Enemy 레이어 한정.
// Phase 2-D fallback (2026-05-09): TagManager에 "Enemy" 레이어 미등재 시 LayerMask.NameToLayer 반환값 = -1.
// 레이어 매칭 실패 시 EnemyController 컴포넌트 존재 여부로 대체 판정.
int enemyLayer = LayerMask.NameToLayer("Enemy");
bool isEnemy = (enemyLayer != -1 && other.gameObject.layer == enemyLayer)
|| other.GetComponent<EnemyController>() != null;
if (!isEnemy) return;
var health = other.GetComponent<Health>();
if (health == null || !health.IsAlive) return;
_hitTargets.Add(other);
// 유효 대미지 산출 (balance/01 v0.2 §3 공식 — ActiveSkillRuntime.CalculateEffectiveDamage())
int damage = _runtime.CalculateEffectiveDamage();
// 피해 적용
health.Decrement(damage);
// 부가 효과 (DoT·Stun·Slow·DebuffStack) — StatusApplier 위임
var enemy = other.GetComponent<EnemyController>();
if (enemy != null)
{
StatusApplier.Apply(_data, enemy);
}
// 단일 적 타격 후 소멸 (관통 미지원 — Phase 2 범위 내)
SelfDestruct();
}
protected void SelfDestruct()
{
CancelInvoke(nameof(SelfDestruct));
Destroy(gameObject);
}
}
}