Project/OUTPOST

[Unity UniTask] 헬기 접근부터 이륙까지 하나의 탈출 연출로 만들기

유탐기 2026. 8. 29. 19:48
반응형

OUTPOST의 마지막 목표는 헬기를 타고 탈출하는 것입니다. 단순히 특정 위치에 도착하자마자 결과 화면을 보여주면 마지막 상황이 밋밋해질 수 있어, 헬기 진입부터 탑승·이륙까지 이어지는 연출을 구성했습니다.

1. 탈출을 하나의 순서로 관리하기

헬기 탈출 과정에는 순서가 있습니다. 헬기가 착륙장으로 들어오고, 플레이어가 문 앞으로 이동하고, 탑승한 뒤 이륙하는 흐름입니다. 한 단계가 끝나기 전에 다음 단계를 시작하면 서로 다른 Transform 이동이 겹칠 수 있습니다.

그래서 HelicopterExtractionController.PlayExtractionAsync()에서 각 단계를 await로 연결했습니다.

if (!await MoveAsync(helicopter, helicopter.position,
    landingPoint.position, approachDuration, token)) return false;

// 플레이어 입력을 잠근 다음
if (!await MoveAsync(player.transform, player.transform.position,
    doorPosition, doorApproachDuration, token)) return false;
if (!await MoveAsync(player.transform, doorPosition,
    insidePosition, boardingDuration, token)) return false;

bool departed = await DepartWithCameraAsync(token);

UniTask는 비동기 작업을 C#의 async/await 흐름으로 작성할 수 있게 합니다. 각 이동을 한 번에 모두 실행하는 대신, 현재 이동이 끝난 뒤 다음 동작으로 넘어가도록 했습니다.

2. 연출 중 플레이어 조작을 막는 이유

헬기에 탑승하는 연출 중 플레이어가 계속 WASD나 마우스로 움직인다면 자동으로 이동시키는 코드와 충돌할 수 있습니다.

그래서 플레이어를 이동시키기 전에 PlayerController, WeaponManager, MouseLook을 비활성화하고, CharacterController도 잠시 꺼둡니다. 조준점과 저격 조준경 역시 숨기도록 했습니다.

이 단계부터는 플레이어 입력이 아니라 연출 코드가 위치와 카메라를 제어합니다.

3. 이동을 프레임 단위로 보간하기

MoveAsync()에서는 이동 시간을 누적한 다음 Mathf.SmoothStep()을 통해 부드러운 이동 비율을 만듭니다.

while (elapsed < duration)
{
    elapsed += Time.deltaTime;
    float t = Mathf.SmoothStep(0f, 1f,
        Mathf.Clamp01(elapsed / Mathf.Max(0.01f, duration)));
    target.position = Vector3.Lerp(from, to, t);
    await UniTask.Yield(PlayerLoopTiming.Update);
    if (token.IsCancellationRequested) return false;
}

Lerp()는 시작 위치와 목표 위치 사이를 보간하고, SmoothStep()은 출발과 도착 근처의 속도 변화를 완만하게 만듭니다. 또 CancellationToken을 받도록 해서 게임 오브젝트가 파괴되거나 전체 흐름이 취소될 때 연출을 계속 진행하지 않도록 했습니다.

4. 이륙은 직선이 아니라 곡선으로

이륙 구간은 단순한 직선 이동과 다릅니다. DepartWithCameraAsync()에서는 시작점, 제어점, 도착점을 이용한 2차 베지어 곡선으로 헬기의 위치를 계산합니다.

helicopter.position =
    inverse * inverse * helicopterFrom +
    2f * inverse * t * curveControl +
    t * t * departurePoint.position;

여기에 기체 기울기와 시네마틱 카메라 추적을 더해 탈출 장면을 만들었습니다. 탑승 이후에는 플레이어의 렌더러를 숨기고 별도의 카메라를 활성화합니다.


정리

착륙 → 조작 잠금 → 문까지 이동 → 탑승 → 시네마틱 카메라 → 곡선 이륙 순서로 탈출 장면을 구성했습니다. 비동기 메서드를 단계별로 분리해 순서를 읽기 쉽도록 했고, 취소 상황을 검사하도록 만들었습니다. 이 글은 개별 헬기 연출에 대한 내용이며, 전체 웨이브 진행을 제어하는 내용은 별도 글에서 다루겠습니다.


참고한 구현 코드

반응형