본문 바로가기

시간 시스템과 주기 조정

Directional Light 회전과 Sky Light·노출을 연동해 낮과 밤의 주기를 만들고 시간 속도와 갱신 비용을 조정합니다.

비를 내리고 구름을 바꾸는 단계까지 왔다면, 이제 레벨에 시간의 흐름을 넣을 차례입니다. 플레이어가 한 장소에 오래 머무는 동안 하늘의 색과 그림자의 길이, 빛의 세기가 자연스럽게 바뀌어야 세계가 살아 있다고 느끼게 됩니다. 반대로 해는 지는데 하늘만 낮처럼 밝거나, 자정을 넘는 순간 조명이 툭 튀면 몰입이 바로 깨집니다.

이 글에서는 시간 시스템(time of day system)을 이루는 액터들의 역할을 정리한 뒤, 블루프린트에서 게임 시계 하나를 만들어 태양의 회전과 조명, 후처리가 모두 그 시계를 읽게 하는 방법을 다룹니다. 특히 초를 시간으로 바꾸는 단위 계산과, 자정을 넘어갈 때의 처리를 계산 예와 함께 꼼꼼히 따라갑니다. 이 설정을 마치면 레벨은 고정된 배경이 아니라 시간에 따라 성격이 바뀌는 공간이 됩니다.


시간 시스템이 하는 일

시간의 흐름은 레벨의 분위기를 시시각각 바꿉니다. 밝고 활기찬 낮, 낭만적이거나 음산한 저녁노을, 어둡고 긴장된 밤이 하나의 레벨 안에서 이어집니다. 태양이 움직이면 빛의 방향과 그림자의 길이, 선명도도 실시간으로 바뀌어 장면이 훨씬 사실적으로 보입니다.

게임 플레이에도 쓸 수 있습니다. 밤에는 시야가 좁아져 새로운 도전이 되거나 숨고 잠입하는 플레이를 이끌 수 있고, 특정 시간에만 열리는 이벤트나 시간에 따라 행동을 바꾸는 NPC, 밤에만 나타나는 몬스터를 만들 수도 있습니다. 현실 세계의 자연스러운 흐름을 담기 때문에 플레이어가 게임 세계에 더 깊이 빠져들게 됩니다.


시간 시스템을 이루는 액터

디렉셔널 라이트: 태양과 달

디렉셔널 라이트는 낮에는 태양, 밤에는 달 역할을 하는 시간 시스템의 중심입니다. 회전이 빛의 방향을 정하고, Intensity가 빛의 양을 정합니다. 표면이 실제로 받는 빛은 빛이 들어오는 각도에도 영향을 받으므로, 해가 낮게 뜨면 같은 강도라도 땅이 덜 밝게 보입니다.

낮과 밤의 주기처럼 실행 중에 계속 회전해야 하므로 Mobility는 반드시 Movable로 정합니다. Stationary는 실행 중에 움직이거나 회전하는 것을 허용하는 선택이 아닙니다. Rotation에서는 Pitch(Y축 회전)가 해의 높이를, Yaw(Z축 회전)가 해가 떠 있는 방위를 정합니다. Intensity는 낮에는 높게, 밤에는 낮게 하고, Light Color는 낮에는 흰색이나 노란색 계열로, 해 질 녘에는 주황색이나 붉은색으로, 밤에는 푸른색 계열로 바꿔 분위기를 만듭니다.

스카이 라이트: 하늘의 주변광

스카이 라이트는 하늘 전체에서 들어오는 주변광을 흉내 내므로, 시간이 흐르며 하늘색이 바뀌면 그 색을 다시 캡처해 간접광을 갱신해야 합니다. 이 동적인 예에서는 Mobility를 Movable로 씁니다. Stationary도 캡처를 갱신할 수는 있지만, 구운 조명 부분의 제약을 따로 확인해야 합니다.

갱신하는 방법은 두 가지입니다. Real Time Capture를 켜면 지원되는 하늘 요소가 바뀔 때 자동으로 다시 캡처하고, 그렇지 않다면 필요할 때 Recapture를 직접 호출합니다. 자동 갱신이 늘 꼭 필요한 것은 아니므로, 장면에 맞게 고릅니다. Intensity Scale은 밤에는 낮게, 낮에는 높게 조절합니다.

