151 lines
8.1 KiB
C#
151 lines
8.1 KiB
C#
|
|
// ================================================================
|
||
|
|
// CardParticleEffect.cs
|
||
|
|
// 카드 선택 시 파티클 이펙트를 켜고 끄는 컴포넌트
|
||
|
|
// CardSelectionEffect.cs의 파티클 버전 대체품
|
||
|
|
// ================================================================
|
||
|
|
// ⚠️ 기존 LevelUpUIManager + CardUI 시스템과 통합 버전
|
||
|
|
// - Time.unscaledDeltaTime 사용 → Time.timeScale = 0 에서도 동작
|
||
|
|
// - Instantiate로 매번 새로 생성되는 카드 프리팹에 대응
|
||
|
|
// ================================================================
|
||
|
|
using UnityEngine; // Unity 엔진 핵심 네임스페이스
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 카드 하나에 부착되어 선택 시 파티클 이펙트를 재생/정지하는 컴포넌트
|
||
|
|
/// CardUI.SetSelected()에서 Select()/Deselect()가 호출됩니다
|
||
|
|
/// </summary>
|
||
|
|
public class CardParticleEffect : MonoBehaviour
|
||
|
|
{
|
||
|
|
// =======================================================================
|
||
|
|
// 인스펙터 노출 필드
|
||
|
|
// =======================================================================
|
||
|
|
|
||
|
|
[Header("=== 파티클 설정 ===")]
|
||
|
|
[SerializeField] private ParticleSystem _particleSystem; // 카드 자식으로 배치된 파티클 시스템을 할당합니다 (미할당 시 자동 캐싱)
|
||
|
|
|
||
|
|
[Header("=== 스케일 설정 ===")]
|
||
|
|
[SerializeField] private float _selectedScale = 1.08f; // 선택 시 카드 확대 비율 (1.0 = 원본 크기)
|
||
|
|
[SerializeField] private float _scaleSpeed = 8.0f; // 스케일 변화의 보간 속도
|
||
|
|
|
||
|
|
// =======================================================================
|
||
|
|
// 내부 상태 변수
|
||
|
|
// =======================================================================
|
||
|
|
private bool _isSelected; // 현재 선택 상태 여부
|
||
|
|
private Vector3 _originalScale; // 원본 스케일 (복원용)
|
||
|
|
private Vector3 _targetScale; // 목표 스케일
|
||
|
|
private bool _isInitialized; // 초기화 완료 여부 (중복 초기화 방지)
|
||
|
|
|
||
|
|
// =======================================================================
|
||
|
|
// 읽기 전용 프로퍼티
|
||
|
|
// =======================================================================
|
||
|
|
public bool IsSelected => _isSelected; // 외부에서 선택 상태를 확인할 수 있는 프로퍼티
|
||
|
|
|
||
|
|
// =======================================================================
|
||
|
|
// Unity 라이프사이클: Awake - 컴포넌트 캐싱
|
||
|
|
// =======================================================================
|
||
|
|
private void Awake()
|
||
|
|
{
|
||
|
|
CacheComponents(); // Awake에서 컴포넌트를 미리 캐싱합니다
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 파티클 시스템을 캐싱하고 초기 상태를 설정합니다
|
||
|
|
/// Awake에서 한 번만 호출하여 런타임 GetComponent 호출을 제거합니다
|
||
|
|
/// </summary>
|
||
|
|
private void CacheComponents()
|
||
|
|
{
|
||
|
|
if (_isInitialized) return; // 이미 초기화되었으면 중복 실행을 방지합니다
|
||
|
|
|
||
|
|
if (_particleSystem == null) // Inspector에서 안 넣었으면
|
||
|
|
_particleSystem = GetComponentInChildren<ParticleSystem>(); // 자식에서 자동으로 찾습니다
|
||
|
|
|
||
|
|
if (_particleSystem != null) // 파티클을 찾았으면
|
||
|
|
{
|
||
|
|
_particleSystem.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear); // 시작 시 파티클을 완전히 정지시킵니다
|
||
|
|
|
||
|
|
var main = _particleSystem.main; // Main 모듈에 접근합니다
|
||
|
|
main.playOnAwake = false; // 자동 재생을 꺼서 Select() 호출 전까지 조용히 대기합니다
|
||
|
|
main.useUnscaledTime = true; // Time.timeScale=0에서도 파티클이 재생되도록 설정합니다
|
||
|
|
}
|
||
|
|
|
||
|
|
_originalScale = transform.localScale; // 원본 스케일을 저장합니다 (나중에 복원용)
|
||
|
|
_targetScale = _originalScale; // 초기 목표 스케일 = 원본
|
||
|
|
|
||
|
|
_isInitialized = true; // 초기화 완료 플래그를 설정합니다
|
||
|
|
}
|
||
|
|
|
||
|
|
// =======================================================================
|
||
|
|
// Unity 라이프사이클: Update - 매 프레임 스케일 보간
|
||
|
|
// ⚠️ Time.unscaledDeltaTime 사용 → Time.timeScale = 0 에서도 동작
|
||
|
|
// =======================================================================
|
||
|
|
private void Update()
|
||
|
|
{
|
||
|
|
if (!_isInitialized) return; // 초기화 안 됐으면 스킵합니다 (방어 코드)
|
||
|
|
|
||
|
|
if (Vector3.SqrMagnitude(transform.localScale - _targetScale) < 0.0001f) // 스케일이 목표에 거의 도달했으면
|
||
|
|
return; // 불필요한 연산을 스킵합니다 (SqrMagnitude: 루트 연산 없이 거리 비교)
|
||
|
|
|
||
|
|
transform.localScale = Vector3.Lerp( // 현재 → 목표로 부드럽게 보간합니다
|
||
|
|
transform.localScale, // 현재 스케일
|
||
|
|
_targetScale, // 목표 스케일
|
||
|
|
_scaleSpeed * Time.unscaledDeltaTime // ⚠️ unscaledDeltaTime → timeScale=0에서도 동작합니다
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// =======================================================================
|
||
|
|
// 공개 API: 외부에서 선택/해제를 제어하는 메서드들
|
||
|
|
// =======================================================================
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 이 카드를 선택 상태로 전환합니다
|
||
|
|
/// 파티클 재생 + 스케일 확대
|
||
|
|
/// CardUI.SetSelected(true)에서 호출됩니다
|
||
|
|
/// </summary>
|
||
|
|
public void Select()
|
||
|
|
{
|
||
|
|
if (!_isInitialized) CacheComponents(); // Instantiate 직후 호출에 대비하여 방어적 초기화
|
||
|
|
|
||
|
|
if (_isSelected) return; // 이미 선택 상태면 중복 처리를 방지합니다
|
||
|
|
|
||
|
|
_isSelected = true; // 선택 상태 플래그를 켭니다
|
||
|
|
|
||
|
|
_targetScale = _originalScale * _selectedScale; // 목표 스케일을 확대 비율로 설정합니다
|
||
|
|
|
||
|
|
if (_particleSystem != null) // 파티클이 있으면
|
||
|
|
_particleSystem.Play(true); // 파티클 재생을 시작합니다 (true = 자식 파티클도 함께)
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 이 카드를 해제 상태로 전환합니다
|
||
|
|
/// 파티클 정지 (자연 소멸) + 스케일 원복
|
||
|
|
/// CardUI.SetSelected(false)에서 호출됩니다
|
||
|
|
/// </summary>
|
||
|
|
public void Deselect()
|
||
|
|
{
|
||
|
|
if (!_isSelected) return; // 이미 해제 상태면 중복 처리를 방지합니다
|
||
|
|
|
||
|
|
_isSelected = false; // 선택 상태 플래그를 끕니다
|
||
|
|
|
||
|
|
_targetScale = _originalScale; // 목표 스케일을 원본으로 되돌립니다
|
||
|
|
|
||
|
|
if (_particleSystem != null) // 파티클이 있으면
|
||
|
|
_particleSystem.Stop(true, ParticleSystemStopBehavior.StopEmitting); // 방출을 중지합니다 (이미 나온 파티클은 자연스럽게 사라짐)
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 이펙트를 즉시 초기화합니다 (보간 없이 바로 꺼짐)
|
||
|
|
/// CardUI.Setup()에서 호출되어 카드 재사용 시 깨끗한 상태를 보장합니다
|
||
|
|
/// </summary>
|
||
|
|
public void ResetEffect()
|
||
|
|
{
|
||
|
|
if (!_isInitialized) CacheComponents(); // 초기화 안 됐으면 실행합니다
|
||
|
|
|
||
|
|
_isSelected = false; // 선택 상태를 해제합니다
|
||
|
|
|
||
|
|
_targetScale = _originalScale; // 스케일 목표를 원본으로 되돌립니다
|
||
|
|
transform.localScale = _originalScale; // 스케일을 즉시 원본으로 적용합니다 (보간 없이)
|
||
|
|
|
||
|
|
if (_particleSystem != null) // 파티클이 있으면
|
||
|
|
_particleSystem.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear); // 즉시 정지하고 남은 파티클도 모두 제거합니다
|
||
|
|
}
|
||
|
|
}
|