공부용 이모저모
UE4 - 아이템 상자와 무기 제작 본문
이득우의 언리얼 C++ 챕터 10

받는것과 별개로 프로젝트에 추가는 따로 들어가서 해주어야한다.

언리얼 엔진에는 무기나 엑서서리를 부착할 소켓 이라는 시스템을 제공함.







다음은 실제로 붙혀본다
ABCharacter.h
UPROPERTY(VisibleAnywhere, Category = Weapon)
USkeletalMeshComponent* Weapon;
ABCharacter.cpp
// Sets default values
AABCharacter::AABCharacter()
{
...
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.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "ABWeapon.h"
#include "GameFramework/Actor.h"
#include "ABWeapon.generated.h"
UCLASS()
class ARENABATTLE_API AABWeapon : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AABWeapon();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
public:
UPROPERTY(VisibleAnywhere, Category = Weapon)
USkeletalMeshComponent* Weapon;
};
ABWeapon.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "ABWeapon.h"
// Sets default values
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"));
}
// Called when the game starts or when spawned
void AABWeapon::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void AABWeapon::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}

#include "ABCharacter.h"
#include "ABAnimInstance.h"
#include "ABGhostTrail.h"
#include "ABWeapon.h"
#include "DrawDebugHelpers.h"
AABCharacter::AABCharacter()
{
...
//FName WeaponSocket(TEXT("hand_rSocket"));
//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);
//}
}
void AABCharacter::BeginPlay()
{
Super::BeginPlay();
FName WeaponSocket(TEXT("hand_rSocket"));
auto CurWeapon = GetWorld()->SpawnActor<AABWeapon>(FVector::ZeroVector, FRotator::ZeroRotator);
if (nullptr != CurWeapon)
{
CurWeapon->AttachToComponent(GetMesh(), FAttachmentTransformRules::SnapToTargetNotIncludingScale, WeaponSocket);
}
...
}

아이템 상자 제작


ABItemBox.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "ArenaBattle.h"
#include "GameFramework/Actor.h"
#include "ABItemBox.generated.h"
UCLASS()
class ARENABATTLE_API AABItemBox : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AABItemBox();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
public:
UPROPERTY(VisibleAnywhere, Category = Box)
UBoxComponent* Trigger;
UPROPERTY(VisibleAnywhere, Category = Box)
UStaticMeshComponent* Box;
};
ABItemBox.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "ABItemBox.h"
// Sets default values
AABItemBox::AABItemBox()
{
// 틱을 쓸일이 없으면 꺼두는것이 부하가 적다
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("/Game/InfinityBladeGrassLands/Environments/Breakables/StaticMesh/Box/SM_Env_Breakables_Box1.SM_Env_Breakables_Box1"));
if (SM_BOX.Succeeded())
{
Box->SetStaticMesh(SM_BOX.Object);
}
Box->SetRelativeLocation(FVector(0.0f, -3.5f, -30.0f));
}
// Called when the game starts or when spawned
void AABItemBox::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void AABItemBox::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}

이번에는 폰이 아이템을 획득하도록 아이템상자에 오브젝트 채널을 추가해본다


아이템이므로 충돌로서 막히면 안되며, 주인공은 그걸 감지해야 하므로
주인공을 제외한 모든 개체를 무시로 설정.

박스 컴포넌트에 Overlap 이벤트를 처리하도록 OnComponentBeginOverlap이란 딜리게이트가 존재한다.
멀티 캐스트 다이나믹 딜리게이트이다. 박스를 만들어 콜리전정보를 붙혀서 딜리게이트를 발동시키기로 한다.
ABItemBox.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "ArenaBattle.h"
#include "GameFramework/Actor.h"
#include "ABItemBox.generated.h"
UCLASS()
class ARENABATTLE_API AABItemBox : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AABItemBox();
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;
private:
UFUNCTION()
void OnCharacterOverlap(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
};
ABItemBox.cpp
// Sets default values
AABItemBox::AABItemBox()
{
...
Trigger->SetCollisionProfileName(TEXT("ItemBox"));
//박스에는 NoCollision이라는 콜리전 정보를 세팅한다
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)
{
UE_LOG(LogTemp, Warning, TEXT("Warning"));
}

