[UE5] PickUpDefense #3 - 마우스 입력 처리 및 위젯 동적 생성

ChangJin·2024년 8월 31일
1

Unreal Engine5

목록 보기
101/114
post-thumbnail

2024.08.31

이번 글에서는 PickUpDefense 게임에서 마우스 입력을 처리하고, 위젯을 동적으로 생성하는 과정에서 발생한 문제들을 해결하는 방법에 대해 다룹니다. 특히, 언리얼 엔진에서 액터 클릭 이벤트와 UI 위젯 생성 시 발생하는 충돌을 해결하는 데 중점을 두었습니다.


진행상황

  • ✅ 상단 스테이지 UI
    - ✅ UI 위치
    • ⬜ 상단 UI 시간 시스템
    • ⬜ 상단 UI 스테이지 시스템
  • ✅ 하단 차량 상점 UI
    - ✅ UI 위치
    • ✅ 마우스 입력 처리
    • ⬜ 드래그 앤 드롭으로 카드 옮기기
    • ⬜ 마우스 우클릭으로 구매, 판매하기
  • ⬜ 우측 아이템 UI
    • ⬜ 드래그 앤 드롭 기능을 통한 무기 장착
    • ⬜ 여러 아이템의 마우스 좌클릭 입력 처리
  • ⬜ 호버시 자세히보기

1. 액터 클릭 이벤트 처리

게임 내에서 액터를 클릭했을 때 특정 동작을 수행하도록 하려 했으나, 처음에는 클릭 이벤트가 트리거되지 않는 문제가 발생했습니다. 특히, OnClicked 이벤트가 호출되지 않았으며, 이는 충돌 설정이나 입력 처리 방식에 문제가 있을 수 있었습니다.

원인:

  • 충돌 설정이 올바르게 적용되지 않았거나, 클릭 이벤트가 제대로 바인딩되지 않았기 때문에 발생한 문제였습니다.

해결 방법:

  • BaseMesh의 충돌 설정을 다시 확인하고, OnClicked 이벤트를 PlayerController와 연동하여 클릭 이벤트를 처리하도록 수정했습니다.
BaseMesh->SetCollisionProfileName(TEXT("BlockAll"));
BaseMesh->SetGenerateOverlapEvents(true);
BaseMesh->SetNotifyRigidBodyCollision(true);
BaseMesh->OnClicked.AddDynamic(this, &ABaseVehicle::OnMeshClicked);

이렇게 수정한 후, 액터 클릭 이벤트가 정상적으로 트리거되었고, UE_LOG를 통해 디버깅 메시지를 출력할 수 있었습니다.


2. 위젯 동적 생성 시 발생한 문제

액터를 클릭했을 때, 해당 위치에 옵션 메뉴 위젯을 생성하려 했습니다. 하지만 CreateWidget 함수 호출 시 ABaseCharacter를 소유자로 사용하려 했던 것이 문제를 일으켰습니다. CreateWidget 함수는 UWorld, APlayerController, 또는 UGameInstance 타입의 소유자만을 지원합니다.

원인:

  • 잘못된 타입의 객체를 CreateWidget의 소유자로 사용하려 했기 때문에 발생한 컴파일 오류였습니다.

해결 방법:

  • APlayerController를 소유자로 지정하여 위젯을 생성하도록 수정했습니다.
UOptionMenu* OptionMenuWidget = CreateWidget<UOptionMenu>(PlayerController, UOptionMenu::StaticClass());

이제 위젯이 정상적으로 생성되었고, 마우스 클릭 위치를 기준으로 살짝 오른쪽 아래에 나타나도록 설정할 수 있었습니다.


3. 버튼 클릭 시 동작 정의

생성된 옵션 메뉴에서 버튼을 클릭했을 때 특정 동작을 수행하도록 구현했습니다. 이를 위해 버튼 클릭 이벤트에 동작을 바인딩하여 처리했습니다.

방법:

  • AddButton 함수 내에서 버튼 생성 시, 클릭 이벤트를 특정 함수에 바인딩했습니다.
if (ButtonName == FName("삭제하기"))
{
    Button->OnClicked.AddDynamic(this, &UOptionMenu::OnDeleteButtonClicked);
}

버튼 클릭 시 수행할 동작:

  • 예를 들어, "삭제하기" 버튼이 클릭되었을 때, 위젯을 화면에서 제거하고 로그 메시지를 출력하도록 설정했습니다.
void UOptionMenu::OnDeleteButtonClicked()
{
    UE_LOG(LogTemp, Warning, TEXT("삭제하기 버튼이 클릭되었습니다!"));
    RemoveFromParent();  // 이 위젯을 화면에서 제거
}

4. 위젯 동적 생성

위젯을 동적으로 생성하고, UI에 필요한 정보를 할당하는 방법에 대해 학습했습니다.

4.1 위젯 초기화와 데이터 바인딩

위젯을 동적으로 생성한 후, 해당 위젯에 필요한 데이터를 설정합니다. 아래는 UShopPanelSlot 위젯을 동적으로 생성하고, 카드 정보를 할당하는 과정입니다.

코드 예시: 위젯 동적 생성 및 데이터 바인딩

UShopPanelSlot* PanelSlot = WidgetTree->ConstructWidget<UShopPanelSlot>(UShopPanelSlot::StaticClass(), BorderName);
if (PanelSlot)
{
    // 변수에 값을 설정
    PanelSlot->CardName = TEXT("모르가나");
    PanelSlot->Synergy1Text = TEXT("마녀");
    PanelSlot->Synergy2Text = TEXT("박쥐여왕");
    PanelSlot->Synergy3Text = TEXT("보호술사");
    PanelSlot->PriceText = TEXT("$5");

    // UpdateCardInfo를 호출하여 UI를 업데이트
    PanelSlot->UpdateCardInfo();  // 즉시 UI를 갱신
}

