using UnityEngine;
namespace Colosseum.Skills.Effects
{
///
/// VFX 생성 효과.
/// 지정된 위치에 파티클 프리팹을 생성하고, 생명주기 및 재생 옵션을 제어합니다.
///
[CreateAssetMenu(fileName = "VfxEffect", menuName = "Colosseum/Skills/Effects/VFX")]
public class VfxEffect : SkillEffect
{
[Header("VFX 설정")]
[Tooltip("생성할 VFX 프리팹")]
[SerializeField] private GameObject vfxPrefab;
[Header("위치 설정")]
[Tooltip("VFX 생성 위치: Caster=시전자, CasterForward=시전자 정면, Target=대상 위치, GroundPoint=지면 타겟 위치")]
[SerializeField] private VfxSpawnLocation spawnLocation = VfxSpawnLocation.Caster;
[Tooltip("위치 오프셋")]
[SerializeField] private Vector3 offset = Vector3.zero;
[Header("생명주기")]
[Tooltip("VFX 자동 파괴 시간 (0이면 파괴하지 않음)")]
[Min(0f)] [SerializeField] private float lifetime = 2f;
[Tooltip("시전자에게 부모 설정 여부")]
[SerializeField] private bool parentToCaster = false;
[Tooltip("VFX 크기 배율")]
[Min(0.01f)] [SerializeField] private float scaleMultiplier = 1f;
[Header("파티클 설정")]
[Tooltip("ParticleSystem 자동 재생 여부 (false면 스폰만 하고 외부에서 제어)")]
[SerializeField] private bool autoPlay = true;
[Tooltip("ParticleSystem 루핑 여부")]
[SerializeField] private bool loop = false;
///
/// VFX 효과는 순수 시각 효과이므로 모든 클라이언트에서 로컬 실행됩니다.
///
public override bool IsVisualOnly => true;
private Vector3? groundPosition;
///
/// 지면 타겟 위치를 캡처하여 ApplyEffect에서 사용할 수 있도록 합니다.
///
public override void ExecuteOnCast(GameObject caster, GameObject targetOverride = null, Vector3? groundPosition = null)
{
this.groundPosition = groundPosition;
base.ExecuteOnCast(caster, targetOverride, groundPosition);
this.groundPosition = null;
}
///
/// VFX 프리팹을 생성하고 파티클을 재생합니다.
///
protected override void ApplyEffect(GameObject caster, GameObject target)
{
if (vfxPrefab == null || caster == null) return;
try
{
Vector3 spawnPos = GetSpawnPosition(caster, target);
Quaternion spawnRot = caster.transform.rotation;
Transform parent = parentToCaster ? caster.transform : null;
// 비제네릭 Instantiate로 캐스팅 예외 방지
GameObject instance = (GameObject)Object.Instantiate(vfxPrefab, spawnPos + offset, spawnRot, parent);
instance.transform.localScale *= scaleMultiplier;
if (parentToCaster)
instance.transform.localPosition = offset;
if (autoPlay)
{
ParticleSystem[] particleSystems = instance.GetComponentsInChildren(true);
foreach (var ps in particleSystems)
{
var main = ps.main;
main.loop = loop;
if (loop)
main.stopAction = ParticleSystemStopAction.None;
ps.Clear(true);
ps.Play(true);
}
}
if (lifetime > 0f)
Object.Destroy(instance, lifetime);
}
catch (System.Exception e)
{
Debug.LogError($"[VfxEffect] VFX 생성 실패: {e.Message}\n{e.StackTrace}", this);
}
}
///
/// spawnLocation에 따른 VFX 생성 위치를 계산합니다.
///
private Vector3 GetSpawnPosition(GameObject caster, GameObject target)
{
return spawnLocation switch
{
VfxSpawnLocation.Caster => caster.transform.position,
VfxSpawnLocation.CasterForward => caster.transform.position + caster.transform.forward * 2f,
VfxSpawnLocation.Target => target != null ? target.transform.position : caster.transform.position,
VfxSpawnLocation.GroundPoint => groundPosition ?? caster.transform.position,
_ => caster.transform.position
};
}
}
public enum VfxSpawnLocation
{
Caster,
CasterForward,
Target,
GroundPoint
}
}