스카이 어트모스피어와 구름

스카이 어트모스피어는 해의 높이에 따라 낮의 푸른 하늘과 노을의 붉은색, 새벽의 푸르스름한 색을 자동으로 표현합니다. 이렇게 하려면 디렉셔널 라이트에서 Atmosphere Sun Light를 켜고 인덱스를 지정합니다. 스카이 어트모스피어 쪽에 디렉셔널 라이트 액터를 참조하는 칸을 채우는 방식이 아닙니다. Rayleigh Scattering은 하늘이 얼마나 푸를지에, Mie Scattering은 노을이나 스모그 같은 뿌연 정도에 영향을 줍니다.

볼류메트릭 클라우드는 3D 구름으로 하늘에 입체감을 더하고, 디렉셔널 라이트의 방향에 따라 땅에 구름 그림자를 드리웁니다. 디렉셔널 라이트의 Atmosphere Sun Light와 구름 그림자 옵션을 확인하고, 구름 컴포넌트의 층 높이와 시작 고도, 구름 재질이 노출한 밀도와 덮는 정도의 파라미터를 구분해 조절합니다.

후처리 볼륨: 시간대의 색감

후처리 볼륨은 최종 화면에 노출, 색 보정, 블룸 같은 효과를 더해 시간대의 분위기를 강화합니다. 노출에서는 자동 노출이 움직일 범위인 Min EV100과 Max EV100(설정에 따라 Min Brightness와 Max Brightness)과, 전체 밝기를 한쪽으로 미는 Exposure Compensation을 구분합니다. Max Brightness를 낮춘다고 화면이 무조건 어두워진다는 규칙은 없습니다. 그래서 먼저 노출을 고정한 상태에서 기준을 잡은 뒤, 밤에 자동 노출이 얼마나 밝게 보정할지 그 범위를 조절합니다. Color Grading으로는 낮과 저녁, 밤에 맞는 색과 채도, 대비를 만들고, Bloom으로는 시간대마다 빛이 번지는 정도를 바꿉니다.


블루프린트로 시간 시스템 만들기

시간 시스템은 보통 블루프린트 하나가 위의 액터들을 모두 제어하게 만듭니다. 액터마다 시계를 따로 두면 태양의 각도와 하늘의 밝기, 후처리 색감이 서로 조금씩 다른 시각을 기준으로 움직여 어긋나기 쉽기 때문입니다. 그래서 아래에서는 블루프린트를 만들어 제어할 액터의 참조를 연결하고, 그 안에 게임 시계를 하나 둔 뒤, 그 시각으로 태양 회전과 조명, 후처리 값을 계산하는 순서로 진행합니다.

시간 시스템 블루프린트와 참조

콘텐츠 브라우저를 오른쪽 클릭해 Blueprint Class를 고르고, 부모 클래스로 Actor를 골라 BP_TimeOfDaySystem 같은 이름을 붙인 뒤 레벨에 하나 배치합니다. 그다음 이 블루프린트 안에 제어할 액터들의 참조 변수를 만듭니다. Directional Light 타입의 Directional Light Actor, Sky Light 타입의 Sky Light Actor, Sky Atmosphere 타입의 Sky Atmosphere Actor, Volumetric Cloud 타입의 Volumetric Cloud Actor, Post Process Volume 타입의 Post Process Volume Actor입니다.

여기서 구분할 것이 있습니다. 블루프린트에 변수를 만드는 것과 그 변수가 레벨의 실제 액터를 가리키게 하는 것은 다른 일입니다. 참조 변수를 Instance Editable로 노출하고, 레벨에 배치한 BP_TimeOfDaySystem의 디테일 패널에서 이 레벨의 광원과 하늘 액터를 하나씩 지정해야 합니다. 또 값을 바꾸는 노드의 Target이 액터를 받는지, 그 액터의 컴포넌트를 받는지도 확인합니다. 참조가 비어 있으면 시계 계산이 맞아도 화면은 전혀 바뀌지 않습니다.