아이템을 습득하는 코드를 추가한다.
아이템 안에 무기정보를 넣어주는 변수를 추가한다.
ABItemBox.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "ArenaBattle.h"
#include "GameFramework/Actor.h"
#include "ABItemBox.generated.h"
UCLASS()
class ARENABATTLE_API AABItemBox : public AActor
{
...
public:
...
UPROPERTY(EditInstanceOnly, Category = Box)
TSubclassOf<class AABWeapon> WeaponItemClass;
private:
...
};
ABItemBox.Cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "ABItemBox.h"
#include "ABWeapon.h"
// Sets default values
AABItemBox::AABItemBox()
{
...
WeaponItemClass = AABWeapon::StaticClass();
}

이제 캐릭터에게 아이템을 장착시킬 준비를 한다.
ABCharacter.h
class ARENABATTLE_API AABCharacter : public ACharacter
{
...
public:
bool CanSetWeapon(); //웨폰 장착이 가능한가?
void SetWeapon(class AABWeapon* NewWeapon); //웨폰 장착 함수
UPROPERTY(VisibleAnyWhere, Category=Weapon)
class AABWeapon* CurrentWeapon; // 웨폰 변수
private:
...
};
ABCharacter.cpp
...
void AABCharacter::BeginPlay()
{
Super::BeginPlay();
//기존 웨폰 끼우기는 주석처리한다.
//FName WeaponSocket(TEXT("hand_rSocket"));
//auto CurWeapon = GetWorld()->SpawnActor<AABWeapon>(FVector::ZeroVector, FRotator::ZeroRotator);
//if (nullptr != CurWeapon)
//{
// CurWeapon->AttachToComponent(GetMesh(), FAttachmentTransformRules::SnapToTargetNotIncludingScale, WeaponSocket);
//}
}
bool AABCharacter::CanSetWeapon()
{
return { nullptr == CurrentWeapon };
}
void AABCharacter::SetWeapon(AABWeapon* NewWeapon)
{
ABCHECK(nullptr != NewWeapon && nullptr == CurrentWeapon);
FName WeaponSocket(TEXT("hand_rSocket"));
if (nullptr != NewWeapon)
{
NewWeapon->AttachToComponent(GetMesh(), FAttachmentTransformRules::SnapToTargetIncludingScale, WeaponSocket);
NewWeapon->SetOwner(this);
CurrentWeapon = NewWeapon;
}
}
ABItemBox.cpp
#include "ABCharacter.h"
...
void AABItemBox::OnCharacterOverlap(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
//UE_LOG(LogTemp, Warning, TEXT("Warning"));
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);
}
else
{
UE_LOG(LogTemp, Warning, TEXT("Weapon Equiped"));
}
}
}

이번엔 이펙트가 나오게 하면서 박스를 소멸 시켜보기로 한다

ABItemBox.h
{
...
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);
};
ABItemBox.cpp
//Effect 관련 신규 생성
AABItemBox::AABItemBox()
{
// 틱을 쓸일이 없으면 꺼두는것이 부하가 적다
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(40.0f, 42.0f, 30.0f));
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, -30.0f));
//아이템박스라는 콜리전 정보를 세팅한다
Trigger->SetCollisionProfileName(TEXT("ItemBox"));
//박스에는 NoCollision이라는 콜리전 정보를 세팅한다
Box->SetCollisionProfileName(TEXT("NoCollision"));
WeaponItemClass = AABWeapon::StaticClass();
}
void AABItemBox::OnCharacterOverlap(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
//UE_LOG(LogTemp, Warning, TEXT("Warning"));
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, &AABItemBox::OnEffectFinished);
}
else
{
UE_LOG(LogTemp, Warning, TEXT("Weapon Equiped"));
}
}
}
void AABItemBox::OnEffectFinished(class UParticleSystemComponent* PSystem)
{
//이펙트가 끝나면 박스 삭제
Destroy();
}

다음엔 무기의 종류를 바꾸기 위해 블루프린트로 상속시키기로 한다





'UE4 - C++' 카테고리의 다른 글
| UE4 - AI컨트롤러와 비헤이비어 트리 (0) | 2021.12.02 |
|---|---|
| UE4 - 게임데이터와 UI 위젯 (0) | 2021.11.04 |
| UE4 - 충돌 설정과 데미지 전달 (0) | 2021.09.30 |
| UE4 - 잔상 효과 만들기(SPAWN/TIME LINE/POSEABLEMESH) (0) | 2021.09.24 |
| UE4 - 애니메이션 몽타주 (0) | 2021.09.09 |