본문 바로가기
작심삼일/The Cosmic Robot

설날 프로젝트 - 행성과 중력

by water_beetle 2025. 1. 27.

중력 구현 영상

디버깅한 Gravity Field

 

1. 중력을 어떻게 구현할 것인가

중력을 어떻게 구현할지 어제 자면서 고민해봤는데,

제일 먼저 한 생각은 고등학교 물리 시간에 배운 만류인력의 법칙을 적용해볼까 생각이 들었는데

그러면 질량도 추가하고, 중력에 영향을 받을, 중력에 영향을 끼칠 액터들을 고려.. 등등 좀 생각할게 많은 것 같아 패스 하였다.

 

그래서 또 뭐다?

-> 구글링

유튜브에서 몇개 영상을 봤는데 Gravity Field를 사용해서 구현하는 아이디어가 정말 멋졌다.

설명도 잘되어 있고, 구현하기도 생각보다 어렵지 않을 것 같아 마음에 들었다.

 

https://www.youtube.com/watch?v=x_5pxFtDMMI

 

 

 

영상의 한 부분을 캡쳐한 이미지인데, Gravity Field를 추가한 뒤, 물체가 해당 구역에 들어옴/나감에 따라 Gravity Field List에 추가/삭제하고, Gravity Field의 설정 방향으로 잡아 당기는 방법이다.

 

뭔가 글을 쓰면서도 다시 읽어보니까 설명이 좀 이상한데 유튜브 영상과 Github 코드를 보면 쉽게 이해할 수 있을 것이다.

 

2. 중력에 따른 Controller Rotation 처리

유튜브에 적혀있는 Github에 들어가 코드를 읽어보고, 언리얼에서 구현해보자... 마음먹고 또 어떻게 구현할지 찾아보는데

Unreal 5.4 부터는 Gravity 방향을 설정할 수 있는 기능이 추가되었다!!!!

 

이전에 사람들이 올린 내용들을 좀 찾아보니까 Character의 Gravity 설정을 끄고, Custom으로 중력을 구현했는데

이제는 Gravity 기능을 끌 필요 없이, Gravity 방향만 설정하면 쉽게 중력을 구현할 수 있을 것 같았다.

 

대충 Gravity 방향만 딸깍하고 Character 클래스에 아래와 같이 코드를 수정했더니...

하나도 제대로 되지 않았다.

GetCharacterMovement()->SetGravityDirection(GravityDir);

 

구글링하다가 아래 링크를 봤는데, 감탄만 나왔다.

https://dev.epicgames.com/community/learning/tutorials/w6l7/unreal-engine-custom-gravity-in-ue-5-4

 

난 그냥 중력 방향만 설정하면 되는 줄 알았는데, 

중력방향이 바뀜에 따라, Control Rotation의 방향 처리를 해줘야 했었다.

구현한 코드를 읽으면서 Rotation을 왜 두 번 변환하는지 이해가 안돼 GPT의 도움을 빌리니

이해가 한번에 됬다.

 

****Chat GPT의 설명****

1. 문제를 비유로 설명하기

상황:

  • 당신은 공원을 걷고 있고, 손에 **컴퍼스(나침반)**를 들고 있습니다.
  • 이 컴퍼스는 항상 북쪽을 가리키도록 만들어졌어요.
  • 하지만 갑자기 중력이 바뀌어서 아래 방향(발 밑)이 달라진다고 상상해 봅시다:
    • 원래 아래쪽은 땅(지구) 방향이었는데, 이제는 오른쪽 벽(수평 방향)이 아래가 됐다고 해요.

문제:

  • 컴퍼스는 "북쪽"을 가리키고 있어야 하지만, 중력이 바뀌면서 당신의 머리가 갑자기 이상한 각도로 돌아가거나 왜곡될 수 있습니다.
  • 그래서 이 상황에서 중력의 새로운 방향을 기준으로 컴퍼스가 올바르게 보정된 방향을 가리키도록 다시 정렬해 줘야 합니다.

