강화 결과 연출 처리 구조

목차

1. 요구 사항

2. 설계 목표

3. 흐름도

4. 구현

        4.1. 강화 결과 처리 진입점

       4.2. 결과 연출 처리

5. 개발 의도

1. 시스템 요구 사항

룰렛 기반 강화 방식과 확률 판정 구조를 구현한 이후, 다음으로 해결해야 했던 문제는 강화 결과를 어떻게 플레이어에게 전달할 것인가였다.

강화 시스템에서 성공과 실패는 단순히 수치만 변경되는 이벤트가 아니라, 플레이어의 감정 반응과 직접적으로 연결되는 핵심 순간이다.

만약 강화 결과가 텍스트 한 줄로만 표시되거나, UI 반응이 늦거나 혹은 성공과 실패의 차이가 명확하지 않다면 강화 시도의 긴장감과 성취감은 크게 떨어질 수 있다.

따라서 이 단계에서는 강화 결과를 즉각적이고 직관적으로 인식할 수 있는 연출 구조를 설계하는 것이 핵심 요구사항이였다.

2. 설계  목표

- 강화 성공 / 실패를 즉시 구분할 수 있을 것

- 시각적·청각적 피드백이 동시에 제공될 것

- 연출 로직이 강화 판정 로직과 섞이지 않을 것

- 연출 방식이 변경되더라도 수정 범위가 최소화될 것

- 강화 결과 인지와 UI 반응 사이에 지연이 발생하지 않을 것

3. 흐름도

강화 결과 연출은 확률 판정 이후 즉시 실행되며, '결과 표시 → 연출 재생 → 상태 정리'의 흐름으로 구성된다.

이 흐름을 통해 강화 결과가 판정되는 순간과 플레이어가 결과를 인식하는 순간이 최대한 일치하도록 구성했다.

4. 구현

위 그림은 본 강화 시스템에서 강화 성공 및 실패 시 결과 연출이 처리되는 전체 흐름을 나타낸다.

강화 결과는 단순히 수치만 변경되는 이벤트가 아니라, 플레이어의 감정 반응과 직결되는 핵심 순간이기 때문에 '결과 인지 → 시각적 연출 → 청각적 피드백'이 즉각적으로 연결되도록 설계하였다.

강화 결과가 판정되는 즉시 성공과 실패를 명확히 구분할 수 있는 텍스트, 색상, 이펙트, 사운드를 함께 출력함으로써, 플레이어가 결과를 직관적으로 인식할 수 있도록 구성하였다.

이를 통해 강화 성공 시에는 성취감을, 실패 시에도 결과에 대한 납득 가능성을 제공하는 것을 목표로 했다.

4.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에 즉시 반영한다.

이 과정에서 강화 전·후 단계를 별도로 저장한 이유는, 결과 텍스트 출력 시 강화 단계 변화를 명확히 표시하기 위함이다.

아이템 데이터가 변경된 이후에는, 강화 단계 변경으로 영향을 받는 UI 상태를 즉시 갱신하기 위해 UpdateSelectedItemUI(), UpdateRequirementsByType(), UpdateUpgradeState()를 같은 함수 내에서 순차적으로 호출한다.

이를 통해 강화 결과가 판정된 프레임과 UI가 갱신되는 프레임 사이에 상태 불일치가 발생하지 않도록 했다.

강화 실패 시에는 아이템 데이터에는 어떠한 변경도 가하지 않는다.

실패의 경우 강화 단계와 요구 재료가 변하지 않기 때문에, UI 상태 갱신 함수는 호출하지 않고 결과 텍스트 설정과 연출 호출만 수행하도록 분기하였다.

마지막으로 강화 성공 / 실패 여부만을 인자로 PlayResultEffect()를 호출하여, 시각적·청각적 연출 처리를 별도의 함수로 위임한다.

이를 통해 결과 데이터 처리와 연출 로직을 코드 레벨에서 명확히 분리하였다.

4.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으로 선택되며, PlayOneShot()을 사용해 기존 사운드 재생 상태에 영향을 주지 않고 단발성 결과 사운드를 중첩 재생하도록 구성하였다.

강화 결과의 사운드 출력에는 AudioSource.PlayOneShot()을 사용하였다.

강화 시스템은 UI 버튼 클릭음, 슬롯 선택 사운드 등 다른 UI 사운드와 동시에 동작할 가능성이 높은 구조이기 때문에, 강화 결과 사운드가 기존 사운드를 중단하거나 덮어쓰는 상황은 피할 필요가 있었다.

PlayOneShot()은 현재 재생 중인 사운드에 영향을 주지 않고, 지정한 효과음을 단발성으로 중첩 재생할 수 있기 때문에, 강화 성공·실패와 같이 짧고 명확한 피드백이 필요한 상황에 적합한 방식이다.

만약 AudioSource.Play()를 사용할 경우, 강화 결과 사운드 재생 시 기존에 재생 중이던 UI 사운드가 끊기거나 교체될 수 있으며, 이를 제어하기 위해 추가적인 오디오 소스 분리나 상태 관리가 필요해진다.

반면 PlayOneShot()을 사용하면 하나의 AudioSource를 유지한 상태에서 결과 사운드를 자연스럽게 겹쳐서 재생할 수 있어, 사운드 관리 구조를 단순하게 유지할 수 있었다.

강화 결과 사운드는 반복 재생이나 중간 제어가 필요한 BGM이 아니라, 결과 인지를 돕는 단발성 효과음이기 때문에, 재생 제어보다는 즉시성과 안정적인 중첩 재생이 더 중요한 요소였다.

이러한 이유로 본 강화 시스템에서는 시각적 연출에는 즉각적인 반응성과 단순성을 가진 ParticleSystem.Play()를, 청각적 피드백에는 다른 사운드 흐름에 영향을 주지 않는 AudioSource.PlayOneShot()을 사용하였다.

이 조합을 통해 강화 결과가 판정되는 순간과 플레이어가 결과를 인식하는 순간 사이의 지연을 최소화할 수 있었고, 연출 방식이 변경되더라도 강화 판정 및 데이터 처리 로직에는 영향을 주지 않는 안정적인 결과 연출 구조를 구성할 수 있었다.

이 함수는 연출 처리만을 담당하며, 강화 결과 데이터나 UI 텍스트 변경과 같은 로직은 포함하지 않는다.

이를 통해 연출 방식이 변경되더라도 강화 결과 판정 및 데이터 처리 로직에는 영향을 주지 않도록 설계하였다.

5. 개발 의도

강화 결과 연출은 강화 시스템 전체에서 가장 감정적인 반응이 발생하는 구간이다.

이 때문에 연출 코드가 강화 판정 로직과 섞일 경우 코드 가독성과 유지보수성이 빠르게 저하될 가능성이 있었다.

따라서 이 단계에서는 강화 결과 판정, 데이터 반영, 연출 실행을 명확히 분리한 구조를 선택했다.

이 구조를 통해 연출 방식이 변경되거나 강화 UI가 확장되더라도 강화 로직의 안정성을 유지할 수 있었고, 강화 결과 전달 방식만 독립적으로 개선할 수 있는 기반을 마련할 수 있었다.