
에셋 팩 MedievalDungeon을 활용하여 지하로 향하는 던전 맵을 만들어 주었다.
각각 던전, 계단, 복도, 묘실로 분류하여 파일화해주었다.

맵에 디렉셔널 라이트를 추가하고 스카이 라이트와 엔진 콘텐츠로 주어지는 BP_Sky_Sphere를 추가하였다.
스포이드 버튼을 눌러 맵에 소환한 다이렉트 라이트를 선택해주고 다이렉트 라이트를 조정한 후 Refresh Material을 누르면 하늘과 해가 다이렉트 라이트의 각도에 따라 조정된다.

추가하고 보니 벽의 빛 반사가 이상한 부분이 있다.
새로 추가된 루멘이 아직 지원하지 않아 생기는 현상이라고 한다.

벽을 선택하여 벽을 이루는 머터리얼의 부모를 찾아주고 픽셀 뎁스 오프셋을 해제해주니 빛을 받는 벽의 모습이 자연스러워졌다.


빛의 모빌리티는
게임 시작전에 고정되어 설정할 수 없는 static,
게임 시작 이후에도 밝기 정도를 변경할 수 있는 stationary,
게임 시작 이후 모든 Movable이 있다.
현재 루멘 시스템은 Movable로 설정했을 때 가장 잘 동작한다고 한다.
것을 변경할 수 있는 모든 빛의 모빌리티는 무버블로 설정해주었다.

Movable로 설정하고 맵을 둘러보니 맵의 곳곳에 빛이 새고 있는 모습을 발견할 수 있었다.

바닥을 들어올리고 빛이 새는 부분을 증축하여 빛을 막아주었다.

내부로 들어오는 빛을 모두 막아주었으니 이제 내부에도 빛을 넣어준다.
BP_Torch를 찾아 자손 블루프린트를 만들어고 BP_Torch_Dungeon으로 명명하고 그림자를 설정해주었다.

토치의 그림자가 과한 영향이 있어 BP_Torch의 디테일부분에서 다이나믹 섀도와 스태틱 섀도를 해제하여 해결해주고 밝기를 100으로 빛의 범위인 어테뉴에이션 반경을 800으로 낮추었다.
앞으로 사용하는 모든 횃불에 설정을 적용하기 위해 BP_Torch_Dungeon의 변경사항을 블루프린트에 적용하였다.



맵 내의 조명작업을 완료해주었다.

게임을 시작하면 바닥이 뚫리고 플레이어가 떨어지는 현상을 고치기 위해 바닥을 선택하고 콘텐츠 드로어에서 바닥에 해당하는 스태틱 메시를 찾아주었다.

바닥에 박스 단순화 콜리전을 추가하고 z 크기를 10으로 늘린 후 바닥에 맞게 하기 위해서 중앙 위치를 -10으로 설정해주었다.

플레이어의 로봇 팔을 없애기 위해 BP_FirstPerson을 찾아 자손 블루프린트를 생성하여 BP_Player를 만들어주었다.

BP_Player에 들어가 Skeleton Mesh Asset에서 팔을 지워주었다.

CryptRaiderGameMode를 부모로 BP_CryptRaiderGameMode를 만들어 기본 게임모드로 설정 후 만든 BP_Player를 디폴트 폰으로 설정해주었다.

이제 움직이는 문을 만들기 위해 새로운 벽을 생성하고 Mover라는 액터 컴포넌트를 만들어 벽에 추가해주었다.

액터의 원래 위치를 설정하기 위해 BeginPlay에 OriginalLocation을 만들어주었고 Tick에 현재위치 목표위치를 선언하여 업데이트 된 위치를 액터를 움직여주는 FMath::VInterpConstantTo()에 전달하여 작동하도록 만들어주었다.

ShouldMove 변수가 True일때 액터가 Z(-600) 방향으로 잘 이동하는 것을 확인해주었다.

기믹을 위해 던전 내부 감옥 안에 석상을 배치하고 플레이어가 쉽게 알아차리게 하기 위해 스포트라이트를 비춰두었다.

플레이어가 석상을 잡고 이동할 수 있게 만들기 위해 씬 컴포넌트 Grabber를 생성하고 플레이어의 카메라에 붙여주었다.

Grabber에서는 플레이어의 화면이 회전할때 마다 그 값을 가져오도록 코드를 작성해주었다.

GetWorld()는 보통 라인 트레이스나 스윕을 사용하기 위해 많이 사용된다.
다른 유용한 기능으로는 TimeSeconds가 있는데 게임이 시작된 때부터 초를 세주는 기능이다. 위는 GetWorld()->TimeSeconds를 사용해 초를 로그에 출력해준 모습이다.

