
저번 내용과 이어서 이번에는 캐릭터가 시작하자마자 무기를 습득하는 것이 아닌, 상자를 획득했을때 무기를 장착하도록 구현해보겠다
먼저 오브젝트 채널을 추가해준다
이름은 ItemBox, Default Response 는 Ignore 로 해둔다

이후 ItembBox 프리셋을 만들고 유형을 만들어둔 ItemBox 채널로 한 후 상호작용할 캐릭터를 제외 모두 Ignore 시켜둔다

이때, 캐릭터 프리셋에서도 기본적으로는 ItemBox가 Ignore로 지정되어있기 때문에, Overlap 으로 바꿔주자

이제, 이 둘은 서로 Overlap 이 가능하고, 추후 둘이 충돌하였을때, OnComponentOverlap 함수를 통해 무기를 장착하도록 구현할 것이다
액터를 부모로 하는 상자 클래스를 생성해준다
헤더에는 Trigger 포인터 변수, 스태틱 메시 변수, 파티클 시스템, 아이템 클래스 변수, 이렇게 4가지의 변수와
PostInitializeComponents, 충돌 시를 설정해줄 OnCharacterOverlap, 파티클이 끝난 후 실행되는 함수 OnEffectFinished 를 만들어준다
ABItem.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "ArenaBattle.h"
#include "GameFramework/Actor.h"
#include "ABItem.generated.h"
UCLASS()
class ARENABATTLE_API AABItem : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AABItem();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
virtual void PostInitializeComponents() override;
public:
UPROPERTY(VisibleAnywhere, Category = Box)
UBoxComponent* Trigger;
UPROPERTY(VisibleAnywhere, Category = Box)
UStaticMeshComponent* Box;
UPROPERTY(VisibleAnywhere, Category = Effect)
UParticleSystemComponent* Effect;
UPROPERTY(EditInstanceOnly, Category = Box)
TSubclassOf<class AABWeapon> WeaponItemClass;
private:
UFUNCTION()
void OnCharacterOverlap(UPrimitiveComponent* OVerlappedComp, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
UFUNCTION()
void OnEffectFinished(class UParticleSystemComponent* PSystem);
};
TSubclassof를 사용하면 특정 클래스만 보이게 된다 이를 통해 불필요하게 많은 정보가 뜨는 것을 방지할 수 있다
OnCharacterOverlap과OnEffectFinished는 언리얼에서 제공하는 다이나믹 델리게이트 함수들이다
그렇기 때문에UFUNCTION을 붙여줘야 한다
상자 스태틱 메시와 이펙트는 각각 Box 를 검색 후 찾을 수 있는 SM_Env_Breakables_Box1 과

Chest 를 검색 후 찾을 수 있는 P_TreasureChest_Open_Mesh 이다

둘을 레퍼런스 주소로 삼아 cpp 파일을 작성하자
단 이때, OnCharacterOverlap 함수는 ABCharacter.cpp 파일에 수정이 필요하기 때문에, 아직 작성하지 않는다
ABItem.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "ABItem.h"
#include "ABCharacter.h"
#include "ABWeapon.h"
// Sets default values
AABItem::AABItem()
{
// 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"));
Effect = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("Effect"));
// 트리거를 루트 컴포넌트로 지정
RootComponent = Trigger;
Box ->SetupAttachment(RootComponent);
Effect->SetupAttachment(RootComponent);
//트리거의 사이즈와 크기를 지정
Trigger->SetBoxExtent(FVector(26.5f, 27.5f, 13.5f));
Trigger->SetRelativeScale3D(FVector(1.5f, 1.5f, 1.5f));
static ConstructorHelpers::FObjectFinder<UStaticMesh> SM_BOX
(TEXT("/Game/InfinityBladeGrassLands/Environments/Breakables/StaticMesh/Box/SM_Env_Breakables_Box1.SM_Env_Breakables_Box1"));
if (SM_BOX.Succeeded())
Box->SetStaticMesh(SM_BOX.Object);
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;
}
// 박스와 이펙트의 위치를 조정해줘 자연스럽게 보이도록 구성
Box->SetRelativeLocation(FVector(0.0f, -3.5f, -13.5f));
Effect->SetRelativeLocation(FVector(0.0f, 0.0f, -45.0f));
Trigger->SetCollisionProfileName(TEXT("ItemBox"));
Box ->SetCollisionProfileName(TEXT("NoCollision"));
WeaponItemClass = AABWeapon::StaticClass();
}
// Called when the game starts or when spawned
void AABItem::BeginPlay()
{
Super::BeginPlay();
}
void AABItem::PostInitializeComponents()
{
Super::PostInitializeComponents();
// AddDynamic 매크로를 통해 OnCharacterOverlap 함수를 OnComponentBeginOverlap 델리게이트에 추가
Trigger->OnComponentBeginOverlap.AddDynamic(this, &AABItem::OnCharacterOverlap);
}
void AABItem::OnEffectFinished(UParticleSystemComponent* PSystem)
{
Destroy();
}
ABCharacter.h 에 무기 장착이 가능한지 파악하는 bool 변수, 장착시키는 함수, 현재 장착하고 있는 무기 변수를 만들어준다
ABCharacter.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "ArenaBattle.h"
#include "GameFramework/Character.h"
#include "ABCharacter.generated.h"
UCLASS()
class ARENABATTLE_API AABCharacter : public ACharacter
{
...
public:
...
bool CanSetWeapon();
void SetWeapon(class AABWeapon* NewWeapon);
UPROPERTY(VisibleAnywhere, Category = Weapon)
class AABWeapon* CurrentWeapon;
...
};
이후 저번에 작성한 BeginPlay 에 있는 무기 액터를 장착시키는 스크립트는 지워준다
ABCharacter.cpp
...
bool AABCharacter::CanSetWeapon()
{
return (nullptr == CurrentWeapon);
}
void AABCharacter::SetWeapon(AABWeapon* NewWeapon)
{
FName WeaponSocket(TEXT("hand_rSocket"));
if (nullptr != NewWeapon)
{
NewWeapon->AttachToComponent(GetMesh(), FAttachmentTransformRules::SnapToTargetNotIncludingScale, WeaponSocket);
NewWeapon->SetOwner(this);
CurrentWeapon = NewWeapon;
}
}
...
이제 다시 ABItem.cpp 에 돌아가 OnCharacterOverlap 함수를 완성해주자
ABItem.cpp
...
void AABItem::OnCharacterOverlap(UPrimitiveComponent* OVerlappedComp, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
auto ABCharacter = Cast<AABCharacter>(OtherActor);
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, &AABItem::OnEffectFinished);
}
}
}
...
마지막으로 컴파일 후 캐릭터가 상자와 접촉할때 무기를 습득하며 이펙트가 잘 나오는지 확인하자
