리얼 개발

[UE5] 아이템 획득하기 본문

Unreal Engine5

[UE5] 아이템 획득하기

econo-my 2024. 7. 11. 19:37

 

특정 구역에 들어가면 캐릭터가 아이템을 획득할 수 있게 만들어보자.

 

 

우선 ItemBox 라는 클래스를 만들어 우리가 들어갔을 때 반응할 액터를 만들자.

public:	
	// Sets default values for this actor's properties
	AItemBox();

protected:
	virtual void BeginPlay() override;

public:
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Box")
	TObjectPtr<class UBoxComponent> Trigger;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Box")
	TObjectPtr<class UStaticMeshComponent> Mesh;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Box")
	TObjectPtr<class UItemData> Item;

	UFUNCTION()
	void OnOverlapBegin(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, 
    			UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, 
    			bool bFromSweep, const FHitResult& SweepResult);

 

 

#include "ItemBox.h"
#include "Components/StaticMeshComponent.h"
#include "Components/BoxComponent.h"
#include "CharacterItemInterface.h"
#include "ItemData.h"

// Sets default values
AItemBox::AItemBox()
{
    Trigger = CreateDefaultSubobject<UBoxComponent>(TEXT("Trigger"));
    Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));

    RootComponent = Trigger;
    Mesh->SetupAttachment(Trigger);

    Trigger->SetBoxExtent(FVector(40.0f, 40.0f, 40.0f));
    Trigger->OnComponentBeginOverlap.AddDynamic(this, &AItemBox::OnOverlapBegin);
    Trigger->SetCollisionProfileName("Item");

    Mesh->SetCollisionProfileName("NoCollision");

    static ConstructorHelpers::FObjectFinder<UStaticMesh> MeshRef(TEXT("/Script/Engine.StaticMesh'/Game/LevelPrototyping/Meshes/SM_Cube.SM_Cube'"));
    if (MeshRef.Object)
    {
        Mesh->SetStaticMesh(MeshRef.Object);
    }
}

void AItemBox::BeginPlay()
{
    Super::BeginPlay();
	
		//구역 박스를 표시할 디버그
    DrawDebugBox(GetWorld (), GetActorLocation(), FVector(40.0f, 40.0f, 40.0f), FColor::Red, true);
}

void AItemBox::OnOverlapBegin(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
    UE_LOG(LogTemp, Display, TEXT("닿음?"));
    if (Item == nullptr)
    {
        Destroy();
        return;
    }

    ICharacterItemInterface* Character = Cast<ICharacterItemInterface>(OtherActor);
    if (Character)
    {
        Character->TakeItem(Item);
    }

    Destroy();
}

생성자는 박스의 생김새와 관련된 코드이며, 취향껏 조정할 수 있다.

 

생성자에서 OnComponentBeginOverlap 키워드를 볼 수 있는데, 이는 PrimitiveComponent에 선언된 델리게이트이다. 

DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_SixParams
( FComponentBeginOverlapSignature, UPrimitiveComponent, 
OnComponentBeginOverlap, UPrimitiveComponent*, OverlappedComponent,
AActor*, OtherActor, UPrimitiveComponent*, OtherComp, 
int32, OtherBodyIndex, bool, bFromSweep, const FHitResult &, SweepResult);

 

 

BoxComponent는 이를 상속받고 있기 때문에 BoxComponent로 접근할 수 있다. 이를 이용해 박스 컴포넌트에 물체가 들어왔을 때 감지할 수 있다. 델리게이트 규칙에 맞춰 OnOverlapBegin이라는 함수를 정의해서 AddDynamic을 통해 바인딩 시켜주면 캐릭터가 해당 구역에 들어왔는지 감지할 수 있다.

 

 

아이템을 얻을 수 있는 구역은 하나의 캐릭터가 아닌 다양한 캐릭터들이 들어올 수 있기 때문에 특정 캐릭터의 헤더파일을 인클루드 하는 것이 아닌 인터페이스를 통해 접근하도록 한다. 그러면 아이템 박스와 캐릭터간 의존성을 줄일 수 있으며 확장이 필요한 경우 쉽게 확장할 수 있다.

 

인터페이스 코드는 다음과 같다.

class PRACTICE_API ICharacterItemInterface
{
	GENERATED_BODY()

	// Add interface functions to this class. This is the class that will be inherited to implement this interface.
public:
	virtual void TakeItem(class UItemData* ItemData) = 0;
};

 

 

아이템은 종류가 여러가지일 수 있으니, 별도로 PrimaryDataAsset 클래스를 사용해서 관리할 수 있다. 여기서는 한가지 아이템 종류가 있다고 가정한다.

UCLASS()
class PRACTICE_API UItemData : public UPrimaryDataAsset
{
	GENERATED_BODY()
	
public:
	UPROPERTY(EditAnywhere, Category = "Item")
	TObjectPtr<class USkeletalMesh> Item;
};

 

이 클래스를 기반으로 DataAsset 블루프린트를 만들어준 뒤

 

캐릭터가 가지게 할 아이템 메시를 선택해준다.

 

 

 

이제 마지막으로 캐릭터 클래스로 돌아가 인터페이스를 구현한 뒤 아이템을 캐릭터의 스켈레탈 메시 소켓에 끼우면 끝이다! 여기선 ThirdPerson 템플릿의 캐릭터를 사용했다.

//인터페이스 헤더 추가 및 상속
#include "CharacterItemInterface.h"
class APracticeCharacter : public ACharacter, public ICharacterItemInterface

//아이템 메시와 인터페이스 구현 함수
public:
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Item")
	TObjectPtr<class USkeletalMeshComponent> Item;

	virtual void TakeItem(class UItemData* ItemData) override;

 

 

생성자에서 Item에 컴포넌트를 만들어준 뒤 스켈레탈 메시 소켓을 찾아 끼워주면 된다.

//캐릭터의 생성자 코드
Item = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Item"));
Item->SetupAttachment(GetMesh(), TEXT("hand_rSocket"));

 

 

Item의 스켈레탈 메시는 인터페이스에 있는 함수를 구현함으로써 초기화 세팅해줄 수 있다.

void APracticeCharacter::TakeItem(UItemData* ItemData)
{
	Item->SetSkeletalMesh(ItemData->Item);
}

 

 

ItemBox를 월드에 배치한 뒤 아까 만들어뒀던 DataAsset을 넣어주면 캐릭터가 이제 아이템을 획득할 수 있다!

 

'Unreal Engine5' 카테고리의 다른 글

[UE5] 적 AI 공격 구현하기  (0) 2024.07.29
[UE5] AI 적 조종하기  (0) 2024.07.29
[UE5] Enhanced Input을 활용한 이동  (1) 2024.07.01
[UE5] 캐릭터 회전 설정값  (0) 2024.07.01
[UE5] 물체 잡기  (0) 2024.06.29