using UnityEngine; using Unity.Netcode; namespace Colosseum.Skills.Effects { /// /// 프리팹 스폰 효과 (투사체, 파티클 등) /// [CreateAssetMenu(fileName = "SpawnEffect", menuName = "Colosseum/Skills/Effects/Spawn")] public class SpawnEffect : SkillEffect { [Header("Spawn Settings")] [SerializeField] private GameObject prefab; [SerializeField] private SpawnLocation spawnLocation = SpawnLocation.Caster; [SerializeField] private Vector3 spawnOffset = Vector3.zero; [SerializeField] private bool parentToCaster = false; [Min(0f)] [SerializeField] private float autoDestroyTime = 3f; [Header("Hit Settings")] [Tooltip("투사체가 대상에 명중했을 때 적용할 효과. 미설정 시 명중 효과 없음.")] [SerializeField] private SkillEffect hitEffect; protected override void ApplyEffect(GameObject caster, GameObject target) { if (prefab == null || caster == null) return; Vector3 spawnPos = GetSpawnPosition(caster, target) + spawnOffset; Quaternion spawnRot = GetSpawnRotation(caster, target); var networkObject = prefab.GetComponent(); if (networkObject != null) { // 네트워크 오브젝트: 서버에서 스폰 후 전파 // (OnEffect 가드에 의해 이미 서버에서만 호출됨) GameObject instance = Object.Instantiate(prefab, spawnPos, spawnRot); var spawnedNet = instance.GetComponent(); spawnedNet.Spawn(destroyWithScene: true); var projectile = instance.GetComponent(); if (projectile != null) projectile.Initialize(caster, hitEffect); } else { // 로컬 오브젝트 (파티클 등): 기존 방식 유지 Transform parent = parentToCaster ? caster.transform : null; GameObject instance = Object.Instantiate(prefab, spawnPos, spawnRot, parent); var projectile = instance.GetComponent(); if (projectile != null) projectile.Initialize(caster, hitEffect); if (autoDestroyTime > 0f) Object.Destroy(instance, autoDestroyTime); } } private Vector3 GetSpawnPosition(GameObject caster, GameObject target) { return spawnLocation switch { SpawnLocation.Caster => caster.transform.position, SpawnLocation.CasterForward => caster.transform.position + caster.transform.forward * 2f, SpawnLocation.Target => target != null ? target.transform.position : caster.transform.position, _ => caster.transform.position }; } private Quaternion GetSpawnRotation(GameObject caster, GameObject target) { if (spawnLocation == SpawnLocation.Target && target != null) { return Quaternion.LookRotation(target.transform.position - caster.transform.position); } return caster.transform.rotation; } } public enum SpawnLocation { Caster, CasterForward, Target } }