그래서 처음 시험할 때는 플레이 중에 Tick이 실행되는지, 계산한 시각 h가 실제로 바뀌는지, 광원 참조가 유효한지를 이 순서로 확인합니다. 시계를 갱신하는 부분과 각 효과에 적용하는 부분을 나눠 두면, 광원 참조가 빠진 오류를 찾으면서도 시간 계산은 따로 확인할 수 있습니다.

게임 시계 만들기

현재 시각은 CurrentTimeInHours 같은 Float 변수로 두고, 0.0 이상 24.0 미만의 값으로 표현합니다. 이 값을 Event Tick에서 매 프레임 늘립니다. 이때 Tick이 넘겨주는 Delta Seconds, 즉 지난 프레임부터 흐른 시간을 쓰면 프레임 속도와 상관없이 시간이 일정하게 흐릅니다.

시간이 흐르는 빠르기는 TimeScale이라는 게임 시간의 배율로 정합니다. 월드의 시간 배율이 1이고 일시정지하지 않았다면, 1.0은 현실과 같은 속도이고 60.0은 그 60배입니다. 그러면 시각은 다음과 같이 갱신됩니다.

CurrentTimeInHours = FMOD(CurrentTimeInHours + DeltaSeconds * TimeScale / 3600.0, 24.0)

DeltaSeconds * TimeScale은 이번 프레임에 흐른 게임 시간을 초 단위로 나타내고, 3600으로 나누면 시간 단위가 됩니다. FMOD는 나머지를 구하는 함수라서, 24를 넘은 값을 다시 0부터 시작하게 해 하루가 반복되게 합니다. 이 식은 처음 시각이 0 이상 24 미만이고 TimeScale이 0 이상이라는 조건을 전제로 합니다.

같은 시계를 모두가 읽게 하기

초 단위 입력을 공통 게임 시계로 변환

본문의 시각 23.99 h/Δt=1 s/배율 72에서 증가 0.02 h→24.01 h→순환 0.01 h→공통 소비자 조회를 추적할 수 있다.

TIME OF DAY · ONE CLOCK

초를 시간으로 바꾸고 모든 효과가 같은 시계를 읽습니다

프레임 경과시간에 배율을 곱하고 3600으로 나눠 게임 시각에 더합니다. 광원 회전과 각 커브는 24시간으로 순환하는 같은 h를 사용합니다.

공통 게임 시계의 단위와 사용처Delta Seconds에 시간 배율을 곱하고 3600으로 나눠 시간 단위로 누적한다. 24시간으로 순환하는 게임 시각 h를 광원 회전, 강도와 색 커브, 후처리 커브가 함께 읽는다.프레임 시간 [초]Δt × 시간 배율게임 시각 h [시]0 ≤ h < 24÷ 3600태양 회전h로 각도 계산강도·색 커브h로 값 읽기후처리 커브h로 값 읽기
공통 게임 시계의 단위와 세 사용처프레임 시간 초에 배율을 곱하고3600으로 나누어 게임 시각h에 누적한다.24시간으로 순환하는 동일한h가 태양 회전, 강도·색 커브, 후처리 커브로 각각 전달된다. 세 소비자를 서로 연결한 실행순서가 아니다.프레임 시간 [초]Δt × 시간 배율÷ 3600후 누적게임 시각 h [시]0 ≤ h < 24태양 회전h로 각도 계산강도·색 커브h로 값 읽기후처리 커브h로 값 읽기

게임 시각 갱신

h ← FMOD(h + Δt × TimeScale / 3600, 24)

초기 0 ≤ h < 24, TimeScale ≥ 0인 예제입니다. 커브의 0시·24시 끝값을 연결하고, Sky Light 캡처 갱신 정책은 별도로 정합니다.

이 예의 조건

  • 본문과 원 도표에 제시된 사례의 입력·조건을 사용합니다. 설명용 수치나 독립 비교 경로는 각 단계에 표시합니다.
  • 현재 상태·값과 전환 근거의 위치는 모든 단계에서 같습니다. 조건이 맞지 않거나 실행되지 않은 경로를 성공한 동작으로 해석하지 않습니다.
