- 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/ 폴더 생성
123 lines
4.7 KiB
C#
123 lines
4.7 KiB
C#
using UnityEngine;
|
|
|
|
namespace Colosseum.Skills.Effects
|
|
{
|
|
/// <summary>
|
|
/// VFX 생성 효과.
|
|
/// 지정된 위치에 파티클 프리팹을 생성하고, 생명주기 및 재생 옵션을 제어합니다.
|
|
/// </summary>
|
|
[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;
|
|
|
|
/// <summary>
|
|
/// VFX 효과는 순수 시각 효과이므로 모든 클라이언트에서 로컬 실행됩니다.
|
|
/// </summary>
|
|
public override bool IsVisualOnly => true;
|
|
|
|
private Vector3? groundPosition;
|
|
|
|
/// <summary>
|
|
/// 지면 타겟 위치를 캡처하여 ApplyEffect에서 사용할 수 있도록 합니다.
|
|
/// </summary>
|
|
public override void ExecuteOnCast(GameObject caster, GameObject targetOverride = null, Vector3? groundPosition = null)
|
|
{
|
|
this.groundPosition = groundPosition;
|
|
base.ExecuteOnCast(caster, targetOverride, groundPosition);
|
|
this.groundPosition = null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// VFX 프리팹을 생성하고 파티클을 재생합니다.
|
|
/// </summary>
|
|
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<ParticleSystem>(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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// spawnLocation에 따른 VFX 생성 위치를 계산합니다.
|
|
/// </summary>
|
|
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
|
|
}
|
|
}
|