이번 내용
라이터만들기, 손전등 만들기, 바닥에 떨어진 아이템 줍기,
인벤토리에 추가하기, 인벤토리 위젯 업데이트, 장비 변경,
앉기, 달리기(스태미나 표시), 간단한 메시 수정
메인 세팅 구조체 만들기
먼저 세팅 값에 대해서 각 분야별로 나누어 세팅 값을 관리할 메인 세팅 구조체를 만든다.
현재는 크게 장비 세팅과 플레이어 세팅으로 나누어져 있고 각 내용 안에 또 다른 구조체가 들어가 있는 형식이다.장비 세팅 구조체 만들기
장비 세팅 구조체에는 인벤토리에서 장착이 가능한 라이터와 손전등에 대한 세팅 구조체가 들어있고 Equip / Unequip Speed는 장비 장착 또는 장비 해제할 때 걸리는 시간이다. 예를 들어서
1. 라이터를 장착할 때
2. 손전등을 장착할 때
3. 라이터를 들고 있는 상태에서 손전등으로 교체할 때
4. 손전등을 들고 있는 상태에서 라이터로 교체할 때
5. 라이터를 해제할 때
6. 손전등을 해제할 때
이런 경우에 모두 해당된다.라이터 세팅 구조체 만들기
라이터 세팅은 라이터가 가지고 있는 고유한 세팅값을 가지는 구조체이다.
Lighter Duration은 라이터가 가지고 있는 연료가 시간에 따라 줄어드는 Timeline의 Play Rate를 조절하는 값
그 외에도 라이터 자체를 구성하는 값에 대한 내용이 있다.손전등 세팅 구조체 만들기
손전등 세팅 역시 비슷한 구조로 세팅하였다.PointLight와 SpotLight 세팅
라이터는 PointLight를, 손전등은 SpotLight를 사용하기 때문에 각각 세팅을 위한 구조체를 만들어준다.
그럼 이제 장비 세팅에 관한 구조체 작업은 끝났고 Character에 메인 세팅 구조체를 가진 변수를 만들어 값을 할당해준다.