관계도의 보충 설명
원 도표의 제목·노드·관계·조건·주석
범위원문
관계·조건 설명초 단위의 프레임 시간에 배율을 곱하고 3600으로 나누어 게임 시각 h에 누적한다. 24시간으로 순환하는 동일한 h가 태양 회전, 강도·색 커브, 후처리 커브로 각각 전달된다. 세 소비자를 서로 연결한 실행 순서가 아니다.
예의 값과 변화
관찰·상황값·보충 정보변화·이유
시계 입력

공통 게임 시계를 23.99시간에서 시작한다.

  • 시각: 23.99 h
  • Δt / 배율: 1 s / 72
  • 시간 단위가 초와 시간으로 나뉘므로 3600 변환이 필요하다.
증가·순환

0.02시간을 더한 24.01을 하루 범위로 순환한다.

  • 증가: 1*72/3600=0.02 h
  • 순환 결과: 0.01 h
  • 24시간 경계를 넘어도 공통 값은 같은 하루 범위에 놓인다.
공통 소비

각 소비자가 같은 시계값을 읽는다.

  • 조회 시각: 0.01 h
  • 원천: 공통 게임 시계
  • 태양·UI 등이 각자의 시계를 누적하지 않고 같은 값을 소비한다.

초 단위 입력을 공통 게임 시계로 변환

본문의 시각 23.99 h/Δt=1 s/배율 72에서 증가 0.02 h→24.01 h→순환 0.01 h→공통 소비자 조회를 추적할 수 있다.

TIME OF DAY · ONE CLOCK

초를 시간으로 바꾸고 모든 효과가 같은 시계를 읽습니다

프레임 경과시간에 배율을 곱하고 3600으로 나눠 게임 시각에 더합니다. 광원 회전과 각 커브는 24시간으로 순환하는 같은 h를 사용합니다.

공통 게임 시계의 단위와 사용처Delta Seconds에 시간 배율을 곱하고 3600으로 나눠 시간 단위로 누적한다. 24시간으로 순환하는 게임 시각 h를 광원 회전, 강도와 색 커브, 후처리 커브가 함께 읽는다.프레임 시간 [초]Δt × 시간 배율게임 시각 h [시]0 ≤ h < 24÷ 3600태양 회전h로 각도 계산강도·색 커브h로 값 읽기후처리 커브h로 값 읽기
공통 게임 시계의 단위와 세 사용처프레임 시간 초에 배율을 곱하고3600으로 나누어 게임 시각h에 누적한다.24시간으로 순환하는 동일한h가 태양 회전, 강도·색 커브, 후처리 커브로 각각 전달된다. 세 소비자를 서로 연결한 실행순서가 아니다.프레임 시간 [초]Δt × 시간 배율÷ 3600후 누적게임 시각 h [시]0 ≤ h < 24태양 회전h로 각도 계산강도·색 커브h로 값 읽기후처리 커브h로 값 읽기

게임 시각 갱신

h ← FMOD(h + Δt × TimeScale / 3600, 24)

초기 0 ≤ h < 24, TimeScale ≥ 0인 예제입니다. 커브의 0시·24시 끝값을 연결하고, Sky Light 캡처 갱신 정책은 별도로 정합니다.

이 예의 조건

  • 본문과 원 도표에 제시된 사례의 입력·조건을 사용합니다. 설명용 수치나 독립 비교 경로는 각 단계에 표시합니다.
  • 현재 상태·값과 전환 근거의 위치는 모든 단계에서 같습니다. 조건이 맞지 않거나 실행되지 않은 경로를 성공한 동작으로 해석하지 않습니다.

관계도의 보충 설명

원 도표의 제목·노드·관계·조건·주석
범위원문
관계·조건 설명초 단위의 프레임 시간에 배율을 곱하고 3600으로 나누어 게임 시각 h에 누적한다. 24시간으로 순환하는 동일한 h가 태양 회전, 강도·색 커브, 후처리 커브로 각각 전달된다. 세 소비자를 서로 연결한 실행 순서가 아니다.
예의 값과 변화
관찰·상황값·보충 정보변화·이유
시계 입력

공통 게임 시계를 23.99시간에서 시작한다.

  • 시각: 23.99 h
  • Δt / 배율: 1 s / 72
  • 시간 단위가 초와 시간으로 나뉘므로 3600 변환이 필요하다.
