diff --git a/Assets/Scripts/Skills/Effectors/SpiritFireSpawner.cs b/Assets/Scripts/Skills/Effectors/SpiritFireSpawner.cs index e93c1fd..392faef 100644 --- a/Assets/Scripts/Skills/Effectors/SpiritFireSpawner.cs +++ b/Assets/Scripts/Skills/Effectors/SpiritFireSpawner.cs @@ -48,10 +48,19 @@ namespace EerieVillage.Skills.Effectors /// /// 정령불 인스턴스 — Player 자식 부착·duration 동안 OverlapCircle 영역 적 투사체 SelfDestruct·근접 적 매 초 5 피해. + /// PD 지시 2026-05-13 — FX_Rotating shield Animator frame 제어 (60fps·intro 1~88·loop 89~105·outro 남은 frame). /// public class SpiritFireInstance : MonoBehaviour { + // PD 지시 2026-05-13 — FX_Rotating shield.anim 정합 (m_SampleRate 60·m_StopTime 2.8166666 → 169 frame·2.8167s) + const float FPS = 60f; + const int INTRO_END_FRAME = 88; + const int LOOP_END_FRAME = 105; + const float CLIP_LENGTH = 2.8166666f; + static readonly int STATE_HASH = Animator.StringToHash("Base Layer.FX_Rotating shield"); + Transform _player; + Animator _animator; float _spawnTime; float _duration; float _radius; @@ -65,10 +74,48 @@ namespace EerieVillage.Skills.Effectors _radius = radius; _damage = damage; _spawnTime = Time.unscaledTime; + + _animator = GetComponent(); + if (_animator == null) _animator = GetComponentInChildren(); + if (_animator != null) + { + _animator.updateMode = AnimatorUpdateMode.UnscaledTime; + _animator.speed = 1f; + } } void Update() { + // PD 지시 2026-05-13 — Animator frame 제어 (intro 0~88f·loop 88~105f·outro 105~clip_end) + if (_animator != null) + { + float introEnd = INTRO_END_FRAME / FPS; // 1.4667s + float loopEnd = LOOP_END_FRAME / FPS; // 1.75s + float outroLength = Mathf.Max(0f, CLIP_LENGTH - loopEnd); // 1.0666s + float outroStart = Mathf.Max(introEnd, _duration - outroLength); + + float elapsed = Time.unscaledTime - _spawnTime; + float targetTime; + + if (elapsed < introEnd) + { + targetTime = elapsed; + } + else if (elapsed < outroStart) + { + float loopRange = Mathf.Max(0.01f, loopEnd - introEnd); + float loopT = (elapsed - introEnd) % loopRange; + targetTime = introEnd + loopT; + } + else + { + targetTime = loopEnd + (elapsed - outroStart); + } + + float normalized = Mathf.Clamp01(targetTime / CLIP_LENGTH); + _animator.Play(STATE_HASH, 0, normalized); + } + if (Time.unscaledTime - _spawnTime >= _duration) { Destroy(gameObject);