구조체와 열거형을 따로 정의해 필요할 때 마다 사용하는게 좋다는 피드백을 받아 둘다 따로 헤더로 빼는 작업을 진행하던 중에 문제가 있었다.

먼저 Visual Studio 안에서 header를 만들 경우에 프로젝트 폴더 속 프로젝트파일이 있는 Intermediate/ProjectFiles 에 생성된다. 하지만 이렇게 된다면 헤더 파일을 인클루드 하는 것에 있어서 경로 문제가 발생한다. 그렇기 때문에 저 폴더속에 있는 파일들을 다시 Source폴더 속 프로젝트 소스 폴더로 옮겨 다시 비주얼 스튜디오에 넣어주어야 한다.

헤더를 따로 뺄 경우 #pragma once를 넣어 헤더의 중복을 방지해주어야 하는 것은 필수이며 USTRUCT의 경우 헤더선언들 맨 마지막에 아래와 같은 선언이 필요하다.

#include <구조체이름.generated.h>

또한 struct의 맨 처음엔 GENERATED_USTRUCT_BODY()를 넣어주어야 한다.

ItemTable 축소 및 수정

XLMS 양식의 엑셀 파일로 데이터를 추출해내는 파일을 만들었었는데 뭐하러 일을 두 번 하게 하는건지 기분도 묘하고 가장 핵심적으로는 한글을 넣으면 깨져서 나온다 ㅠ 그래서 그냥 새로 CSV를 만들고 UTF-8로 인코딩 한 뒤에 저장하는 데이터양도 아직은 확실히 구조를 잡지 못해서 다시 대충 몇개만 넣어두었다.

InteractorBase, PickupBase

InteractionCore를 상속받아 추상적인 개념으로 InteractorBase와 PickupBase를 나눠 두었다. 그런데 문제가 생겼다… 뭔가 코드가 너무 안예쁘다. InteractionCore에서 순수 가상함수를 두고 그걸 또 InteractorBase와 PickupBase에서 다시 순수가상함수로 구현하려 했더니 인스턴스화에서 추상적인 요소가 있다고 컴파일이 진행되지 않는 문제가 있었다. 이 부분은 그냥 가상함수 (Not Pure)로 cpp파일에 함수를 만들고 주석을 달아놓아 해결했는데 이건 좀 아닌거같다… 다시 구현해야겠다.

//InteractionCore.h
UFUNCTION(BlueprintCallable)
	virtual void ShowInteractUI();

	UFUNCTION(BlueprintCallable)
	virtual void CloseInteractUI();

	UFUNCTION(BlueprintCallable)
	virtual void Interact();

//InteractorBase.h & PickupBase.h
virtual void ShowInteractUI();

	virtual void CloseInteractUI();

	virtual void Interact();

DataTable 연동, 불러오기

오늘 최대의 삽질이… 일단 ItemTable로 관리를 해야하는지 조금 의문이 들지만 가능하면 사용해서 구현해내고싶다. 이유는 기획자가 지속적으로 관리하기가 편해지고 한 눈에 리스트로 확인이 가능하며 변경점을 직접 입력하기보다 데이터 테이블에 있는 요소를 알아서 갖다 붙였으면 하기 때문이다.

일단 초기화를 진행함에 있어서 프로젝트를 만들고 처음으로 엔진이 터졌다..ㅠ Access Violation이 일어났다는데 에러로그상에서 문제는 PickupBase에 구현해둔 아이템 테이블을 불러오는 것에 대한 문제이다. 원래는 Enum으로 ItemName을 이렇게 선언해두고,

UENUM(BlueprintType)
enum class EItemName : uint8
{
	Pistol = 0
};

언리얼 문서를 참고해서 템플릿을 이용해 Enum에서 이름 자체를 FString으로 뽑는 코드를 조금 수정해 FName으로 뽑아와 DataTable의 Name을 통해 아이템을 자동으로 찾도록 만들어보았다. 그런데 테스트를 자주 안하면서 만들어서 로그가 제대로 뽑히는지를 아직 못받아낸 것 같다.

template<typename TEnum>
static FORCEINLINE FName GetEnumValueAsName(const FString& Name, TEnum Value)
{
	const UEnum* enumPtr = FindObject<UEnum>(ANY_PACKAGE, *Name, true);
	if (!enumPtr)
	{
		return FName("Invalid");
	}
	return enumPtr->GetNameByValue((int64)Value);
}