블루프린트 내용
BP_Character : 캐릭터 무브먼트와 캐릭터 메시를 가진 BP
BP_Engine : 캐릭터 카메라 안에 속해 있는 BP로 Character 메시의 움직임을 제외한 모든 상호작용이 구현된 BP
BP_Function : 블루프린트 함수 라이브러리로 서로 다른 클래스들이 특정 로직을 자주 필요로 하는 경우 (예: 현재 캐릭터의 Engine을 오브젝트 레퍼런스로 가져오기, 메인 게임 세팅과 플레이어 세팅, 장비 세팅 값들을 가져오기 등) 이곳에서 함수를 정의하여 자유롭게 사용가능
BP_Lighter : 라이터의 연료, 연료가 닳는 속도, 불빛의 깜빡임에 대한 BP
BP_FlashLight : 손전등의 배터리, 배터리가 닳는 속도, 불빛의 깜빡임에 대한 BP
BP_Use : 인벤토리에서 장착이 가능한 아이템들을 Useable로 콜리전 타입을 설정하고 장착이 가능한 아이템 중에서 Enum과 Switch 구문을 통해 원하는 액터의 함수를 실행할 수 있도록 만든 BP
BPI_Interactable : 열고 닫을 수 있는 문 뿐만아니라 엘레베이터 버튼 처럼 인벤토리에 장착은 안되지만 상호작용은 되는 액터들이 가진 고유한 로직을 쉽게 Interact 할 수 있는 블루프린트 인터페이스
라이터 줍기
먼저 입력으로 왼쪽 마우스 클릭을 받았을 때 Inveraction Distance라는 200의 값을 가진 거리만큼 Line Trace By Channel로 검사한다.
캐릭터에 IgnoreTrace라는 태그 값을 미리 넣어놔서 캐릭터를 제외하고 Line Trace한 후 Hit된 액터와 컴포넌트를 반환한다.
라이터와 손전등은 인벤토리에 들어갈 수 있고 장착까지 되는 아이템이기 때문에 사전에 콜리전 타입을 Useable로 설정하고 이 로직에서는 Useable이면 해당 액터를 Add Item으로 넘겨 인벤토리에 넘겨주고 그렇지 않으면 그 다음 Branch문으로 간다.
그 다음은 BPI_Interactable이란 인터페이스를 가지고 있으면 해당 액터가 가지고 있는 Interact를 실행한다.
이건 BP_Door로 2미터 안에 있는 문을 클릭했을 때 인터페이스로 인해 Interact가 실행되고 문이 열고 닫히는게 반복되는 구조이다.
인터페이스 vs 이벤트 디스패처
인터페이스와 이벤트 디스패처는 어떤 상황에서 사용하는지 간략하게만 보자
인터페이스 : 필드 위의 특정 하나의 적에게만 특정 로직을 수행 ← 인터페이스 사용
예를 들어서 필드 위에 존재하는 적 중 특정 적에게만 진화를 적용해야 할 때 ( 이벤트 디스패처로 했을 경우 특정 적에게 진화를 적용하는 로직이 필드 위의 존재하는 적의 개수만큼 중복 실행된다. )이벤트 디스패처 : 모든 액터에게 해당 클래스의 특정 로직을 일괄 수행 ← 이벤트 디스패처 사용
예를 들어서 필드 위에 존재하는 모든 적을 제거해야 할 때 제거하는 로직을 바인딩 시켜놓고 모든 적을 제거해야 할 조건이 충족되었을 때 이벤트 디스패처를 호출하면 적이 전부 삭제 ( 인터페이스로 했을 경우 필드 위에 있는 액터들을 배열로 받고 for loop로 각 액터를 하나씩 제거해야 한다. )이어서
만약 인벤토리에 넣을 수 있는 액터라면 AddItem으로 넘어가게 된다.
앞에서 생략된 로직은 인벤토리 액터 배열을 관리하는 Player Inventory라는 액터 배열에 현재 인벤토리 슬롯 인덱스에 해당하는 자리에 액터 값을 할당한다.
WB_Inventory 인벤토리 위젯
그 다음에는 매크로를 사용해 인벤토리 위젯을 업데이트해주고
열거형을 가져와 액터에 맞는 노드를 호출하게 된다.
사실 이 부분은 Use로 Cast하고 열거형을 가져와 선택하는 방법도 틀리지는 않았지만 연결되는 BP에 인터페이스를 사용했으면 더 간결하게 되었을 것 같다.라이터 켜고 들기
이 부분은 만들면서 정말 복잡했는데 노드도 워낙 많아 사진으로 올릴 수가 없고 조건에 따른 로직만 간단하게 정리하겠다.
1. 인벤토리 슬롯 1~5키 중 하나를 선택(1번에 라이터가 있다면 1번키 누르기)
현재 인벤토리 인덱스와 이전 인벤토리 인덱스라는 인티저 변수는 0으로 초기화 되어있다.
1번키인 경우 0~4인덱스이기 때문에 0의 값이 현재 인덱스로 세팅됨
동시에 EquipProgress라는 부울 변수를 True로 바꾸고 이게 True일 때는 장착 또는 해제하는 딜레이가 끝나기 전까지 장비 교체나 버리기를 막는다.2. 인벤토리 0번 인덱스가 라이터 액터라면 라이터 액터가 가진 Equip Lighter를 실행한다. 만약 손전등이라면 Equip Flashlight이 실행된다.
3. 현재 EquipProgress 상태인지, HasLighter로 라이터를 가지고 있는지, 연료가 충분한지 등등 여러 조건문을 지나고 Lighter Off라는 부울 변수를 검사한다.
이 변수가 중요한 이유는 여러 조건을 내포하고 있는데, 만약 Lighter Off가 False라면 라이터가 켜져있다는 뜻이고 켜져있는 라이터를 장비 해제해야하고, True라면 라이터가 꺼져있다는 뜻이고 이는 기존에 아무런 장비를 착용하지 않았거나 아니면 손전등을 켜고 있는 상태일 수 있다.4. 먼저 Lighter Off가 True일 경우 매크로 Unequip을 통해 모든 장비를 해제한다.
그리고 시야에 보이지 않는
라이터를 Set Visibility로 LighterMesh와 LighterLight를 화면에 보이게 해주고
장비를 아래에서 위로 들게 하도록 타임라인을 만들어 자연스럽게 장착해준다.5. 장비를 장착했을 때 BP_Lighter에 구현되어 있는
연료가 지속적으로 감소하는 타임라인과 불빛이 반복적으로 깜빡이는 타임라인을 구현해준다.
연료를 뜻하는 Remaining이 0이될 경우 라이터를 해제하게 되고
들고 있던 라이터를 다시 내려놓으며 장비를 해제시키기 위해 Reverse from End를 연결시켜 준다.손전등 로직도 라이터와 비슷하므로 패스
이런식으로 장비 장착, 해제, 교체까지 가능하다.
위 gif처럼 장착한 아이템의 연료 소모가 파란색 프로그레스바로 확인이 가능하다.
아이템 버리기 ( 피직스 시뮬레이트 )
지금까지 이 프로젝트를 진행하면서 가장 문제해결이 힘들었던 부분이 있는데 그게 버리기이다.
사실 버리기 로직이 어려운게 아니라 바로 피직스 시뮬레이트란 것이 문제였는데
먼저 피직스 시뮬레이트란 액터나 컴포넌트에 물리 엔진의 제어를 위임하여 엔진의 물리 법칙 중 중력, 충돌, 마찰 등을 받게 되는 것이다.일단 아이템을 버렸으면 그 아이템이 내 앞에 떨어져야 하므로 BP_Lighter와 BP_FlashLight는 피직스 시뮬레이트와 중력을 활성화 해야한다.
그래서 기본 로직은 먼저 플레이어의 카메라를 가져오고 Drop Distance라고 하는 20의 값을 가진 변수로, 카메라 앞 20cm에 내가 들고 있는 아이템을 Set Actor Location으로 움직이게 한다.
근데 이렇게 하면 gif처럼 라이터를 버렸을 때 내 카메라 앞 20cm가 아닌 원래 있던 자리로 돌아간다.
이 문제를 해결하기 위해 버리고 나서의 라이터 좌표를 찍어봤는데 좌표자체는 카메라 앞 20cm가 맞았다.
이 상황에서 피직스 시뮬레이트를 끄게 되면
중력의 영향을 받지 않는 채로 카메라 앞 20cm에 제대로 떠있는 것을 볼 수 있다.
그러면 로직의 문제가 아니라 피직스 시뮬레이트와 Set Actor Location의 문제라고 볼 수 있겠다.Chat GPT와 커뮤니티에 따르면 Set Actor Location은 Transform을 강제로 옮길 때 액터의 Root Component 기준으로 이동한다고 한다.
근데 이 때 피직스 시뮬레이트로 인해 Root Component가 물리 효과를 제어 중이면 메시가 제대로 이동이 안된다고 한다.그래서 이 때는 SetWorldLocation을 사용하여 문제를 해결하였다.
Sweep을 True로 설정하여 벽을 바라보고 아이템을 버렸을 때 아이템이 벽을 통과하는 일이 없도록 했다.
앉기, 달리기 ( 스태미나 위젯 )
먼저 Player Setting에 앉기, 달리기 관련 구조체를 만들어 준다.달리기
쉬프트를 누르고 있을 경우 Running 상태, 쉬프트를 떼면 Walking 상태가 된다.
WB_Stamina 위젯 내용
스태미나에 해당하는 위젯을 만들고 쉬프트를 꾹 누르면 값이 1이하가 될 때가지 달리고 Stamina가 1이하가 되면 Walking 속도로 바뀐다.
달리지 않으면 Stamina가 차오르고 100이되면 위젯이 사라진다.
앉기
앉기는 Player Setting으로 서있는 높이와 앉았을 때의 높이를 가져온다.
이게 필요한 이유는 앉은 상태로 천장이 낮은 공간으로 들어갔을 때 천장의 높이가 서있는 높이보다 낮으면 일어나지 못해야 하기 때문이다.
이번에는 Sphere Trace로 위쪽 방향으로 Crouch Heiget 길이까지 물체가 없으면
타임라인으로 서있는 높이에서 앉은 높이까지 서서히 앉게 된다. 이후 최대 걷기 속도는 Crouch Speed가 된다.
아직 부족한 부분이 많지만 더 노력해서 제대로된 게임을 만들어 보도록 하자.
다음에는 엘리베이터 구현, 맵 제대로 만들기, 게임설정창 구현,
오버랩으로 시나리오 구성, AI 귀신 강화, 설치형 아이템 등등 해보겠다.