4.2 발생한 문제 및 해결

위젯 생성 후 데이터를 할당할 때, 일부 위젯 컴포넌트가 null인 상태에서 접근하려고 하여 게임이 크래시되는 문제가 발생했습니다. 이는 위젯이 아직 완전히 초기화되지 않았기 때문이었습니다. 이를 해결하기 위해, 다음과 같은 조치를 취했습니다.

문제 해결 과정

  1. null 체크 추가: 각 컴포넌트가 null이 아닌지 확인한 후 데이터를 설정하도록 코드를 수정했습니다.
  2. 디버그 로그 추가: 위젯이 제대로 초기화되지 않은 경우, 로그를 통해 문제를 확인할 수 있도록 디버그 로그를 추가했습니다.

문제 해결 코드

void UShopPanelSlot::UpdateCardInfo()
{
    if (ensure(Synergy1))
    {
        Synergy1->SetText(FText::FromString(Synergy1Text));
    }

    if (ensure(Synergy2))
    {
        Synergy2->SetText(FText::FromString(Synergy2Text));
    }

    if (ensure(Synergy3))
    {
        Synergy3->SetText(FText::FromString(Synergy3Text));
    }

    if (ensure(Name))
    {
        Name->SetText(FText::FromString(CardName));
    }

    if (ensure(Price))
    {
        Price->SetText(FText::FromString(PriceText));
    }

    if (ensure(Image) && !ImageDir.IsEmpty())
    {
        UTexture2D* Texture = Cast<UTexture2D>(StaticLoadObject(UTexture2D::StaticClass(), nullptr, *ImageDir));
        if (ensure(Texture))
        {
            Image->SetBrushFromTexture(Texture);
        }
        else
        {
            UE_LOG(LogTemp, Warning, TEXT("Failed to load texture from path: %s"), *ImageDir);
        }
    }
}

이 수정으로 인해 더 이상 null 참조로 인한 크래시가 발생하지 않았고, 위젯이 정상적으로 동작했습니다.


5. 드래그 취소 시 메쉬 적용

NativeOnDragCancelled 함수에서 드래그가 취소되었을 때 메쉬를 로드하고 적용하는 방법도 다루었습니다. 이 과정에서 런타임 중에 메쉬를 안전하게 로드하는 방법에 대해 논의했습니다.

코드 예시: 드래그 취소 시 메쉬 로드 및 적용

void UShopPanelSlot::NativeOnDragCancelled(const FDragDropEvent& InDragDropEvent, UDragDropOperation* InOperation)
{
    // 메쉬 로드 시 반드시 성공하도록 확인
    UStaticMesh* LoadedMesh = Cast<UStaticMesh>(StaticLoadObject(UStaticMesh::StaticClass(), nullptr, TEXT("/Game/Meshes/MyMesh.MyMesh")));
    ensureMsgf(LoadedMesh, TEXT("Failed to load mesh at path: /Game/Meshes/MyMesh.MyMesh"));

    // MyMeshComponent가 유효한지 확인한 후 메쉬 할당
    ensureAlwaysMsgf(MyMeshComponent && LoadedMesh, TEXT("Mesh Component is null or Mesh failed to load. Cannot assign mesh."));
    
    if (ensure(MyMeshComponent) && ensure(LoadedMesh))
    {
        MyMeshComponent->SetStaticMesh(LoadedMesh);
    }

    // MyMeshComponent가 유효하지 않을 경우에 대비한 추가 검증
    if (!ensureMsgf(MyMeshComponent, TEXT("MyMeshComponent is null in NativeOnDragCancelled")))
    {
        UE_LOG(LogTemp, Error, TEXT("Cannot set mesh because MyMeshComponent is null"));
    }
}

이 코드에서는 StaticLoadObject를 사용하여 런타임 중 메쉬를 로드하고, 해당 메쉬를 컴포넌트에 할당했습니다.


6. 마무리 및 정리

이번 작업을 통해 PickUpDefense의 UI 시스템에서 발생한 몇 가지 주요 문제를 해결할 수 있었습니다. 특히, 마우스 입력 처리와 위젯 동적 생성 과정에서 발생한 문제를 효과적으로 해결했습니다.

  • 액터 클릭 이벤트 처리: 충돌 설정과 이벤트 바인딩을 통해 클릭 이벤트가 정상적으로 처리되도록 했습니다.
  • 위젯 동적 생성: CreateWidget 함수의 올바른 사용법을 이해하고, 이를 통해 UI를 동적으로 생성하는 방법을 확립했습니다.
  • 버튼 클릭 이벤트 처리: 버튼 클릭 시 특정 동작을 수행할 수 있도록 클릭 이벤트에 함수를 바인딩하여 처리했습니다.

결론

이번 글에서는 언리얼 엔진 5 위젯에서 마우스 입력을 처리하고 위젯을 동적으로 생성하는 방법에 대해 다루었습니다. 위젯 생성 과정에서 발생한 문제들과 그 해결 방법을 통해 UI 시스템을 안정적으로 구현하는 방법을 배울 수 있었습니다.


이 글이 PickUpDefense 게임의 개발 과정을 이해하는 데 도움이 되길 바라며, 다음 글에서는 더욱 흥미로운 주제로 찾아뵙겠습니다. 감사합니다!

profile
게임 프로그래머

0개의 댓글