테스트를 하면서 진행해야하는걸 뼈저리게 느낀다 ㅠㅠ 겁도 없이 많이도 짰다가 지워버렸다.

void APickupBase::InitItemData(EItemName itemName)
{
FItemData* itemdata = itemTable->FindRow<FItemData>(GetEnumValueAsName("EItemName", itemName), FString(""));
	if (itemdata == nullptr)
	{
	UE_LOG(LogTemp, Warning, TEXT("ERROR"), *itemdata->DisplayName);
	}
		
	UE_LOG(LogTemp, Warning, TEXT("Display Name : %s"),*itemdata->DisplayName);
	UE_LOG(LogTemp, Warning, TEXT("Model Path : %s"), *itemdata->ModelPath);
	static ConstructorHelpers::FObjectFinder<UStaticMesh>SM(*itemdata->ModelPath);
	if (SM.Succeeded())
	{
		body->SetStaticMesh(SM.Object);
	}

	displayName = itemdata->DisplayName;
	internalName = itemdata->InternalName;
	item_type = itemdata->Type;
}

위의 코드로 만든 엑터를 올리고 에디터 안에서 진행하는 PIE(Play In Editor)모드로 실행한 결과 에디터가 터져버린다…ㅠ 크래쉬로그는 나올 기미가 안보여서 로그폴더로 들어가 봤더니 엔진 로그상에선 정상적으로 데이터가 출력됨을 볼 수 있었다. 하지만 터지는 문제가 있으니 다시 봐야할듯..

[2019.01.16-14.25.22:156][156]LogTemp: Warning: Display Name : 권총
[2019.01.16-14.25.22:156][156]LogTemp: Warning: Model Path : StaticMesh’/Game/Models/Pickup/Weapon/SM_Gun.SM_Gun’

일단 집중이 안되기 시작해서 이정도만 정리해 두어야할 것 같다….

내일 구조를 다시 설계해 봐야할 것 같다. 그리고 뭔가 순서가 잘못된 것 같다… 이렇게 아이템을 분류하는 기능을 만들기 전에 가까이 있는 엑터를 먼저 부착시키는 기능을 만드는걸 선행시켰어야 했다.

내일은 줍기 기능을 먼저 만들도록 해야겠다. 아이템 구현쪽으로 삽질만 한 하루였다…띠로리…ㅠ

Interaction 탐지 기능 변경

원래는 아이템에 정확히 LineTrace를 해서 Hit될 경우 데이터를 받아와 착용하는 작업을 했었다. 낮에 구현이 상당히 빠르게 진행되어 로그를 볼 정도로 만들긴 했는데 레이를 쏴서 맞추다보니 아이템에 정확히 맞아야한다는 문제가 있었다. 그래서 LineTrace로 인터렉션 하는 부분을 버리고 UBoxComponent를 플레이어에게 부착시켜 가장 근거리의 엑터를 찾아오는 기능을 만들었다.

void ATESTCharacter::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	TArray<AActor*> interactors;
	//Only store InteractionCore
	InteractionBox->GetOverlappingActors(interactors, TSubclassOf<AInteractionCore>());
	float closestDistance = 9999.0f;
	AInteractionCore* closestInteractor = nullptr;

	if (interactors.Num() == 0)
		return;

	//Do rangebase iteration and pick closest actor to closestInteractor pointer
	for (AActor* iter : interactors)
	{
		float distance = FVector::Distance(this->GetActorLocation(), iter->GetActorLocation());
		if ( distance < closestDistance)
		{
			closestDistance = distance;
			closestInteractor = Cast<AInteractionCore>(iter);
		}
		else
		{
			closestInteractor->CloseInteractUI();
		}
	}
	//call closest interactor's functions
	closestInteractor->ShowInteractUI();
	closestDistance = 9999.0f;
}

Tick을 돌며 계속 아이템을 업데이트 시키는 방식인데 로그로 받아보니 아이템을 감지하는 기능은 동작하는게 확인되었다. 하지만 UI를 내보이는 부분에 있어서는 조금 더 생각을 해봐야겠다.

UI를 일단 저렇게 만들어 두었는데 아직 부착하는 기능을 구현해두지 못했다. 이 부분도 내일 픽업 아이템 줍기를 진행하며 같이 하는걸로!

로그나 테스트도 자주 진행해야하니 LOG CATEGORY도 구현하자.

그리고 제발 집중좀 잘 하자 흑흑 ㅠ