using UnityEngine; using Colosseum; namespace Colosseum.Skills { /// /// 스킬 효과의 기본 클래스. /// 모든 효과는 이 클래스를 상속받아 구현합니다. /// 효과 발동 타이밍은 애니메이션 이벤트(OnEffect)로 제어합니다. /// public abstract class SkillEffect : ScriptableObject { [Header("대상 설정")] [Tooltip("Self: 시전자, Area: 범위 내 대상")] [SerializeField] protected TargetType targetType = TargetType.Self; [Header("Area Settings (TargetType이 Area일 때)")] [Tooltip("범위 내에서 공격할 대상 필터")] [SerializeField] protected TargetTeam targetTeam = TargetTeam.Enemy; [SerializeField] protected AreaCenterType areaCenter = AreaCenterType.Caster; [Min(0.1f)] [SerializeField] protected float areaRadius = 3f; [SerializeField] protected LayerMask targetLayers; // Properties public TargetType TargetType => targetType; public TargetTeam TargetTeam => targetTeam; /// /// 스킬 시전 시 호출 /// public void ExecuteOnCast(GameObject caster) { switch (targetType) { case TargetType.Self: ApplyEffect(caster, caster); break; case TargetType.Area: ExecuteArea(caster); break; } } /// /// 투사체 충돌 시 호출 /// public void ExecuteOnHit(GameObject caster, GameObject hitTarget) { if (IsValidTarget(caster, hitTarget)) { ApplyEffect(caster, hitTarget); } } /// /// 실제 효과 적용 (상속받은 클래스에서 구현) /// protected abstract void ApplyEffect(GameObject caster, GameObject target); /// /// 충돌한 대상이 유효한 타겟인지 확인 /// public bool IsValidTarget(GameObject caster, GameObject target) { if (target == null) return false; // 레이어 체크 if (targetLayers.value != 0 && (targetLayers.value & (1 << target.layer)) == 0) return false; // 팀 체크 return IsCorrectTeam(caster, target); } private bool IsCorrectTeam(GameObject caster, GameObject target) { bool isSameTeam = Team.IsSameTeam(caster, target); return targetTeam switch { TargetTeam.Enemy => !isSameTeam, TargetTeam.Ally => isSameTeam, TargetTeam.All => true, _ => true }; } private void ExecuteArea(GameObject caster) { Vector3 center = GetAreaCenter(caster); Collider[] hits = Physics.OverlapSphere(center, areaRadius, targetLayers); foreach (var hit in hits) { if (hit.gameObject == caster) continue; if (!IsCorrectTeam(caster, hit.gameObject)) continue; ApplyEffect(caster, hit.gameObject); } } private Vector3 GetAreaCenter(GameObject caster) { if (caster == null) return Vector3.zero; return areaCenter switch { AreaCenterType.Caster => caster.transform.position, AreaCenterType.CasterForward => caster.transform.position + caster.transform.forward * areaRadius, _ => caster.transform.position }; } } public enum TargetType { Self, Area } public enum TargetTeam { Enemy, Ally, All } public enum AreaCenterType { Caster, CasterForward } }