[UE5] Lena: Dev Diary #17 - 아이템 캡처 및 충돌 처리 개선

ChangJin·2024년 8월 2일
0

Unreal Engine5

목록 보기
90/114
post-thumbnail

2024.08.02

깃허브!풀리퀘!
https://github.com/ChangJin-Lee/Project-Lena https://github.com/ChangJin-Lee/Project-Lena/pull/16

이번 포스팅에서는 아이템 캡처 이미지 저장 및 충돌 처리와 관련된 문제를 해결하는 과정을 다룹니다. SceneCaptureComponent2D를 활용한 이미지 캡처 방법, 다중 충돌 처리, 그리고 히트 결과를 효과적으로 필터링하는 방법을 설명합니다.

진행상황

  • ✅ 아이템 캡처 이미지 저장
    • ✅ SceneCaptureComponent2D를 활용한 메시를 이미지로 저장하는 기능 구현
    • ✅ 아이템 이미지 저장 경로 설정 및 파일 생성
  • ✅ 충돌 처리
    • ✅ 다중 충돌 결과 필터링
    • ✅ 불필요한 충돌 결과 제거
  • ✅ UI 및 HUD
    • ✅ 캡처된 이미지 UI에 표시
    • ✅ HUD에서 메테리얼을 활용한 이미지 표시 기능 구현
상호작용 관련 Class 상속 관계도
인벤토리 관련 Class 상속 관계도

1. SceneCaptureComponent2D를 활용한 이미지 캡처 및 저장

구현 방법

SceneCaptureComponent2D와 RenderTarget을 설정하고, 캡처한 이미지를 렌더 타깃으로 내보내기 위해 다음과 같은 작업을 수행했습니다.

  1. SceneCaptureComponent2D를 설정하여 특정 메시 컴포넌트만 캡처하고, 이를 RenderTarget에 저장합니다.
  2. RenderTarget을 에디터에서 마우스 우클릭하여 Material로 변환합니다.

코드 구현

먼저, SceneCaptureComponent2D를 설정하여 특정 메시 컴포넌트만 캡처하고, 이를 RenderTarget에 저장하는 코드를 작성합니다.

void ABase_Item::BeginPlay()
{
    Super::BeginPlay();

    // SceneCaptureComponent2D 설정
    SceneCaptureComponent2D->TextureTarget = RenderTarget;
    SceneCaptureComponent2D->ShowOnlyComponent(MeshComponent);  // 특정 메시 컴포넌트만 캡처
    SceneCaptureComponent2D->CaptureScene();
}

RenderTarget을 Material로 변환

  1. 에디터에서 생성된 RenderTarget을 선택합니다.
  2. 마우스 우클릭하여 Create Material 옵션을 선택합니다.
  3. 생성된 Material을 더블 클릭하여 에디터에서 엽니다.
  4. Material의 도메인을 User Interface로 설정하고, 블렌드 모드를 Translucent로 변경합니다.
  5. 캡처된 이미지가 제대로 표시되도록 Material의 RGB를 Base Color에, 알파 값을 Opacity에 연결합니다.

렌더 타깃 내보내기

SceneCaptureComponent2D로 캡처한 이미지를 RenderTarget에 저장하고, 이를 Material로 변환하여 UI에서 사용할 수 있도록 합니다. 이를 통해 동적으로 캡처한 이미지를 게임 내 UI에 표시할 수 있습니다.

void ABase_Item::CaptureSceneAndSaveImage()
{
    if (SceneCaptureComponent2D && RenderTarget)
    {
        // Capture the scene
        SceneCaptureComponent2D->CaptureScene();

        // Save the RenderTarget as a Texture
        FString Directory = FPaths::ProjectContentDir() / TEXT("RenderTargets");
        FString FileName = TEXT("CapturedImage");

        IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
        if (!PlatformFile.DirectoryExists(*Directory))
        {
            PlatformFile.CreateDirectoryTree(*Directory);
        }

        FString AbsoluteFilePath = Directory / (FileName + TEXT(".png"));
        FTextureRenderTargetResource* RenderTargetResource = RenderTarget->GameThread_GetRenderTargetResource();
        
        TArray<FColor> OutBMP;
        FReadSurfaceDataFlags ReadPixelFlags(RCM_UNorm);
        ReadPixelFlags.SetLinearToGamma(false);
        
        RenderTargetResource->ReadPixels(OutBMP, ReadPixelFlags);

        // Compress image array to PNG format using PNGCompressImageArray
        TArray64<uint8> CompressedPNG;
        FImageUtils::PNGCompressImageArray(RenderTarget->SizeX, RenderTarget->SizeY, OutBMP, CompressedPNG);

        if (FFileHelper::SaveArrayToFile(CompressedPNG, *AbsoluteFilePath))
        {
            UE_LOG(LogTemp, Log, TEXT("Successfully saved screenshot to %s"), *AbsoluteFilePath);
        }
        else
        {
            UE_LOG(LogTemp, Error, TEXT("Failed to save screenshot to %s"), *AbsoluteFilePath);
        }
    }
}

