BulletAnt 개발일지 (20) - 태양 회전 알고리즘

김펭귄·6일 전

Today What I Learned (TIL)

목록 보기
135/139

SunManager를 통한 낮/밤 및 태양 궤적 시스템 구현

  • 낮에는 준비 시간 (채굴, 맵 파밍, 건축 등)
  • 밤에는 웨이브 진행 (몹 스폰, 디펜스)

시간에 따라 레벨의 Directional Light를 제어하는 SunManager를 별도로 구현했다.

SunManager 설계

처음엔 이것도 Subsystem으로 만들까 고민했지만,
결국 레벨에 배치된 Directional Light 하나만 관리하면 되었기 때문에
그냥 액터 기반 SunManager로 구현했다.
레벨에 배치된 빛을 참조해서 회전만 제어하는 구조다.

단순히 Pitch만 돌리면:

  • 항상 같은 위치에서 해가 뜨고 (정동->남->정서)
  • 밤/낮 길이 변화도 없고 (1:1 비율)
  • 태양 궤적도 부자연스럽다.

그래서 실제 지구처럼:

  • 자전축 기울기
  • 낮/밤 시간 비율 (대략 3:1)

에 따라 태양의 일출 지점과 경로가 달라지도록 공식을 유도했다.

핵심 아이디어

게임 시작 전 결정되는 값은:

  • SunTiltAngle (자전축)
  • 낮 / 밤 시간

뿐이다.

즉, 밸런싱 과정에서 낮/밤의 비율이 바뀌어도, 자동으로 자연스러운 태양 궤적이 다시 계산되도록 만들고 싶었다.

그래서:

  • 시간 당 회전하는 태양의 각도 (RotationPerMSec)
  • 밤 시간 동안 태양이 이동해야 하는 각도 (NightAngle)
  • 일출 위치 (w)

를 런타임에 계산하도록 구현했다.

삼각함수를 이용해 낮/밤 비율에 맞는 일출·일몰 지점의 각도(w)를 유도했다.

void ASunManager::SetSunInitRotator_Implementation(int32 InInitWaveTime)
{
    RotationPerMSec = 360 * 0.1f / TotalWaveTime;

    float SafeAlpha = FMath::Clamp(NightAngle, 0.001f, 179.9f);
    float SafeTheta = FMath::Clamp(SunTiltAngle, 0.001f, 179.f);

    float AlphaRad = FMath::DegreesToRadians(SafeAlpha);
    float ThetaRad = FMath::DegreesToRadians(SafeTheta);

    float TanPart = FMath::Tan(AlphaRad / 2.0f);
    float SinPart = FMath::Sin(ThetaRad);

    float W_Rad = FMath::Atan(TanPart * SinPart);
    float W_Deg = FMath::RadiansToDegrees(W_Rad);

    SunRise = FRotator(0, -(180 - W_Deg), 0);		// 언리얼에서의 동서남북을 맞추기 위한 각도 수정
    Sun->SetActorRotation(SunRise);
}

태양 회전 방식

태양은 단순 Rotator가 아니라 Quaternion 기반으로 회전시켰다.

이유는:

  • Rotator는 장시간 회전 시 지글거림 발생
  • 짐벌락 발생 가능
  • “자전축 기준 회전”이라는 개념 자체가 Quaternion과 잘 맞음

이었기 때문이다.

그래서:

회전축 = 자전축
회전각 = (360 / 하루시간) * DeltaTime

형태로 Timer로 회전시켰다.

void ASunManager::RotateSun()
{
    FRotator SunRotator = Sun->GetActorRotation();
    FRotator BaseRotator(SunRotator);
    FQuat QBase = BaseRotator.Quaternion();

    FQuat QDelta = FQuat(CustomAxis, FMath::DegreesToRadians(RotationPerMSec));

    FQuat QResult = QDelta * QBase;
    Sun->SetActorRotation(QResult);
}

태양 위치 동기화

시간은 GameState에서 복제되고 있으며,
클라이언트는 OnRep 시 현재 태양 위치를 다시 보정한다.

그리고 평소에는 0.1초 단위로 로컬에서 계속 회전시켜 자연스럽게 움직이게 했다.

즉:

  • 평소엔 클라가 자연스럽게 보간
  • 서버 시간 기준으로 동기화

하는 구조다.

결과

최종적으로:

  • 낮/밤 흐름이 자연스럽게 연결되고
  • 웨이브 분위기도 살아났고
  • 밸런스로 인해 시간이 바뀌어도 자동 대응 가능하며
  • 태양이 실제 낮/밤 비율에 알맞게 천체처럼 회전하는 시스템

을 만들 수 있었다.

profile
반갑습니다

0개의 댓글