1인칭 시점의 캐릭터가 필요해서 FirstPerson 템플릿을 베이스로 시작했다

상호작용 대상의 공통 부모를 먼저 정의하기

가장 먼저 한 일은 상호작용 가능한 모든 것들이 공유할 인터페이스(ItemInterface)를 정의하는 것이었다.

그리고 그 인터페이스를 구현하는 베이스 클래스(BaseItem)를 만들었다.

BaseItem에는 씬 컴포넌트를 루트로 지정하고, 콜리전을 씬에 붙이고, 스태틱 메시를 붙이는 기본 구조를 잡았다.

오버랩이 발생하면 OnItemOverlap 같은 함수가 실행되도록 기본 구현도 여기에 넣었다.

 

이후 실제 아이템은 BaseItem을 상속받아 액터로 만들고, 생성자와 개별 로직만 추가로 구현하면 됐다.

공통 동작을 부모에 몰아두고 자식은 차이만 구현하는 구조이다

 

원래는 입력 처리가 플레이어 컨트롤러에 있는 게 더 일반적인 구조라고 알고 있는데, 이 프로젝트에서 주어진 캐릭터 파일 구조상 입력이 캐릭터 클래스에 이미 구현되어 있어서 그 구조를 따라갔다. 

E키를 코드까지 연결하는 과정

먼저 헤더에 입력을 담을 변수를 선언한다.

UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=Input, meta=(AllowPrivateAccess = "true"))
UInputAction* InteractAction;

 

그리고 이 입력이 들어왔을 때 실행할 함수를 선언한다.

void Interact();

 

여기까지는 코드 쪽 준비고, 실제 키와의 연결은 에디터에서 이루어진다.

콘텐츠 브라우저에서 Input Action 에셋(IA_Interact)을 만들고, Value Type을 Digital(bool)로 지정했다.

(방향값 없이 눌렸다/안눌렸다만 필요한 동작이라서)

이후 Input Mapping Context에 이 액션을 E키와 묶는다

다음으로 캐릭터 블루프린트의 Details 패널에서 InteractAction 변수 슬롯에 방금 만든 IA_Interact 에셋을 드래그해서 끼운다.

마지막으로 cpp의 SetupPlayerInputComponent에서 입력과 함수를 바인딩한다.

EnhancedInputComponent->BindAction(InteractAction, ETriggerEvent::Started, this, &ATheRoomCharacter::Interact);

 

그리고 함수 몸체를 구현한다

void ATheRoomCharacter::Interact()
{
    if (CurrentInteractable)
    {
        CurrentInteractable->ActivateItem(this);
    }
}

 

 

상호작용 안내 UI를 띄우는 구조

아이템 근처에 가면 "E를 눌러 상호작용" 같은 안내 문구가 뜨도록 만드는 것도 비슷한 다단계 연결이 필요했다.

먼저 위젯 블루프린트(WBP_InteractionPrompt)를 만들고 Text 위젯으로 안내 문구를 넣었다. 

UUserWidget을 상속하는 C++ 클래스(UInteractionWidget)를 따로 만들고, WBP_InteractionPrompt의 부모 클래스를 이걸로 지정해서 타입을 이어주는 다리를 만들었다.

캐릭터 헤더에는 두 종류의 변수를 추가했다.

UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Interaction, meta = (AllowPrivateAccess = "true"))
TSubclassOf<UInteractionWidget> InteractionWidgetClass;

UPROPERTY()
UInteractionWidget* InteractionWidgetInstance;

 

하나는 어떤 위젯을 띄울지에 대한 설계도(TSubclassOf), 다른 하나는 지금 실제로 화면에 떠 있는 위젯 인스턴스다.

클래스는 에디터에서 한 번 지정해두는 고정값이고, 인스턴스는 오버랩 상황에 따라 생성되고 사라지는 값이다.

위젯을 띄우고 숨기는 함수는 다음과 같이 구현했다.

void ATheRoomCharacter::ShowInteraction()
{
    if (!InteractionWidgetInstance && InteractionWidgetClass)
    {
        InteractionWidgetInstance = CreateWidget<UInteractionWidget>(GetWorld(), InteractionWidgetClass);
        InteractionWidgetInstance->AddToViewport();
    }
}

void ATheRoomCharacter::HideInteraction()
{
    if (InteractionWidgetInstance)
    {
        InteractionWidgetInstance->RemoveFromParent();
        InteractionWidgetInstance = nullptr;
    }
}

 

이 함수들은 BaseItem의 오버랩 함수에서 호출된다. OnItemOverlap에서 ShowInteraction을, OnItemEndOverlap에서 HideInteraction을 부르는 식으로 연결했다.

 

마지막으로 캐릭터 블루프린트의 InteractionWidgetClass 슬롯에 WBP_InteractionPrompt를 끼워서 실제 에셋을 연결했다

+ Recent posts