증가·순환

0.02시간을 더한 24.01을 하루 범위로 순환한다.

  • 증가: 1*72/3600=0.02 h
  • 순환 결과: 0.01 h
  • 24시간 경계를 넘어도 공통 값은 같은 하루 범위에 놓인다.
공통 소비

각 소비자가 같은 시계값을 읽는다.

  • 조회 시각: 0.01 h
  • 원천: 공통 게임 시계
  • 태양·UI 등이 각자의 시계를 누적하지 않고 같은 값을 소비한다.

위 그림은 이 시계의 단위와 쓰는 곳을 보여 줍니다. 왼쪽에서 프레임 시간을 초 단위로 받아 시간 배율을 곱하고, 3600으로 나눠 시간 단위로 바꾼 뒤 게임 시각 h에 더합니다. h는 24시간으로 순환하므로 늘 0 이상 24 미만입니다. 오른쪽에서는 이 하나의 h를 태양 회전과 강도와 색 커브, 후처리 커브가 각각 읽습니다. 그림의 설명처럼 세 곳은 서로 이어진 실행 순서가 아니라, 같은 시계를 따로 읽는 사용처입니다.

단계 그림은 자정을 넘는 순간을 두 장면으로 따라갑니다. 첫 장면에서 게임 시계는 23.99시입니다. 다음 장면에서 0.02시간이 더해져 24.01이 되고, 이 값을 24로 순환시키면 0.01시가 됩니다. 그림 아래의 주석은 이 예가 처음 h가 범위 안에 있고 배율이 음수가 아닌 경우라는 점, 커브의 0시와 24시 끝값을 이어야 한다는 점, 스카이 라이트의 캡처 갱신은 따로 정한다는 점을 짚습니다.

이렇게 시간을 쌓는 일과 화면에 적용하는 일을 나눠 두면, 시간 배율을 바꿔도 모든 효과가 같은 시계를 읽으므로 서로 어긋나지 않습니다.

태양 회전 계산하기

CurrentTimeInHours에 따라 디렉셔널 라이트의 회전을 계산해 적용합니다. 해의 높이는 주로 Pitch가 정하므로, 하루 동안 Pitch가 어떻게 바뀔지 규칙을 정합니다. 교육용으로는 24시간에 360도를 도는 Pitch = -15 * (CurrentTimeInHours - 6) 같은 규칙을 쓸 수 있습니다. 한 시간에 15도씩 돌기 때문입니다. 이 규칙에서 6시에는 Pitch가 0이라 빛이 지평선과 나란하고, 12시에는 -90이라 빛이 바로 위에서 내리쬐며, 18시에는 -180이라 반대쪽 지평선에 닿고, 0시에는 90이라 빛이 땅 아래에서 위를 향하는 밤이 됩니다.

이 값을 적용한 뒤에는 고정한 Yaw와 엔진에서 광원이 가리키는 방향을 확인하며, 6시와 12시, 18시의 그림자를 직접 점검합니다. 이 규칙은 실제 천문 계산이 아니라 교육용 단순화입니다. 반대로 0부터 24까지의 시각을 -90부터 90까지의 각도에 그대로 늘여 대응시키면, 24시의 각도와 0시의 각도가 서로 달라 자정에 해가 갑자기 건너뛰게 됩니다.

회전은 월드 기준의 Set Actor Rotation으로 적용합니다. 디렉셔널 라이트를 다른 액터에 붙여 상대 회전을 쓴다면, 부모의 변환도 계산에 넣어야 합니다.

조명과 후처리 값 바꾸기

디렉셔널 라이트와 스카이 라이트의 Intensity와 Light Color도 CurrentTimeInHours에 맞춰 서서히 바꿉니다. 이때는 Curve Float나 Curve Linear Color 에셋을 만들어 CurrentTimeInHours로 값을 읽는 방법이 시계와 어긋나지 않습니다. 예를 들어 강도 커브는 낮에 높고 밤에 낮게, 색 커브는 해 질 녘에 주황색이나 붉은색이 되게 그립니다. 커브의 0시 값과 24시 값은 같게 맞춰, 자정에 값이 튀지 않게 합니다.

