ftftgop3 2024. 4. 17. 20:15

ABItemBox 클래스 생성

플레이어를 감지하는 콜리전 박스, 박스 렌더링을 담당 하는 스태틱 메시로 구성

루트에는 콜리전 컴포넌트를 배치, 자식에는 스태틱 메시 컴포넌트 추가

 

아이템 상자 메시의 빌드 스케일 조정

게임 로직에서 트랜스폼의 스케일 값을 조정 하지 않아도 되는 편의성 제공

변경 내용 적용 후 화면

 

ABItemBox 예제

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

	UPROPERTY(VisibleAnyWhere, Category = Box)
		UStaticMeshComponent* Box;
        
        
 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("/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));

}

 

오브젝트 채널 추가

ItemBox 라는 오브젝트 채널 생성 반응 무시로 지정

ItemBox 프리셋 추가 후, ABCharacter 오브젝트에 겹침 반응 하게 설정

ABCharacter 프리셋에 가서 ItemBox 프리셋 겹침으로 반응 설정

 

ABItemBox

   UCLASS()
class ARENABATTLE_API AABItemBox : public AActor
{
	GENERATED_BODY()
   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);

}

AABItemBox::AABItemBox()
{
	//Box 컴포넌트 콜리전 프리셋 지정
	Trigger->SetCollisionProfileName(TEXT("ItemBox"));
	//스테틱 메시 콜리전 프리셋 지정
	Box->SetCollisionProfileName(TEXT("NoCollision"));
}

void AABItemBox::PostInitializeComponents()
{
	Super::PostInitializeComponents();
	//Box compoent의 beingoverlap 델리게이트에 함수 등록
	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);
}

 

아이템 습득

아이템 상자를 통과하면 플레이어에게 아이템을 주워주는 기능 구현

아이템 상자에 클래스 정보 저장

플레이어 와 겹침 이벤트 시 아이템 상자의 클래스 정보 값을 토대로 아이템 생성

 

특정 클래싀 상속 받은 클래스 목록을 보여주는 TsubCalssof 키워드 사용

 

#include "ABWeapon.h" //generated.h 전에 참조 
#include "ABItemBox.generated.h"

UCLASS()
class ARENABATTLE_API AABItemBox : public AActor
{
	GENERATED_BODY()
	public:
    UPROPERTY(EditInstanceOnly, Category = Box)
		TSubclassOf<class AABWeapon> WeaponItemClass;
        
}

// Sets default values
AABItemBox::AABItemBox()
{
	WeaponItemClass = AABWeapon::StaticClass();
}

 

캐릭터에 무기 장착하는 함수 구현

캐릭터에 무기가 없으면 hand_rSocket에 무기 장착

UCLASS()
class ARENABATTLE_API AABCharacter : public ACharacter
{
	GENERATED_BODY()
    
    public:
    	bool CanSetWeapon();
	void SetWeapon(class AABWeapon* NewWeapon);

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

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

void AABCharacter::SetWeapon(class AABWeapon* NewWeapon)
{
	//새로운 무기 값이 null 아니고, 현재 무기가 없을 떄
	ABCHECK(nullptr != NewWeapon && nullptr == CurrentWeapon);
	//소캣 이름 정의
	FName WeaponSocket(TEXT("hand_rSocket"));
	if (nullptr != NewWeapon)
	{
		//새로운 무기를 캐릭터 모델의 소캣에 부착
		NewWeapon->AttachToComponent(GetMesh(), FAttachmentTransformRules::SnapToTargetNotIncludingScale, WeaponSocket);
		//새로운 무기의 소유권을 캐릭터로 이전(자식 관계)
		NewWeapon->SetOwner(this);
		CurrentWeapon = NewWeapon;
	}
}

 

Item box에서 겹침 이벤트

충돌한 캐릭터 감지 후 무기 생성 후 캐릭터에 전달

이미 무기를 장착하고 있으면 사용 할 수 없다는 로그 발생

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);

	//둘 객체 nullptr 체크
	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());
		}
	}
}

 

이펙트 추가

아이템 습득 시 이펙트 재생 후 완료 되면 아이템 상자 제거

1. 상자 액터에 파티클 컴포넌트 추가

2. OnSystemFinished 델리게이트를 활용 재생 완료 시점을 파악

3. 아이템 상자 제거

 

OnSystemFinished 은 다이내믹 형식이기 때문에 UFUNCTION 매크로 선언

C++ 람다 형식은 사용 할 수 없다

 

ABitembox 예제 

UCLASS()
class ARENABATTLE_API AABItemBox : public AActor
{
	GENERATED_BODY()
    
    public:
    	UPROPERTY(VisibleAnyWhere, Category = Effect)
		UParticleSystemComponent* Effect;
    private:
    	UFUNCTION()
		void OnEffectFinished(class UParticleSystemComponent* PSysyem);
}

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);

	//둘 객체 nullptr 체크
	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();
}

 

액터 컴포넌트 렌더링 비활성화 함수 비교

SetVisibility 옵션을 비활성화 시 에디터 화면, 게임 플레이 화면에서 모두 사라진다

HiddeninGame 옵션을 비활성화 시 에디터 레벨 작업 할 떄는 렌더링 되고 게임 플레이 중에는 사라진다

 

새로운 무기 추가

ABWeapon 상속 받는 블루프린트 클래스 생성

새로운 무기 생성 시 계속해 C++ 클래스 추가는 비 효율적

스켈레탈 메시 컴포넌트를 도끼로 변경

아이템 상자의 컴포넌트에서 Webponclass 를 위에 생성한 BP_WeaponAxe 로 변경