벌목 진행 설계 (게이지 기반 채집 진행 & 애니메이션 및 사운드 동기화)

목차

1. 요구 사항

2. 설계 목표

3. 흐름도

4. 구현

        4.1. 벌목 진행 게이지 UI 생성

       4.2. 벌목 진행 시간 처리

       4.3. 벌목 완료 처리

       4.4. 벌목 타격 사운드 및 나무 흔들림 연출

5. 개발 의도

1. 시스템 요구 사항

벌목 상호작용이 시작되면 플레이어는 즉시 나무를 획득하는 것이 아니라 일정 시간이 지나야 벌목이 완료되도록 설계해야 한다.

이러한 구조는 단순 클릭 기반 수집이 아닌 행동 기반 채집 시스템을 만들기 위한 것이다.

플레이어는 도끼를 사용하여 일정 시간 동안 벌목 행동을 수행하고, 그 진행 상태를 UI로 확인할 수 있어야 한다.

벌목 진행은 시간 기반 게이지로 표현되며, 게이지가 최대값에 도달하면 벌목이 완료된다.

진행 중에는 캐릭터 애니메이션이 벌목 상태로 전환되어야 하며, 실제 행동 타이밍에 맞추어 사운드가 재생되어야 한다.

또한 벌목 대상인 나무는 시각적으로 타격을 받는 느낌을 주기 위해 흔들리는 연출이 적용되어야 한다.

벌목이 완료되면 벌목 상태를 종료하고, 벌목 대상 오브젝트에게 벌목 완료 이벤트를 전달하여 이후에 나무 쓰러지는 등의 시스템이 진행될 수 있도록 해야 한다.

2. 설계  목표

- 시간 기반 채집 진행 시스템 구현

- Slider UI를 활용한 진행 게이지 표현

- 벌목 애니메이션과 시스템 상태 동기화

- 타격 타이밍에 맞는 사운드 재생

- 나무 흔들림 연출을 통한 타격 피드백 제공

- 벌목 완료 시 Tree 시스템으로 이벤트 전달

3. 흐름도

벌목 시작

  ↓

벌목 게이지 UI 표시

  ↓

Update 루프에서 시간 누적

  ↓

애니메이션 Logging 상태 활성화

  ↓

타격 사운드 재생

  ↓

나무 흔들림 연출

  ↓

게이지 최대값 도달

  ↓

벌목 완료

  ↓

Tree.Felled() 호출

벌목 시스템은 입력 순간 즉시 완료되는 구조가 아니라 일정 시간 동안 진행되는 행동 시스템이다.

플레이어 입력으로 벌목이 시작되면 게이지 UI가 활성화되고, Update 루프에서 시간이 누적되면서 진행 상태가 증가한다.

진행 중에는 애니메이션과 사운드가 동기화되며, 진행이 완료되면 Tree 오브젝트에게 벌목 완료를 알리는 방식으로 시스템이 연결된다.

4. 구현

4.1. 벌목 진행 게이지 UI 생성
void InteractGSP(Vector3 position)
{
    Vector3 viewPoint = Camera.main.WorldToScreenPoint(position);
    GSPBar.transform.position = viewPoint;
    GSPBar.SetActive(true);
}

벌목 진행 UI는 월드 위치를 기준으로 화면에 표시된다.

이때 Camera.WorldToScreenPoint 함수를 사용하여 나무의 월드 좌표를 화면 좌표로 변환한 뒤 해당 위치에 게이지 UI를 배치한다.

이 방식은 플레이어가 실제로 벌목하고 있는 오브젝트 위에 진행 상태가 표시되도록 만들어 주며, 플레이어는 어떤 대상의 채집이 진행되고 있는지 직관적으로 이해할 수 있다.

만약 UI를 화면 고정 위치에 표시한다면 플레이어는 현재 어떤 오브젝트를 채집하고 있는지 명확히 알기 어려울 수 있다.

따라서 월드 좌표 기반 UI 표시 방식이 상호작용 시스템과 자연스럽게 연결된다.

GSPBar.SetActive(true)는 벌목 진행 UI를 활성화하는 역할을 한다.

벌목 시작 시에만 UI를 활성화하고, 벌목이 완료되거나 중단될 경우 UI를 다시 비활성화하도록 설계되어 있다.

4.2. 벌목 진행 시간 처리
void GSPBarTimeTree(GameObject obj)
{
    interactKey.gameObject.SetActive(false);

    ani.SetBool("Logging", true);

    StartCoroutine(PlayActionSound(SoundManager.instance.loggingClips, 0.65f, obj));

    GSP.value += Time.deltaTime;
    GSTxt.text = (int)GSP.value + " / " + (int)GSP.maxValue + "초";
    
    // 벌목 완료 처리 → 4.3. 설명
}

벌목 진행은 Slider UI를 이용한 시간 기반 게이지 방식으로 구현하였다.

Slider는 Unity UI에서 값 범위를 시각적으로 표현하기 위한 컴포넌트이며, value 값을 변경하면 자동으로 진행 바가 업데이트된다.

벌목 진행 시간은 Time.deltaTime을 이용해 누적된다.

Time.deltaTime은 현재 프레임과 이전 프레임 사이의 시간 간격을 의미하며, 이를 이용하면 프레임 속도와 관계없이 일정한 시간 흐름을 구현할 수 있다.