타임라인(Timeline)을 쓸 수도 있지만, 그때는 타임라인의 재생 위치를 같은 게임 시각에 맞춰야 합니다. 타임라인을 따로 재생하면 게임 시계와 어긋날 수 있기 때문입니다. 후처리 볼륨의 노출과 색 보정도 같은 방법으로 CurrentTimeInHours에 따라 바꾸며, 값을 넣을 때는 Set Members in PostProcessSettings 노드를 씁니다.


하루 길이와 엔진의 시간 배율 구분하기

현실의 D초 동안 게임의 하루가 지나게 하려면, 월드 시간 배율이 1이고 일시정지가 없다는 이 예의 조건에서 TimeScale = 24 × 3600 / D입니다. 하루는 86400초이기 때문입니다. 예를 들어 현실 20분, 즉 D = 1200초에 하루를 보내려면 배율은 86400 / 1200 = 72이고, 현실 1초마다 게임의 72초가 흐릅니다. TimeScale이 하루를 끝내는 데 걸리는 초 수를 뜻하는 것이 아니라는 점에 주의합니다.

또 하나 구분할 것은 엔진의 시간 배율입니다. Tick으로 들어오는 Delta Seconds는 이미 엔진의 시간 배율이 반영된 값일 수 있습니다. 예를 들어 게임 전체를 느리게 하는 슬로모션이 걸려 있다면 Delta Seconds도 그만큼 작아져 들어옵니다. 이렇게 이미 조정된 Delta Seconds에 같은 배율을 다시 곱하면 슬로모션이 두 번 적용됩니다. 전달된 Delta Seconds가 이미 0.5이고 TimeScale이 60이라면 증가분은 0.5 × 60 = 30 게임초이고, 그 0.5를 만든 월드 배율을 다시 곱하지 않습니다.

일시정지 중에도 시계를 움직이고 싶거나, 슬로모션과 상관없는 시계를 원한다면 Tick이 그때도 호출되는지와 어떤 시간 기준을 쓸지를 함께 정해야 합니다. 입력 값의 단위와 의미는 UWorld의 Delta Seconds 설명에서 확인합니다.

계산으로 확인해 봅니다. 월드 시간 배율이 1이고 일시정지가 없으며 TimeScale이 60이라고 합시다. Delta Seconds가 0.5초라면 게임 시간의 증가분은 0.5 × 60 ÷ 3600 = 1/120시간, 즉 게임 시간 30초입니다. 같은 현실 1초를 0.5초짜리 두 프레임으로 처리하든 0.25초짜리 네 프레임으로 처리하든, 쌓이는 증가분은 게임 시간 1분으로 같습니다. 이렇게 시계 계산이 맞는지 먼저 확인한 뒤 태양 회전에 연결하면, 시계 계산의 문제와 광원 방향의 문제를 따로 찾을 수 있습니다.


시간 이벤트와 프리셋 이동

같은 값이 아니라 지나간 구간으로 판단하기

시간 배율이 빠르면 한 Tick 사이에 h가 17.9에서 18.1로 건너뛸 수 있습니다. 이때 h == 18인지만 검사하면, h가 정확히 18인 프레임이 없으므로 일몰 이벤트를 놓칩니다. 그래서 이벤트는 같은 값인지가 아니라, 그 시각을 지나갔는지로 판단합니다.

시간이 거꾸로 가지 않는다는 조건에서, 순환하지 않고 계속 쌓이는 누적 게임 시간 H를 따로 둡니다. 화면에 쓰는 h는 FMOD(H, 24)로 계산하고, 이벤트는 이전 H < 이벤트 시각 ≤ 새 H인 구간이 있는지로 찾습니다. 매일 자정은 H = 24, 48, 72처럼 반복되므로, 여러 날을 한 번에 건너뛴 경우도 몇 번 지나갔는지 구별할 수 있습니다. 이 누적 H와 구간 검사는 앞의 순환 시계 식에 더하는 설계입니다.

자정을 넘는 프리셋 이동

일출, 정오, 일몰, 자정처럼 자주 쓰는 시간대는 Sunrise, Noon, Sunset, Midnight 같은 프리셋으로 만들어 두고, Set Time Of Day 같은 커스텀 이벤트로 그 시각으로 바로 옮기거나 부드럽게 보간(Lerp)하게 할 수 있습니다.

