본문 바로가기
언리얼 최고/언리얼 c++

이득우의 언리얼 C++ 정리 - 10 (아이템 상자와 무기제작)

by Lee_story_.. 2022. 9. 7.
728x90

 

 

*  아래의 책의 내용을 정리한 글입니다!  *

 

오늘은 무기!

 

일단 아래 에셋 다운로드 해주고

 

원하는 소켓 우클릭 >> 프리뷰 에셋 추가

 

적당한위치에 검을 놓아 줍시다!

 

저는 index_o1_r 에 넣었으나 책에서는 hand_r 에 넣었네요!

 

여기 넣는다고 넣어지는게 아니라 한번 보는것!

but 소켓의 위치 설정을 해주어야 그대로 검이 붙어가므로 위치설정은 해줍시다!

 

 

그다음 코드 추가

 

 

 

ABCharacter.h

UPROPERTY(VisibleAnywhere, Category = Weapon)
		USkeletalMeshComponent* Weapon;

 

ABCharacter.cpp

AABCharacter::AABCharacter(){
//~~~


//무기설정


	FName WeaponSocket(TEXT("index_01_r"));
	if (GetMesh()->DoesSocketExist(WeaponSocket))
	{
		Weapon = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("WEAPON"));
		static ConstructorHelpers::FObjectFinder<USkeletalMesh> SK_WEAPON(TEXT("/Game/InfinityBladeWeapons/Weapons/Blade/Swords/Blade_BlackKnight/SK_Blade_BlackKnight.SK_Blade_BlackKnight"));
		if (SK_WEAPON.Succeeded())
		{
			Weapon->SetSkeletalMesh(SK_WEAPON.Object);
		}

		Weapon->SetupAttachment(GetMesh(), WeaponSocket);
	}

 

이제 컴파일 후 보면 검을 쥐고있는 캐릭터가 됩니다!

 

이렇게 검을 직접 부여해줄수도있지만

 

무기를 따로 코드로 작성해서 관리하는것도 가능합니다!

 

그럼 바로 ABWeapon 액터를 만들어 줍시다.

 

 

이제 위에 캐릭터에 추가해준 코드를 그대로 아래에도 추가

 

ABWeapon.h

UPROPERTY(VisibleAnywhere, Category = Weapon)
		USkeletalMeshComponent* Weapon;

 

ABWeapon.cpp

AABWeapon::AABWeapon()
{
 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

    Weapon = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("WEAPON"));
    RootComponent = Weapon;

    static ConstructorHelpers::FObjectFinder<USkeletalMesh> SK_WEAPON(TEXT("/Game/InfinityBladeWeapons/Weapons/Blade/Swords/Blade_BlackKnight/SK_Blade_BlackKnight.SK_Blade_BlackKnight"));
    if (SK_WEAPON.Succeeded())
    {
        Weapon->SetSkeletalMesh(SK_WEAPON.Object);
    }
    Weapon->SetCollisionProfileName(TEXT("NoCollision"));

}

 

 

 

다음으로는 검을 캐릭터에 붙여줍시다.

ABCharacter.cpp

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

	FName WeaponSocket(TEXT("index_o1_r "));
	auto CurWeapon = GetWorld()->SpawnActor<AABWeapon>(FVector::ZeroVector, FRotator::ZeroRotator);
	if (nullptr != CurWeapon)
	{
		CurWeapon->AttachToComponent(GetMesh(), FAttachmentTransformRules::SnapToTargetNotIncludingScale, WeaponSocket);
	}

}

 

이제 무기를 바꿔줄 상자를 만들어 봅시다!

 

책에서는 맵 에셋에 있는 박스를 사용한다고 했지만 저는

 

언리얼 5에있는 일반상자를 사용하겠습니다.

 

 

다시 새 엑터생성

 

 

ABItemBox.h

public:
	UPROPERTY(VisibleAnywhere, Category = Box)
		UBoxComponent* Trigger;

	UPROPERTY(VisibleAnywhere, Category = Box)
		UStaticMeshComponent* Box;

 

 

 

ABItemBox.cpp

 

AABItemBox::AABItemBox()
{
 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = false;

    Trigger = CreateDefaultSubobject<UBoxComponent>(TEXT("TRIGGER"));
    Box = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("BOX"));

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

    Trigger->SetBoxExtent(FVector(40.0f, 42.0f, 30.0f));
    static ConstructorHelpers::FObjectFinder<UStaticMesh> SM_BOX(TEXT("StaticMesh'/Game/LevelPrototyping/Meshes/SM_ChamferCube.SM_ChamferCube'"));
    if (SM_BOX.Succeeded())
    {
        Box->SetStaticMesh(SM_BOX.Object);
    }
    Box->SetRelativeLocation(FVector(0.0f, -3.5f, -30.0f));

}

 

그리고 캐릭터와의 충돌을 위한 박스전용 채널을 만들어줍시다.

 

그리고 새 프로파일도 만들어줍시다.

 

캐릭터도 바꾸어 줍시다.

 

 

 

아래 코드를 추가해주고

 

 

ABItemBox.h

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;
	virtual void PostInitializeComponents() override;

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

 

 