Grabber에 DrowDebugLine을 활용하여 현재 위치에서 카메라 방향으로 400만큼 일직선을 그려주는 과정을 수행하였다.
석상을 잡거나 인식하는데 활용될 예정이다.

SweepSingleByChannel을 이용하여 미리 만들어둔 선이 액터와 부딫히면 액터의 이름을 출력하고 아니면 다른 메세지를 출력하도록 코딩해주었다.

로그가 잘 출력되는 모습이다.

석상을 집을 때 마우스를 활용하기 위해 액션 매칭에 마우스 버튼과 게임패드 트리거를 설정해주었다.

BP_Player의 블루프린트 설정에서 Grab을 호출하고 마우스를 눌렀을 때와 뗐을때 메세지를 출력하도록 설정해주었다.

화면에 메세지가 잘 출력되는 모습이다.

Grabber에서 Grab과 Release 함수를 만들어 BlueprintCallable을 사용하여 블루프린트에서 작업할 수 있도록 만들어 주었다. 왼쪽 마우스를 클릭했을때 액터가 감지되지 않으면 아웃로그에 NO Actor Hit을 출력하고 액터가 감지되면 액터의 이름을 그리고 마우스를 떼면 Released grabber을 출력하도록 코딩해주었다.

마우스를 눌렀을때 Grab을 호출하고 뗐을때 Release를 호출하도록 해주었다.

액터가 아닌곳을 클릭했을때는 메세지가 액터인 가고일 석상을 클릭했을때는 가고일의 이름이 로그로 잘 출력되는 모습니다.
PhysicsHandle은 액터를 자연스럽게 잡을 수 있도록 도와주는 함수이다.

PhysicsHandle을 사용하기 위해 BP_player에 PhysicsHandle을 추가해주고

PhysicsHandle가 nullptr일 때 나타날 수 있는 엔진 크러쉬를 막아주기 위해 Beginplay에 작업을 해두었다.

본격적으로 PhysicsHandle을 사용하기에 앞서 액터의 원하는 부분을 제대로 잡기 위해 DrawDebugSphere를 사용하여
전에 설정해준 사거리의 끝에 파란구체,
HitResult.Location의 위치에 초록 구체,
HitResult.ImpactPoint에 빨간 구체가 나타나도록 하고 실행해보았다.

사거리 밖에서 사용하여 사거리 끝에 파란구체가 나타나고 액터 기준 1미터 반경에 초록구체, 가고일 액터의 테두리에 빨간 구체가 생기는 것을 볼 수 있었다.

PhysicsHandle->GrabComponentAtLocationWithRotation()를 이용하여 액터를 집을 수 있게 설정해주었다.

Tick에서 PhysicsHandle->SetTargetLocationAndRotation()를 이용하여 가고일을 집은 후 가고일이 계속 플레이어 앞을 따라다니도록 만들어주었다.

가고일의 모빌리티를 무버블, 피직스에서 피직스 시뮬레이트를 설정해준 뒤 가고일을 집으니 플레이어를 잘 따라 이동하는 모습니다.

먼저 Release()함수에도 얼리 리턴을 해주고 액터를 놓기 위해 PhysicsHandle->GetGrabbedComponent()를 사용하여 마우스 버튼을 뗐을때 널포인터가 아니라면 ReleaseComponent()를 실행하여 액터를 놓도록 해주었다.

가고일을 원하는 곳에 가져다 놓을 수 있게 되었다.

플레이어가 가고일을 집고 이동할때 가고일이 플레이어와 부딫혀 흔들리는 현상이 있어 가고일의 폰 콜리전을 오버랩으로 설정하고 바닥에 붙인 SM_Dirt_Mound_A는 NOCollision으로 설정하였다.

전에 만들었던 움직이는 벽에 오버랩 이벤트를 발생시키기 위해 벽에 하위 블루프린트 BP_SecretWall을 생성하고 블루프린트에 BoxCollision인 Trigger Area를 생성하여 콜리전 프리셋을 OverlapAllDynamic으로 설정하고 오버랩 이벤트 생성을 체크해주었다.
강조를 위해 스포트 라이트도 추가해주었다.

가고일 액터도 마찬가지로 오버랩 이벤트 생성을 체크해주었다.
Trigger Area가 가고일에 잘 반응하는 모습이다.

Trigger Area를 C++로 작업해주기 위해 Trigger Area를 지우고 BoxComponent를 기반으로 하는 새로운 C++ 컴포넌트인 TriggerComponent를 생성하였다. TriggerComponent의 위치를 잘 조정하고 콜리전 설정을 해주니 잘 작동하였다.

TriggerComponent에 생성자를 생성하고 TriggerComponent가 시작되면 PrimaryComponentTick.bCanEverTick이 true가 되도록 만들어 주어 TriggerComponent의 틱이 바로 실행될 수 있도록 만들어주었다.