2. DeltaGravityRotation이 하는 일

  • 중력이 "발 밑 → 오른쪽 벽"으로 바뀌었다고 해봅시다.
  • DeltaGravityRotation은 이 **중력 변화량(발 밑 → 벽)**을 계산해요.
  • 이 값은 **"당신의 머리를 중력 변화에 맞게 부드럽게 돌려주는 역할"**을 합니다.
  • 하지만! 이 작업만으로는 문제가 다 해결되지 않아요.

3. 왜 GetGravityRelativeRotation이 필요한가?

중력 변화가 적용된 후 문제:

  • 중력이 오른쪽 벽을 아래로 바꾸는 동안, 컴퍼스가 "북쪽"을 여전히 올바르게 가리키는지 확인해 봅시다.
  • 문제는:
    • 이제 오른쪽 벽이 아래가 되었기 때문에, 기존 좌표계(월드 좌표계)의 "북쪽"은 더 이상 올바른 북쪽이 아닐 수 있어요.
    • 즉, 중력 방향이 바뀌면서 "내가 보던 방향"이 이상한 방식으로 뒤틀리거나 왜곡될 수 있어요.

해결 방법:

  • **GetGravityRelativeRotation**은 이렇게 생각해요:
    • "좋아, 중력이 바뀌었으니, 새로운 중력 방향을 아래로 삼고, 컴퍼스가 다시 올바르게 북쪽을 가리키게 해 줄게!"
  • 이 함수는 새로운 중력 방향(예: 오른쪽 벽)을 기준으로, 기존의 회전을 새로운 중력 좌표계로 변환해 줍니다.
  • 결과적으로 컴퍼스는 중력이 바뀌더라도 올바른 방향을 계속 유지합니다.

4. 비유로 요약

  • DeltaGravityRotation: 중력 방향이 바뀌었으니, "머리를 돌려" 새 중력 방향에 맞추는 과정입니다.
  • GetGravityRelativeRotation: 머리를 돌렸으니, "중력 방향이 바뀐 새로운 세상(좌표계)"에서 올바른 방향으로 다시 정렬합니다.

5. 간단한 예제

비포(원래 중력):

  • 중력 방향: 발 밑((0, 0, -1)).
  • 카메라가 "북쪽"을 바라보고 있습니다.

중력 변화(DeltaGravityRotation):

  • 중력이 오른쪽 벽((1, 0, 0))으로 바뀌었어요.
  • DeltaGravityRotation이 카메라를 오른쪽으로 회전시켜 중력 방향을 따라가도록 만듭니다.

결과(왜곡 방지):

  • 하지만 중력 변화만 적용하면, 카메라가 새로운 중력 방향에 따라 왜곡된 방향을 가리킬 수 있습니다.
  • GetGravityRelativeRotation이 이 왜곡을 정리하고, 새로운 중력 방향에서 올바른 "북쪽"을 가리키도록 보정합니다.

6. 초간단 요약

  1. 중력이 바뀌면 DeltaGravityRotation으로 "머리를 돌립니다".
  2. 머리가 돌아갔으니, GetGravityRelativeRotation으로 **"새로운 중력 기준에 맞춰 방향을 다시 정렬"**합니다.

 

 

3. 중력장 구현

중력에 따른 Controller Rotation 처리, 중력 방향 처리는 다 끝냈고,

앞부분의 유튜브 내용을 참고해서 중력장을 구현하였다.

 

GravityField.cpp

행성에 부착되어 사용되어 중력장을 관리하는 GravityField 컴포넌트를 구현하였다.

중력장에 캐릭터가 들어오면 중력 영향을 받는 리스트에 추가, 나가면 삭제처리 되도록 구현하였다.

 
void UGravityField::OnEnterGravityArea(UPrimitiveComponent* OverlappedComp, AActor* OtherActor,
                                       UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
    if (OtherActor && OtherActor->IsA(ASpaceCharacter::StaticClass()))
    {
       ASpaceCharacter* SpaceCharacter = Cast<ASpaceCharacter>(OtherActor);
       if (SpaceCharacter)
       {
          SpaceCharacter-> GetGravityBody()->AddGravityArea(this);
       }
    }
}