ABItemBox.cpp

    Trigger->SetCollisionProfileName(TEXT("ItemBox"));
    Box->SetCollisionProfileName(TEXT("NoCollision"));

//~~~~


void AABItemBox::PostInitializeComponents()
{
    Super::PostInitializeComponents();
    Trigger->OnComponentBeginOverlap.AddDynamic(this, &AABItemBox::OnCharacterOverlap);
}

void AABItemBox::OnCharacterOverlap(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
    ABLOG_S(Warning);
}

 

박스를 맵에 설치한후 플레이하면

 

 

위 처럼 경고 로그가 뜨게됩니다

 

이제 상자와 겹치면? 무기가 교체되도록 만들겠습니다.

 

 

ABItemBox.h

UPROPERTY(EditInstanceOnly, Category = Box)
		TSubclassOf<class AABWeapon> WeaponItemClass;

 

ABItemBox.cpp

 WeaponItemClass = AABWeapon::StaticClass();//클래스 기본값 설정

 

 

 

 

ABCharacter.h

	bool CanSetWeapon();
	void SetWeapon(class AABWeapon* NewWeapon);

	UPROPERTY(VisibleAnywhere, category = Weapon)
		class AABWeapon* CurrentWeapon;

 

 

 

ABCharacter.cpp

 

bool AABCharacter::CanSetWeapon() {
	return (nullptr == CurrentWeapon);
}

void AABCharacter::SetWeapon(AABWeapon* NewWeapon) {
	ABCHECK(nullptr != NewWeapon && nullptr == CurrentWeapon);
	FName WeaponSocket(TEXT("index_01_r"));
	if (nullptr != NewWeapon) {
		NewWeapon->AttachToComponent(GetMesh(), FAttachmentTransformRules::SnapToTargetNotIncludingScale, WeaponSocket);
		NewWeapon->SetOwner(this);
		CurrentWeapon = NewWeapon;

	}
}

 

 

ABItemBox.cpp

void AABItemBox::OnCharacterOverlap(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
    ABLOG_S(Warning);

    auto ABCharacter = Cast<AABCharacter>(OtherActor);
    ABCHECK(nullptr != ABCharacter);

    if (nullptr != ABCharacter && nullptr != WeaponItemClass)
    {
        if (ABCharacter->CanSetWeapon())
        {
            auto NewWeapon = GetWorld()->SpawnActor<AABWeapon>(WeaponItemClass, FVector::ZeroVector, FRotator::ZeroRotator);
            ABCharacter->SetWeapon(NewWeapon);
        }
        else
        {
            ABLOG(Warning, TEXT("%s can't equip weapon currently."), *ABCharacter->GetName());
        }
    }
}

 

 

 

여기까지 한다면 박스에 두번이상 오버랩 될 시 위와 같은 문구가 뜨게됩니다!

 

 

 

 

ABItemBox.h

private:
    UFUCNTION()
    void OnEffectFinished(class UParticleSystemComponent* PSystem);
    
    public:
    	UPROPERTY(VisibleAnywhere, Category = Effect)
		UParticleSystemComponent* Effect;

 

 

ABItemBox.cpp

 

AABItemBox::AABItemBox()
{

Effect = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("EFFECT")); // 이펙트
    Effect->SetupAttachment(RootComponent);
    
    static ConstructorHelpers::FObjectFinder<UParticleSystem> P_CHESTOPEN(TEXT("/Game/InfinityBladeGrassLands/Effects/FX_Treasure/Chest/P_TreasureChest_Open_Mesh.P_TreasureChest_Open_Mesh"));
    if (P_CHESTOPEN.Succeeded())
    {
        Effect->SetTemplate(P_CHESTOPEN.Object);
        Effect->bAutoActivate = false;
    }
    
    //~~~~
    
    
    void AABItemBox::OnCharacterOverlap(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
    ABLOG_S(Warning);

    auto ABCharacter = Cast<AABCharacter>(OtherActor);
    ABCHECK(nullptr != ABCharacter);

    if (nullptr != ABCharacter && nullptr != WeaponItemClass)
    {
        if (ABCharacter->CanSetWeapon())
        {
            auto NewWeapon = GetWorld()->SpawnActor<AABWeapon>(WeaponItemClass, FVector::ZeroVector, FRotator::ZeroRotator);
            ABCharacter->SetWeapon(NewWeapon);

            Effect->Activate(true);
            Box->SetHiddenInGame(true, true);
            SetActorEnableCollision(false);
            Effect->OnSystemFinished.AddDynamic(this, &AABItemBox::OnEffectFinished);
        }
        else
        {
            ABLOG(Warning, TEXT("%s can't equip weapon currently."), *ABCharacter->GetName());
        }
    }

}


void AABItemBox::OnEffectFinished(UParticleSystemComponent* PSystem) {
    Destroy
}

 

여기 까지하면

 

 

 

보물 상자 오픈까지 되네요!

 

 

그리고 이제 웨폰 블루프린트를 몇개 만들고

 

 

상자마다 웨폰을 설정해주면

 

 

 

이제상자를 열어서 웨폰을 바꿀수 있습니다!

 

 

 

 

끝!

 

 

 

 

 

틀린점이 있다면 댓 달아주세요!

댓글