프리셋 사이를 보간할 때는 어느 쪽으로 움직일지 의도를 정해야 합니다. 23.8시에서 0.2시로 앞으로 짧게 보간하고 싶다면, 끝점을 24.2로 잡아 23.8에서 24.2로 보간하고 화면에 보일 때만 24로 순환시킵니다. 두 표시값 23.8과 0.2를 그대로 숫자로 보간하면 그 중간이 12시가 되어, 한밤중에 잠깐 정오가 지나가는 이상한 결과가 됩니다. 또 수동으로 프리셋을 옮길 때 그 사이에 지나친 게임 이벤트를 실행할지, 조명만 바로 적용할지도 따로 정합니다.


날씨 연동과 성능

BP_TimeOfDaySystem과 날씨 시스템 설정에서 만든 BP_WeatherSystem이 서로 참조하게 하면, 날씨에 따라 시간 시스템의 변화를 바꿀 수 있습니다. 예를 들어 비 오는 날에는 같은 시각이라도 하늘을 더 어둡게 하고 노출을 낮춥니다.

성능도 함께 생각합니다. 회전하는 디렉셔널 라이트는 Movable이어야 하지만, 나머지 광원은 실제로 바뀌는 것과 글로벌 일루미네이션 경로에 맞춰 고르고, 그림자의 범위와 빛을 받는 메시 수를 측정합니다. 스카이 라이트의 Real Time Capture는 기본적으로 시간 분할(time slicing)로 작업을 여러 프레임에 나눠 할 수 있으므로, 매 프레임 큐브맵 전체를 다시 계산한다고 단정하지 말고 캡처 해상도와 실제 비용을 확인합니다. 볼류메트릭 클라우드의 품질과 후처리 볼륨의 무거운 효과도 성능에 영향을 주므로, 목표 플랫폼에 맞춰 조절합니다.

마지막으로 일출, 정오, 일몰, 자정 프리셋으로 이동해 보며 광원의 방향과 하늘의 갱신, 노출이 같은 시각에 맞는지 확인합니다. 자정 전후에서 커브의 끝값이 이어지는지, 빠른 배율에서 시간 이벤트가 빠지지 않는지도 함께 점검합니다. 같은 게임 시계로 광원 회전과 커브, 후처리를 제어하되, 하늘 캡처를 얼마나 자주 갱신할지는 따로 정합니다.


정리

  • 시간 시스템은 디렉셔널 라이트의 회전과 강도, 스카이 라이트의 캡처, 스카이 어트모스피어와 구름, 후처리의 노출과 색 보정을 함께 바꿉니다.
  • 회전하는 디렉셔널 라이트는 Movable이어야 하고, 스카이 어트모스피어는 디렉셔널 라이트의 Atmosphere Sun Light로 해의 방향을 받습니다.
  • 참조 변수는 Instance Editable로 노출해 레벨의 실제 액터를 지정해야 하며, Tick, 시각 h, 참조의 순서로 문제를 확인합니다.
  • 게임 시각은 FMOD(h + DeltaSeconds * TimeScale / 3600, 24)로 갱신하고, 태양 회전과 커브, 후처리가 모두 이 하나의 h를 읽습니다.
  • 현실 D초에 하루를 보내는 배율은 86400 / D이고, 이미 엔진 배율이 반영된 Delta Seconds에 같은 배율을 다시 곱하지 않습니다.
  • Pitch = -15 * (h - 6)처럼 24시간에 360도를 도는 규칙은 자정에 이어지고, 커브의 0시와 24시 끝값도 같게 맞춥니다.
  • 시간 이벤트는 누적 시간 H가 그 시각을 지나갔는지로 판단하고, 자정을 넘는 보간은 끝점을 24보다 크게 잡아 앞으로 보간합니다.

연습 문제

1. 하루 20분과 자정 넘기기