void UGravityField::OnExitGravityArea(UPrimitiveComponent* OverlappedComp, AActor* OtherActor,
    UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
    if (OtherActor && OtherActor->IsA(ASpaceCharacter::StaticClass()))
    {
       ASpaceCharacter* SpaceCharacter = Cast<ASpaceCharacter>(OtherActor);
       if (SpaceCharacter)
       {
          SpaceCharacter-> GetGravityBody()->RemoveGravityArea(this);
       }
    }
}

 

가끔 게임을 시작할 때 캐릭터가 중력장 안에서 시작할 때가 있어 BeginOverlap 이벤트가 발생하지 않아 중력이 작용되지 않아 Begin Play에서 GetOverlappingActors로 Overlap된 캐릭터를 찾아 중력을 적용하려 했는데

모든 Actor들이 초기화 되지 않아 GetOverlappingActors가 잘 작동하지 않았다.

그래서 타이머를 넣어 Begin Play 시작 후 0.1초 후에 GetOverlappingActors를 실행하도록 하니 중력이 잘 적용되었다.

 

GravityBody.cpp

Character에 부착되어 사용되는 GravityBody를 구현하였다.

영향을 주는 GravityField를 저장하고, 우선순위가 가장 높은 중력장으로 중력 방향을 설정한다.

void UGravityBody::AddGravityArea(UGravityField* GravityField)
{
	GravityFields.Add(GravityField);
	GravityFields.Sort([](const UGravityField& A, const UGravityField& B)
	{
		return A.Priority < B.Priority;
	});

	if(ACharacter* GravityCharacter = Cast<ACharacter>(GetOwner()))
	{
		GravityCharacter->GetCharacterMovement()->GravityScale = GravityFields.Last()->GravityScale;
	}
}
FVector UGravityBody::GetGravityDirection()
{
    // GravityFields가 0일 때는 Gravity Scale이 0이라 아마 영향 없을듯
    // Zero Vector로 설정하니까 set gravity에서 ensure에 걸림
    if (GravityFields.Num() == 0) return FVector::ForwardVector;

    // 가장 우선순위 높은 중력 영역의 방향 반환
    return GravityFields.Last()->GetGravityDirection(this);
}

 

SpaceCharacter.cpp

매 프레임마다 GravityBody에서 중력 방향을 가져와 설정한다.

void ASpaceCharacter::Tick(float DeltaSeconds)
{
    Super::Tick(DeltaSeconds);

    FVector TargetGravityDir = GravityBody->GetGravityDirection();

    // 현재 중력 방향에서 목표 중력 방향으로 점진적으로 보간
    const float GravityChangeSpeed = 2.0f; // 중력 방향 변화 속도 (값이 클수록 빠르게 반응)
    GravityDir = FMath::VInterpTo(GravityDir, TargetGravityDir, DeltaSeconds, GravityChangeSpeed);
    
    GetCharacterMovement()->SetGravityDirection(GravityDir);
}

 

처음에는 Interp하지 않고 중력 방향을 설정했더니 캐릭터가 무중력 -> 중력으로 갈 때 휙휙 돌아갔다.

처음에는 중력 방향 문제가 아니라 Controller Rotation 에서 보간을 안해서 문제가 발생한것 같아

계속 코드를 수정하고 삽질했는데 생각해보니까 중력 방향 문제라는 걸 꺠닫고  바로 interp를 추가하였다.

 

우주와 로봇

구현 후 대표 이미지용으로 한 장 찍었는데 멋있게 잘 나온것 같다 ㅎㅎ...

 

오늘도 코딩하면서 Chatgpt의 도움을 엄청 받았는데

이제는 Chatgpt가 없으면 코딩을 못할것 같다...

최근댓글

최근글

skin by © 2024 ttuttak