TArray를 이용하여 액터를 담는 Actors를 만들고 GetOverlappingActors(Actors);를 사용하여 겹치는 액터가 생기면 TArray에 담기도록 만들었다.
그리고 Actors의 크기가 0이 넘어가면 어떤 액터가 겹치는지 로그를 출력하도록 만들어주었다.

비밀 문을 여는 작업의 마무리를 위해 가고일의 액터태그를 Unlocking1으로 설정한다.

TriggerComponent에서 FName ActorTag를 만들어 언리얼에서 설정한 Tag와 일치하면 조건문이 실행되도록 만들었다.

FName ActorTag를 조금 더 자세한 FName AcceptableActorTag로 변경, GetAcceptableActor()를 생성하여 Tick에 작업해둔 내용을 꺼내주고 Tick에서는 GetAcceptableActor()에서 값을 리턴해주면 조건문이 작동하게 만들어주었다.
현재 SecretWall에는 Trigger와 Mvoer가 둘 다 존재하지만 다른 경우의 진행과정에서는 따로 존재할 가능성이 있다.

이런 경우를 해결해주기 위해서 TriggerComponent에서 Mover를 호출하고 블루프린트에서 수정할 수 있도록 만들어 각기 다른 Mover를 받아 새로 설정할 수 있도록 만들어주었다.

Mover에서는 벽이 움직이기 위한 조건인 ShuldMover를 새로 설정할 수 있는 SetShuldMover를 만들어주었다.
위의 설정이 끝난 뒤 컴파일 실행하면 언리얼이 강제종료되는 부분이 있었지만 에디터 종료 -> 비주얼 빌드 이 후 잘 실행되었다.

벽이 잘 내려가지만 가고일에 막히는 모습이다.

위의 가고일이 걸리는 문제를 해결하기 위해 두 함수를 사용하였다.
특정조건이 완료되면 컴포넌트가 부착되는 AttachToComponent와
물리 시뮬레이션을 꺼주는 SetSimulatePhysics이다.
위의 함수들은 PrimitiveComponent들을 대상으로 사용되는데 부착을 위한 GetRootComponent는 SceneComponent에 해당한다고 나와있다.
SceneComponent는 PrimitiveComponent의 상위 컴포넌트이므로
cast<>()함수를 사용해 새로운 UPrimitiveComponent Component; 변수에 받아주고 조건문을 활용하여 PrimitiveComponent로 인정되어 GetRootComponent가 잘 받아들여졌는지 아니면 nullptr인지 확인한다.

가고일과 같이 문이 내려가며 이제 지하에 도달할 수 있게 되었다.

문은 잘 열리지만 가고일을 들고 있는 상태에서도 비밀문에서 가고일이 인식되면 비밀문에 가고일을 뺏기게 된다.
이 현상을 해결하기 위해 가고일을 들었을때는 태그를 추가하고 놓았을때는 태그를 삭제하도록 코딩해주었다.

트리거 컴포넌트에서는 액터가 올바른 태그를 가지고 있고 "Grabbed"라는 태그를 가지고 있지 않을때 반응하도록 설정해주었다.

이제 비밀문은 가고일을 놓지 않으면 작동되지 않는다.
이제 던전을 탈출하기 위한 작업은 대부분 끝이 났다.
지금까지 만든 것들을 활용하여 마무리 작업을 진행한다.

씬의 노출 보정 메소드에 의해 내부가 어두워지는 현상이 빈번히 일어난다.
이 현상을 안정화시키기 위해 액터인 PostProcessVolume을 추가해 주고 맵 전체를 덮도록 확장시켜주었다.

성묘 구역에 조각상 받침대를 배치하고 TriggerComponent를 추가하고
조각상을 배치하고 Grabber를 추가, 철장을 배치하고 Mover를 추가해주었다. 조각상은 가고일 때와 마찬가지로 콜리전에서 Grabber를 Block, Pawn을 Ignore로 바꿔주었고 조각상과 TriggerComponent의 태그를 Unlock2로 설정,
조각상과 철장을 무버블로 설정해주었다.

철장의 Mover가 조각상의 유무에 따라 움직이도록 서로 연결해주었다.

Grabber에서 조각상이 역할을 다 할 수 있도록 SetSimulatePhysics와 DetachFromActor를 추가해 주었다.

철장은 조건에 따라 다시 원래의 위치로 돌아가게 만들어야 했기에 Mover에서 조건문 안에는 TargetLocation만 바뀌도록 설정 해주었다.

맵 구석에 황금 조각상을 대신할 항아리를 두고 Tag를 Unlock2, 무버블로 설정해 주었다.
이렇게 또 하나의 게임이 완성되었다.