강화 결과 연출 처리 구조
1. 시스템 요구 사항
룰렛 기반 강화 방식과 확률 판정 구조를 구현한 이후, 다음으로 해결해야 했던 문제는 강화 결과를 어떻게 플레이어에게 전달할 것인가였다.
성공과 실패는 단순히 수치만 변경되는 이벤트가 아니라, 플레이어의 감정과 직접적으로 연결되는 핵심 순간이다.
만약 강화 결과가 텍스트 한 줄로만 표시되거나, UI 반응이 늦거나, 혹은 성공과 실패의 차이가 명확하지 않다면 강화 시도의 긴장감과 성취감은 크게 떨어질 수 있다.
따라서 이 단계에서는 강화 결과를 즉각적이고 직관적으로 인식할 수 있는 연출 구조를 설계하는 것이 핵심 목표였다.
2. 흐름도

강화 결과 연출은 확률 판정 이후 즉시 실행되며, '결과 표시 → 연출 재생 → 상태 정리'의 흐름으로 구성된다.
이 흐름을 통해 강화 결과가 판정되는 순간과 플레이어가 결과를 인식하는 순간이 최대한 일치하도록 구성했다.
3. 구현

위 그림은 본 강화 시스템에서 강화 성공 및 실패 시 결과 연출이 처리되는 전체 흐름을 나타낸다.
강화 결과는 플레이어의 감정과 연결되는 순간이기 때문에 '결과 인지 → 시각적 연출 → 청각적 피드백' 이 즉각적으로 연결되도록 설계하였다.
강화 결과가 판정되는 즉시 성공과 실패를 명확히 구분할 수 있는 '텍스트, 색상, 이펙트, 사운드' 를 함께 출력함으로써, 플레이어가 결과를 직관적으로 인식할 수 있도록 구성하였다.
이를 통해 강화 성공 시에는 성취감을, 실패 시에도 결과에 대한 납득 가능성을 제공하는 것을 목표로 했다.
3.1. 강화 결과 처리 진입점
private void ApplyUpgradeResult(bool success)
{
if (success)
{
int currentGrade = selectedItem.grade;
int nextGrade = currentGrade + 1;
selectedItem.grade = nextGrade;
resultTxt.color = Color.yellow;
resultTxt.text = $"강화 성공: {currentGrade} -> {nextGrade}";
UpdateSelectedItemUI();
UpdateRequirementsByType();
UpdateUpgradeState();
PlayResultEffect(true);
}
else
{
resultTxt.color = Color.red;
resultTxt.text = "강화 실패";
PlayResultEffect(false);
}
}
ApplyUpgradeResult 함수는 강화 결과가 확정된 이후, 아이템 데이터 변경과 결과 연출 호출을 순차적으로 처리한다.
함수는 전달받은 success 값에 따라 성공과 실패 로직을 분기한다.
강화 성공 시에는 현재 아이템의 강화 단계를 지역 변수(currentGrade)로 저장하고, 다음 강화 단계(nextGrade)를 계산하여, selectedItem.grade에 즉시 반영하였다.
결과 텍스트 출력 시 강화 단계 변화를 명확히 표시하기 위해, 강화 전·후 단계를 별도로 저장하였다.
아이템 데이터 변경 직후, UpdateSelectedItemUI, UpdateRequirementsByType, UpdateUpgradeState 함수들을 순차적으로 호출하여, 프레임 간 데이터와 화면 사이의 상태 불일치를 원천 차단하였다.
강화 실패 시에는 아이템 데이터 변경이 불필요하므로, UI 갱신 함수 호출을 생략하고 결과 연출만 수행하도록 분기하여 불필요한 연산을 방지하였다.
마지막으로 강화 결과에 대한 시각·청각적 피드백은 PlayResultEffect 함수로 위임하였다.
이는 결과 데이터 처리와 연출 로직을 코드 레벨에서 분리하여 유지보수성을 높이기 위함이다.
3.2. 결과 연출 처리
private void PlayResultEffect(bool success)
{
if (success && successEffect != null)
successEffect.Play();
else if (!success && failEffect != null)
failEffect.Play();
if (audioSource != null)
{
AudioClip clip = success ? successClip : failClip;
if (clip != null)
audioSource.PlayOneShot(clip);
}
}
PlayResultEffect 함수는 강화 결과에 따른 시각적·청각적 연출을 담당하는 전용 함수이다.
이 함수는 강화 성공 여부(success)만을 입력 값으로 받아, 연출에 필요한 이펙트와 사운드를 선택적으로 재생한다.
먼저 강화 성공 여부에 따라 재생할 ParticleSystem을 분기한다.
success가 true일 경우 successEffect를, false일 경우 failEffect를 재생하도록 구성하였으며, 각 이펙트에 대해 null 체크를 수행하여 이펙트가 설정되지 않은 상태에서도 런타임 오류가 발생하지 않도록 했다.
강화 결과의 시각적 연출에는 Unity의 ParticleSystem.Play()를 사용하였다.
ParticleSystem.Play()는 파티클 시스템을 즉시 재생시키는 기본 API로, 호출 시점에 별도의 상태 전환이나 초기화 과정 없이 곧바로 시각적 효과를 출력할 수 있다.
강화 성공·실패는 결과가 판정되는 순간 플레이어의 시선과 감정이 가장 집중되는 이벤트이기 때문에, 연출 역시 입력이나 상태 변화에 따라 지속적으로 제어되는 방식보다는, 결과가 확정되는 시점에 즉각적으로 반응하는 구조가 적합하다고 판단하였다.
또한 Play는 이미 재생 중인 파티클 시스템에 대해 다시 호출되더라도, 설정에 따라 자연스럽게 재생이 이어지거나 다시 시작되므로, 강화 시도가 연속적으로 발생하는 상황에서도 별도의 재생 상태 관리 로직을 추가할 필요가 없다.
연출 재생을 위해 별도의 플래그나 타이머를 관리하지 않아도 되었고, 결과 판정 코드와 연출 코드 사이의 결합도를 낮출 수 있었다.
파티클의 정확한 종료 시점을 코드로 제어하는 것보다, 결과가 판정되는 순간 즉각적으로 시각적 피드백을 제공하는 것이 더 중요했기 때문에, 단순하고 직관적인 ParticleSystem.Play() 방식이 요구사항에 가장 적합하다고 판단하였다.
이후 사운드 재생 처리를 수행한다.
AudioSource가 존재하는 경우에만 처리하도록 하여, 오디오 컴포넌트가 없는 상황에서도 시스템이 안전하게 동작하도록 했다.
AudioClip은 성공 여부에 따라 successClip 또는 failClip으로 선택된다.
강화 결과의 사운드 출력에는 AudioSource.PlayOneShot()을 사용하였다.
강화 시스템은 다른 UI 사운드(버튼 클릭 등)와 동시에 동작할 가능성이 있기 때문에, 강화 결과 사운드가 기존 사운드를 중단하거나 덮어쓰는 상황은 피해야 했다.
PlayOneShot은 현재 재생 중인 사운드에 영향을 주지 않고, 지정한 효과음을 단발성으로 중첩 재생할 수 있기 때문에, 짧고 명확한 피드백이 필요한 상황에 적합한 방식이다.
만약 AudioSource.Play()를 사용할 경우, 강화 결과 사운드 재생 시, 기존에 재생 중이던 UI 사운드가 끊기거나 교체될 수 있으며, 이를 제어하기 위해 추가적인 오디오 소스 분리나 상태 관리가 필요해진다.
반면 PlayOneShot을 사용하면 하나의 AudioSource를 유지한 상태에서 결과 사운드를 자연스럽게 겹쳐서 재생할 수 있어, 사운드 관리 구조를 단순하게 유지할 수 있었다.
강화 결과 사운드는 반복 재생이나 중간 제어가 필요한 BGM이 아니라, 결과 인지를 돕는 단발성 효과음이기 때문에, 재생 제어보다는 즉시성과 안정적인 중첩 재생이 더 중요한 요소였다.
이러한 이유로 본 강화 시스템에서는 시각적 연출에는 즉각적인 반응성과 단순성을 가진 ParticleSystem.Play()를, 청각적 피드백에는 다른 사운드 흐름에 영향을 주지 않는 AudioSource.PlayOneShot()을 사용하였다.
4. 개발 의도
강화 결과 연출은 강화 시스템 전체에서 가장 감정적인 반응이 발생하는 구간이다.
이 때문에 연출 코드가 강화 판정 로직과 섞일 경우 코드 가독성과 유지보수성이 빠르게 저하될 가능성이 있었다.
따라서 이 단계에서는 강화 결과 판정, 데이터 반영, 연출 실행을 명확히 분리한 구조를 선택했다.
