using System.Collections; using UnityEngine; [DisallowMultipleComponent] public class FootprintEffect : MonoBehaviour { [Header("Timing")] [SerializeField] private float appearTime = 0.25f; [SerializeField] private float lifeTime = 3f; [SerializeField] private float fadeTime = 0.8f; [Header("Scale Effect")] [SerializeField] private bool useScaleEffect = true; [SerializeField] private float startScaleMultiplier = 0.75f; [SerializeField] private float popScaleMultiplier = 1.08f; [SerializeField] private float endScaleMultiplier = 0.85f; [Header("Unlit Brightness Effect")] [Tooltip("URP Unlit 머티리얼에서 Emission 대신 색 밝기를 조절합니다.")] [SerializeField] private bool useBrightnessEffect = true; [SerializeField] private float appearBrightness = 1.4f; [SerializeField] private float normalBrightness = 1.0f; [SerializeField] private float fadeBrightness = 0.5f; [Header("Destroy")] [SerializeField] private bool destroyAfterFade = true; [Tooltip("renderer.materials로 생성된 머티리얼 인스턴스를 OnDestroy에서 정리합니다.")] [SerializeField] private bool cleanupMaterialInstances = true; private Renderer[] renderers; private Material[] materials; private Color[] originalColors; private Vector3 originalScale; private Coroutine effectRoutine; private void Awake() { originalScale = transform.localScale; CacheMaterials(); ApplyVisual(0f, startScaleMultiplier, fadeBrightness); } private void OnEnable() { if (effectRoutine != null) StopCoroutine(effectRoutine); effectRoutine = StartCoroutine(EffectRoutine()); } private void OnDisable() { if (effectRoutine != null) { StopCoroutine(effectRoutine); effectRoutine = null; } } private void OnDestroy() { if (!cleanupMaterialInstances || materials == null) return; for (int i = 0; i < materials.Length; i++) { if (materials[i] != null) Destroy(materials[i]); } } private void CacheMaterials() { renderers = GetComponentsInChildren(true); int materialCount = 0; for (int i = 0; i < renderers.Length; i++) materialCount += renderers[i].materials.Length; materials = new Material[materialCount]; originalColors = new Color[materialCount]; int index = 0; for (int i = 0; i < renderers.Length; i++) { Material[] rendererMaterials = renderers[i].materials; for (int j = 0; j < rendererMaterials.Length; j++) { materials[index] = rendererMaterials[j]; originalColors[index] = GetMaterialColor(materials[index]); index++; } } } private IEnumerator EffectRoutine() { yield return AppearRoutine(); if (lifeTime > 0f) yield return new WaitForSeconds(lifeTime); yield return FadeRoutine(); effectRoutine = null; if (destroyAfterFade) Destroy(gameObject); } private IEnumerator AppearRoutine() { if (appearTime <= 0f) { ApplyVisual(1f, 1f, normalBrightness); yield break; } float timer = 0f; while (timer < appearTime) { timer += Time.deltaTime; float t = Mathf.Clamp01(timer / appearTime); float alpha = Mathf.Lerp(0f, 1f, t); float brightness = useBrightnessEffect ? Mathf.Lerp(fadeBrightness, appearBrightness, t) : 1f; float scaleMultiplier; if (t < 0.75f) { float popT = t / 0.75f; scaleMultiplier = Mathf.Lerp(startScaleMultiplier, popScaleMultiplier, popT); } else { float settleT = (t - 0.75f) / 0.25f; scaleMultiplier = Mathf.Lerp(popScaleMultiplier, 1f, settleT); } ApplyVisual(alpha, scaleMultiplier, brightness); yield return null; } ApplyVisual(1f, 1f, normalBrightness); } private IEnumerator FadeRoutine() { if (fadeTime <= 0f) { ApplyVisual(0f, endScaleMultiplier, fadeBrightness); yield break; } float timer = 0f; while (timer < fadeTime) { timer += Time.deltaTime; float t = Mathf.Clamp01(timer / fadeTime); float alpha = Mathf.Lerp(1f, 0f, t); float brightness = useBrightnessEffect ? Mathf.Lerp(normalBrightness, fadeBrightness, t) : 1f; float scaleMultiplier = Mathf.Lerp(1f, endScaleMultiplier, t); ApplyVisual(alpha, scaleMultiplier, brightness); yield return null; } ApplyVisual(0f, endScaleMultiplier, fadeBrightness); } private void ApplyVisual(float alpha, float scaleMultiplier, float brightness) { if (useScaleEffect) transform.localScale = originalScale * scaleMultiplier; if (materials == null) return; for (int i = 0; i < materials.Length; i++) { if (materials[i] == null) continue; Color color = originalColors[i]; color.r *= brightness; color.g *= brightness; color.b *= brightness; color.a = alpha; SetMaterialColor(materials[i], color); } } private Color GetMaterialColor(Material material) { if (material == null) return Color.white; if (material.HasProperty("_BaseColor")) return material.GetColor("_BaseColor"); if (material.HasProperty("_Color")) return material.GetColor("_Color"); return Color.white; } private void SetMaterialColor(Material material, Color color) { if (material == null) return; if (material.HasProperty("_BaseColor")) material.SetColor("_BaseColor", color); else if (material.HasProperty("_Color")) material.SetColor("_Color", color); } #if UNITY_EDITOR private void OnValidate() { appearTime = Mathf.Max(0f, appearTime); lifeTime = Mathf.Max(0f, lifeTime); fadeTime = Mathf.Max(0f, fadeTime); startScaleMultiplier = Mathf.Max(0.01f, startScaleMultiplier); popScaleMultiplier = Mathf.Max(0.01f, popScaleMultiplier); endScaleMultiplier = Mathf.Max(0.01f, endScaleMultiplier); appearBrightness = Mathf.Max(0f, appearBrightness); normalBrightness = Mathf.Max(0f, normalBrightness); fadeBrightness = Mathf.Max(0f, fadeBrightness); } #endif }