월드 시간 배율이 1이고 일시정지가 없는 조건에서, 현실 20분에 게임의 하루를 보내려고 합니다. 필요한 TimeScale은 얼마이고, Delta Seconds의 합이 2초인 여러 Tick 동안 게임 시간은 얼마나 늘어날까요? 또 CurrentTimeInHours가 23.99일 때 Delta Seconds가 1초라면 다음 게임 시각은 몇 시일까요?

해설 보기

TimeScale은 86400 / 1200 = 72입니다. Delta Seconds의 합이 2초라면 2 × 72 = 144 게임초, 즉 144 / 3600 = 0.04시간이 늘어납니다. 각 Tick의 Delta Seconds를 하나씩 더해도 결과는 같습니다.

23.99시에서 1초 동안의 증가분은 72 / 3600 = 0.02시간이므로 23.99 + 0.02 = 24.01이 되고, 이것을 24로 순환시키면 0.01시가 됩니다. 0.01시간은 36초이므로 자정을 막 넘은 0시 0분 36초입니다. 이 계산은 처음 시각이 범위 안에 있고 배율이 음수가 아닌 경우를 전제로 합니다.

2. 배율을 두 번 곱하면

Tick으로 들어온 Delta Seconds가 0.5이고, 이 값에는 이미 엔진의 시간 배율 0.5가 반영되어 있습니다. TimeScale은 60입니다. 원래 식대로 계산한 증가분은 얼마이고, 식에 월드 배율 0.5를 한 번 더 곱하면 얼마가 될까요?

해설 보기

원래 식의 증가분은 0.5 × 60 = 30 게임초입니다. 월드 배율 0.5를 한 번 더 곱하면 15 게임초만 늘어납니다.

즉 Delta Seconds에 이미 반영된 슬로모션을 한 번 더 적용해, 시계가 의도보다 두 배 느리게 흐르게 됩니다. 이 문제는 전달된 Delta Seconds가 이미 엔진 배율을 반영한 값이라는 조건을 명시한 것이므로, 실제 프로젝트에서도 쓰는 Delta Seconds가 어떤 시간 기준인지 먼저 확인합니다.

3. 여러 번의 자정

시간이 거꾸로 가지 않는 조건에서, 누적 게임 시간 H가 한 번의 갱신으로 23.9에서 49.2로 늘었습니다. 그 구간에서 매일 자정 이벤트를 모두 실행하기로 했습니다. 이벤트는 몇 번 실행되고, 지나간 자정은 언제이며, 화면에 쓰는 h는 몇 시일까요? 순환한 표시값 두 개만 저장했다면 같은 계산을 할 수 있을까요?

해설 보기

23.9 < 자정 ≤ 49.2에 드는 자정은 H = 24와 48이므로 이벤트는 두 번 실행됩니다. 화면에 쓰는 h는 49.2를 24로 순환시킨 1.2시입니다.

순환한 값인 23.9와 1.2만 저장했다면, 그 사이에 자정을 한 번 지났는지 두 번 지났는지 알 수 없습니다. 순환하면서 몇 날이 지났는지에 대한 정보가 사라지기 때문입니다. 그래서 이벤트 판단에는 순환하지 않는 누적 H를 따로 둡니다.

4. 23.8시에서 0.2시로

23.8시에서 0.2시로 가는 프리셋 이동을, 앞으로 가장 짧게 움직이도록 정했습니다. 선형 보간의 비율이 0.5일 때 순환하기 전의 H와 화면에 보이는 시각은 몇 시일까요? 두 표시값을 그대로 보간하면 어떻게 될까요?

해설 보기

앞으로 짧게 가려면 끝점을 24.2로 잡으므로, 비율 0.5에서 H는 (23.8 + 24.2) / 2 = 24.0이고, 화면에 보이는 시각은 24를 순환시킨 0시입니다.

23.8과 0.2를 그대로 보간하면 중간이 (23.8 + 0.2) / 2 = 12시가 되어, 자정 근처에서 갑자기 정오를 지나가게 됩니다. 이것은 이 문제에서 정한 앞으로 짧게 움직이는 경로와 다릅니다.

다음 글 날씨 및 시간 효과 적용에서는 앞에서 만든 날씨 시스템과 시간 시스템을 함께 묶어, 시간대와 날씨에 따라 조명과 대기, 재질이 함께 바뀌도록 적용하는 방법을 다룹니다.