저번 내용과 이어서 이번에는 캐릭터가 시작하자마자 무기를 습득하는 것이 아닌, 상자를 획득했을때 무기를 장착하도록 구현해보겠다
먼저 오브젝트 채널을 추가해준다
이름은 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);
}
}
}
...
마지막으로 컴파일 후 캐릭터가 상자와 접촉할때 무기를 습득하며 이펙트가 잘 나오는지 확인하자