feat: VFX 인프라 구축 및 Ground Target 시스템 구현

- VfxEffect 스킬 이펙트 클래스 추가 (일회성 VFX 스폰, 위치/스케일/파티클 제어)
- SkillEffect.IsVisualOnly 프로퍼티 추가로 서버 가드 없이 모든 클라이언트에서 VFX 로컬 실행
- SkillProjectile 트레일 VFX 지원 (OnNetworkSpawn에서 양쪽 생성, despawn 시 월드 분리)
- SkillProjectile HitEffectClientRpc 추가로 충돌 이펙트 클라이언트 동기화
- Ground Target 시스템: 타겟팅 모드 상태머신, 인디케이터, 지면 위치 RPC 전달
- 마법 오름 Ground Target 스킬 에셋 및 VfxEffect 에셋 추가
- 마법 오름 애니메이션 클립 추가
- Ground layer (Layer 7) 추가
- ProjectileBasic에 trailPrefab/hitEffect 필드 추가
- Prefabs/VFX/ 폴더 생성
This commit is contained in:
2026-04-02 22:25:19 +09:00
parent 57ab230c61
commit 188b134062
34 changed files with 42905 additions and 102 deletions

View File

@@ -45,14 +45,21 @@ namespace Colosseum.Skills
public float FanRadius => fanRadius;
public float FanHalfAngle => fanHalfAngle;
public bool IncludeCasterInArea => includeCasterInArea;
public AreaCenterType AreaCenter => areaCenter;
/// <summary>
/// 이 효과가 순수 시각 효과인지 확인합니다.
/// true이면 서버 가드를 무시하고 모든 클라이언트에서 로컬 실행됩니다.
/// </summary>
public virtual bool IsVisualOnly => false;
/// <summary>
/// 스킬 시전 시 호출
/// </summary>
public void ExecuteOnCast(GameObject caster, GameObject targetOverride = null)
public virtual void ExecuteOnCast(GameObject caster, GameObject targetOverride = null, Vector3? groundPosition = null)
{
List<GameObject> targets = new List<GameObject>();
CollectTargets(caster, targets, targetOverride);
CollectTargets(caster, targets, targetOverride, groundPosition);
for (int i = 0; i < targets.Count; i++)
{
@@ -64,7 +71,7 @@ namespace Colosseum.Skills
/// 현재 효과가 영향을 줄 대상 목록을 수집합니다.
/// 젬의 적중 이상상태 적용 등에서 동일한 타겟 해석을 재사용하기 위한 경로입니다.
/// </summary>
public void CollectTargets(GameObject caster, List<GameObject> destination, GameObject targetOverride = null)
public void CollectTargets(GameObject caster, List<GameObject> destination, GameObject targetOverride = null, Vector3? groundPosition = null)
{
if (caster == null || destination == null)
return;
@@ -76,7 +83,7 @@ namespace Colosseum.Skills
break;
case TargetType.Area:
CollectAreaTargets(caster, destination);
CollectAreaTargets(caster, destination, groundPosition);
break;
case TargetType.SingleAlly:
@@ -135,9 +142,9 @@ namespace Colosseum.Skills
};
}
private void CollectAreaTargets(GameObject caster, List<GameObject> destination)
private void CollectAreaTargets(GameObject caster, List<GameObject> destination, Vector3? groundPosition = null)
{
Vector3 center = GetAreaCenter(caster);
Vector3 center = GetAreaCenter(caster, groundPosition);
Collider[] hits = Physics.OverlapSphere(center, Mathf.Max(areaRadius, fanRadius), targetLayers);
foreach (var hit in hits)
{
@@ -189,7 +196,7 @@ namespace Colosseum.Skills
return angle <= fanHalfAngle;
}
private Vector3 GetAreaCenter(GameObject caster)
private Vector3 GetAreaCenter(GameObject caster, Vector3? groundPosition = null)
{
if (caster == null) return Vector3.zero;
@@ -197,6 +204,7 @@ namespace Colosseum.Skills
{
AreaCenterType.Caster => caster.transform.position,
AreaCenterType.CasterForward => caster.transform.position + caster.transform.forward * areaRadius,
AreaCenterType.GroundPoint => groundPosition ?? caster.transform.position,
_ => caster.transform.position
};
}
@@ -205,7 +213,7 @@ namespace Colosseum.Skills
/// <summary>
/// 공격 범위 시각화 (런타임 디버그용)
/// </summary>
public void DrawDebugRange(GameObject caster, float duration = 1f)
public void DrawDebugRange(GameObject caster, float duration = 1f, Vector3? groundPosition = null)
{
if (targetType != TargetType.Area) return;
@@ -214,12 +222,15 @@ namespace Colosseum.Skills
if (areaShape == AreaShapeType.Sphere)
{
Vector3 center = GetAreaCenter(caster);
Vector3 center = GetAreaCenter(caster, groundPosition);
DebugDrawSphere(center, areaRadius, Color.red, duration);
}
else if (areaShape == AreaShapeType.Fan)
{
DebugDrawFan(casterPos, forward, fanOriginDistance, fanRadius, fanHalfAngle, Color.red, duration);
Vector3 center = GetAreaCenter(caster, groundPosition);
DebugDrawFan(center, groundPosition.HasValue && areaCenter == AreaCenterType.GroundPoint
? (center - casterPos).normalized
: forward, fanOriginDistance, fanRadius, fanHalfAngle, Color.red, duration);
}
}
@@ -296,7 +307,8 @@ namespace Colosseum.Skills
public enum AreaCenterType
{
Caster,
CasterForward
CasterForward,
GroundPoint // 지면 지점 타겟팅 (Ground Target)
}
public enum AreaShapeType