위 코드를 통해 SceneCaptureComponent2D로 캡처한 이미지를 RenderTarget에 저장하고, 이를 Material로 변환하여 UI에 표시할 수 있습니다.

2. 다중 충돌 결과 필터링

아이템이 스폰될 때, 동일한 액터 내의 여러 메시 컴포넌트로 인해 다중 충돌이 발생하는 문제를 해결하기 위해 TSet을 사용하여 중복된 충돌 결과를 필터링했습니다.

초기 코드

void AShooterPlayerController::CheckPickUpItemSweep()
{
    ACharacter* Character_ = UGameplayStatics::GetPlayerCharacter(GetWorld(), 0);
    FVector Start = Character_->GetActorLocation();
    FVector End = Character_->GetActorLocation();

    TArray<FHitResult> HitResults;
    FCollisionQueryParams Params;
    float Radius = 130.0f;

    if (GetWorld()->SweepMultiByChannel(HitResults, Start, End, FQuat::Identity, ECC_Visibility, FCollisionShape::MakeSphere(Radius), Params))
    {
        Base_Character->GroundItemsComponent->Items.Empty();

        for (FHitResult HitResult : HitResults)
        {
            AActor* HitActor = HitResult.GetActor();
            if (HitActor)
            {
                ABase_Item* Item = Cast<ABase_Item>(HitActor);
                if (Item)
                {
                    UE_LOG(LogTemp, Warning, TEXT("HitResults.Num() : %s"), *Item->GetName());
                    if (Base_Character)
                    {
                        Base_Character->CheckGroundItem(Item);
                    }
                }
            }
        }
    }
}

개선된 코드

void AShooterPlayerController::CheckPickUpItemSweep()
{
    ACharacter* Character_ = UGameplayStatics::GetPlayerCharacter(GetWorld(), 0);
    FVector Start = Character_->GetActorLocation();
    FVector End = Character_->GetActorLocation();

    TArray<FHitResult> HitResults;
    FCollisionQueryParams Params;
    float Radius = 130.0f;

    if (GetWorld()->SweepMultiByChannel(HitResults, Start, End, FQuat::Identity, ECC_Visibility, FCollisionShape::MakeSphere(Radius), Params))
    {
        TSet<AActor*> ProcessedActors;
        Base_Character->GroundItemsComponent->Items.Empty();

        for (FHitResult HitResult : HitResults)
        {
            AActor* HitActor = HitResult.GetActor();
            if (HitActor && !ProcessedActors.Contains(HitActor))
            {
                ProcessedActors.Add(HitActor);
                ABase_Item* Item = Cast<ABase_Item>(HitActor);
                if (Item)
                {
                    UE_LOG(LogTemp, Warning, TEXT("HitResults.Num() : %s"), *Item->GetName());
                    if (Base_Character)
                    {
                        Base_Character->CheckGroundItem(Item);
                    }
                }
            }
        }
    }
}

이 개선된 코드에서는 TSet을 사용하여 중복된 히트 결과를 제거하고, 각 액터에 대해 한 번만 처리되도록 했습니다.


3. SceneCaptureComponent2D로 생성한 이미지 UI에 표시

SceneCaptureComponent2D로 캡처한 이미지를 UI에 표시하기 위해 다음과 같은 작업을 수행했습니다. 캡처한 이미지를 Material로 만들고, 이 Material을 UI에 표시했습니다. UI에서는 아이템에 해당하는 메테리얼이 Valid한지 체크하고 이를 반환하여 이미지에서 보여주도록 만들었습니다.

Make Brush from Matatial, Texture로 이미지를 반환하는 부분
아이템 이름에 해당하는 메테리얼을 반환하는 부분

결론

이번 작업을 통해 SceneCaptureComponent2D를 활용한 아이템 이미지 캡처 및 저장 기능을 구현하고, 다중 충돌 결과를 효과적으로 필터링하는 방법을 배웠습니다.

profile
게임 프로그래머

0개의 댓글