- C++ 코드카타
- Ch3 개인 프로젝트 구현
이번 챕터에서는 간단한 플랫포머 게임을 만들어보기로 했는데, 나는 레퍼런스 게임으로 AltF4를 모작해보려고 한다.
또한, 다른 에셋을 사용하지 않고 오직 AI로만 게임 개발을 시도해보고 싶어서 고른 래퍼런스이기도 하다.
AltF4는 중세 카툰 풍이 더 강하기 때문에, 모델 AI 생성 사이트로 Meshy AI를 선택했다.
캐릭터를 기반으로 TIL을 작성하려고 했으나 작업 과정을 날려버려서... 함정을 기반으로 설명해보려고 한다.
함정을 만들 메시를 제작하기 위해, Gemini를 사용해서 다음과 같이 요청을 했다.

처음에는 쇠창살 장식이 있는 판자라고 했으나 가운데에 너무 떡칠을 한 게 보기 불편해서 해당 부분만 제거하라고 했다.

이제, 해당 이미지를 기반으로 Meshy ai에 던져보았다.

그래도 단순한 장식을 구현하는 메시의 경우 무료 모델인 Meshy5로도 어느 정도 잘 나오는 것 같다.
세 번째 모델이 적절하게 나온 것 같아서 클릭 후 다음과 같이 텍스처를 생성했다.

그랬더니 다음과 같이 모델이 나왔다.

무료 모델로도 내가 원하는 분위기로 잘 구현해줘서 조금 놀랐다.
이제, 이 모델을 FBX로 다운받고 언리얼로 Import 해주었다.

언리얼 상에서도 잘 나온 모습을 확인했으니, 이제 로직을 추가할 차례이다.
함정 로직을 구현해주기 전에, AltF4 특유의 힘없이 떨어지는 래그돌 기능을 추가해보려고 한다.
처음에는 래그돌을 어떻게 구현하지 싶었으나, 언리얼에서 기본적으로 프리셋이 있어서 다음과 같이 세팅해주면 된다.
GetMesh()->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
GetMesh()->SetCollisionProfileName(TEXT("Ragdoll"));
GetMesh()->SetSimulatePhysics(true);
GetMesh()->AddImpulse(Impulse, NAME_None, true);
여기서 AddImpulse 부분은 옵션이지만, 장애물이 가하는 힘의 방향에 따라서 래그돌이 떨어져나가는 것을 보고 싶었기 때문에 Impulse 변수로 메시에 힘을 가하도록 해주었다.
다락문 함정의 경우 정말 간단하게, 다락문 메시 위에 BoxCollision을 추가하고 플레이어를 인식하면 다락문이 회전하도록 만들었다.
아래는 Overlap이 어떻게 이루어졌는지에 대한 코드이다.
// BoxCollision에 Overlap으로 바인딩 된 함수
oid ATrapDoor::OnTrapOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
Super::OnTrapOverlap(OverlappedComponent, OtherActor, OtherComp, OtherBodyIndex, bFromSweep, SweepResult);
// 다락문이 움직이거나 열린 상태라면 리턴
if (bIsMoving || bIsStanding) return;
AKnightCharacter* KnightCharacter = Cast<AKnightCharacter>(OtherActor);
// KnightCharacter가 없고, 현재 캐릭터가 래그돌 상태라면 리턴
if (!IsValid(KnightCharacter) || KnightCharacter->GetIsRagdollActive()) return;
// Box와 닿으면 문을 열기
OpenDoor();
// 문을 여는 방향에 맞춰서 Impulse 세팅
const FVector& LaunchDirection = (GetActorForwardVector() * HorizontalMultiplier + GetActorUpVector() * VerticalMultiplier).GetSafeNormal();
const FVector& FinalImpulse = LaunchDirection * Force;
// 세팅된 Impulse에 맞춰서 플레이어에게 대미지 부여
KnightCharacter->TakeDamageAndRagdoll(FinalImpulse);
// 문을 닫게 하기 위한 타이머 세팅
GetWorldTimerManager().SetTimer(CloseTimerHandle, this, &ATrapDoor::CloseDoor, CloseTime, false);
bIsStanding = true;
// 플레이어에겐 2회의 목숨이 부여됨. 목숨이 다 소진되면 Die처리
if (!KnightCharacter->GetHasArmor())
{
KnightCharacter->Die();
}
KnightCharacter->SetHasArmor(false);
}

뭔가 현재로서는 최선이긴 한데, 기본 래그돌이다보니 너무 사람의 형태를 유지하지 않고 흐느적거린다.
따로 Physics Assets으로 뼈에 제한을 걸어줄 수 있다곤 하나, 아직 아무것도 모르는 상태인지라 일단 해당 부분은 후순위에 두기로 했다.
내일은 좀 더 다양한 장애물을 만들어보는 것이 목적이다.