- 애니메이션 이벤트 기반 스킬 시스템 추가 - SkillData: 스킬 데이터 (클립, 쿨타임, 효과 목록) - SkillController: 스킬 실행 및 애니메이션 제어 - AnimatorOverrideController로 단일 State에서 다양한 스킬 재생 - 스킬 효과 시스템 - DamageEffect, HealEffect, BuffEffect - KnockbackEffect, SoundEffect, SpawnEffect - 범위 공격 및 팀 구분 지원 - Team 컴포넌트로 아군/적 구분 - 스킬 중 이동 제한 - IsPlayingAnimation으로 애니메이션 종료까지 이동 불가 - OnSkillEnd 호출 시 다음 스킬 시전 가능 - 입력 시스템에 스킬 슬롯 6개 추가 - 애니메이션 에셋 추가 및 정리 - AnimationSwordCombat 패키지 추가 (검 공격 애니메이션) - PlayerAnimationController에 Skill 상태 추가 - External_Used 폴더 구조 정리 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
135 lines
3.9 KiB
C#
135 lines
3.9 KiB
C#
using UnityEngine;
|
|
using Colosseum;
|
|
|
|
namespace Colosseum.Skills
|
|
{
|
|
/// <summary>
|
|
/// 스킬 효과의 기본 클래스.
|
|
/// 모든 효과는 이 클래스를 상속받아 구현합니다.
|
|
/// 효과 발동 타이밍은 애니메이션 이벤트(OnEffect)로 제어합니다.
|
|
/// </summary>
|
|
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;
|
|
|
|
/// <summary>
|
|
/// 스킬 시전 시 호출
|
|
/// </summary>
|
|
public void ExecuteOnCast(GameObject caster)
|
|
{
|
|
switch (targetType)
|
|
{
|
|
case TargetType.Self:
|
|
ApplyEffect(caster, caster);
|
|
break;
|
|
|
|
case TargetType.Area:
|
|
ExecuteArea(caster);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 투사체 충돌 시 호출
|
|
/// </summary>
|
|
public void ExecuteOnHit(GameObject caster, GameObject hitTarget)
|
|
{
|
|
if (IsValidTarget(caster, hitTarget))
|
|
{
|
|
ApplyEffect(caster, hitTarget);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 실제 효과 적용 (상속받은 클래스에서 구현)
|
|
/// </summary>
|
|
protected abstract void ApplyEffect(GameObject caster, GameObject target);
|
|
|
|
/// <summary>
|
|
/// 충돌한 대상이 유효한 타겟인지 확인
|
|
/// </summary>
|
|
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
|
|
}
|
|
}
|