만약 단순히 value += 1과 같은 방식으로 값을 증가시키면 프레임 속도가 높을수록 진행 속도가 빨라지는 문제가 발생한다.

Time.deltaTime을 사용하면 이러한 문제를 해결할 수 있다.

ani.SetBool("Logging", true)는 Animator의 Bool 파라미터를 변경하여 캐릭터의 벌목 애니메이션을 실행한다.

Animator는 Unity의 애니메이션 상태 머신 시스템이며, Bool 파라미터를 기반으로 상태 전환을 수행한다.

벌목이 진행되는 동안 Logging 상태를 활성화하여 캐릭터가 실제로 도끼를 휘두르는 애니메이션을 재생하도록 설계하였다.

4.3. 벌목 완료 처리
if (GSP.value >= GSP.maxValue)
{
    GSP.value = 0f;

    ani.SetBool("Logging", false);

    obj.GetComponent<Tree>().Felled();

    GSPBar.SetActive(false);

    isKeyPress = false;
}

벌목 진행 게이지가 최대값에 도달하면 벌목이 완료된다.

이때 Slider 값은 다시 0으로 초기화된다.

ani.SetBool("Logging", false)를 통해 벌목 애니메이션 상태를 종료한다.

Animator 상태를 명확히 종료하지 않으면 캐릭터가 계속 벌목 애니메이션을 유지하는 문제가 발생할 수 있기 때문이다.

벌목 완료 시 가장 중요한 부분은 Tree 오브젝트에게 벌목 완료를 알리는 것이다.

이를 위해 벌목 중인 나무의 GetComponent를 하여, Tree클래스의 Felled 함수를 호출한다.

GetComponent는 Unity에서 특정 컴포넌트를 가져오는 함수이며, Tree 스크립트에 정의된 Felled 함수를 실행한다.

이 호출을 통해 나무는 물리 상태를 변경하고 실제로 쓰러지는 동작을 수행하게 된다.

이 구조는 벌목 진행 시스템과 나무 행동 시스템을 분리하기 위한 설계이다.

InteractDataJWR는 벌목 진행을 관리하고, Tree 클래스는 나무의 물리 동작과 재생성을 관리한다.

4.4. 벌목 타격 사운드 및 나무 흔들림 연출
IEnumerator PlayActionSound(AudioClip[] clips, float delay, GameObject hitObj = null)
{
    if (isPlayingActionSound) yield break;
    isPlayingActionSound = true;

    yield return new WaitForSeconds(delay);

    if (clips != null && clips.Length > 0)
    {
        SoundManager.instance.PlaySfx(
            transform.position,
            clips[Random.Range(0, clips.Length)],
            0,
            SoundManager.instance.sfxVolum
        );
    }

    if (hitObj != null && hitObj.CompareTag("Tree"))
    {
        hitObj.transform.rotation *= Quaternion.Euler(0.3f, 0, 0.3f);
    }

    isPlayingActionSound = false;
}

벌목 사운드는 코루틴을 이용해 일정 시간 지연 후 재생하도록 설계하였다.

이 지연은 애니메이션의 타격 타이밍과 사운드를 맞추기 위한 것이다.

코루틴은 Unity에서 비동기 시간 흐름을 구현하는 방법 중 하나이며, WaitForSeconds를 사용하면 지정한 시간이 지난 뒤 다음 코드를 실행할 수 있다.

이를 통해 애니메이션 시작 직후가 아니라 실제 도끼가 나무에 닿는 타이밍에 사운드를 재생할 수 있다.

사운드는 SoundManager를 통해 재생된다.

SoundManager는 프로젝트 내에서 사운드를 중앙에서 관리하기 위한 시스템으로, 사운드 재생을 하나의 인터페이스로 통합하는 역할을 한다.

또한 나무가 타격을 받았을 때 시각적 피드백을 제공하기 위해 나무의 회전을 약간 변경하였다.

Quaternion.Euler를 사용하여 나무의 회전에 미세한 변화를 주면 실제로 충격을 받은 것처럼 흔들리는 효과를 만들 수 있다.

Quaternion은 Unity에서 회전을 표현하는 방식이며, Euler 각도를 Quaternion으로 변환하여 적용할 수 있다.

5. 개발 의도

벌목 시스템을 단순한 상호작용 이벤트가 아니라 행동 기반 채집 시스템으로 만들기 위해 시간 기반 진행 구조를 도입하였다.

플레이어가 입력을 한 번 누르는 즉시 결과가 발생하는 방식은 상호작용의 몰입감을 떨어뜨릴 수 있기 때문에, 일정 시간 동안 행동을 수행하는 구조가 더 자연스럽다고 판단하였다.

또한 벌목 행동이 단순히 게이지가 채워지는 UI 이벤트로 끝나지 않도록 애니메이션과 사운드를 동기화하였다.

도끼가 실제로 나무를 타격하는 순간에 사운드가 재생되도록 코루틴을 활용하였고, 나무가 흔들리는 시각적 연출을 추가하여 플레이어가 행동의 결과를 즉각적으로 인지할 수 있도록 설계하였다.

마지막으로 벌목 진행 시스템과 나무 동작 시스템을 분리하여 책임을 명확히 하였다.

벌목 진행은 InteractData가 담당하고, 나무의 물리 동작과 재생성은 Tree 클래스가 담당하도록 구조를 나누어 시스템 간 결합도를 낮추었다.