본문 바로가기

Unreal Engine 4/C++

<Unreal C++> 43 - Action RPG(Weapon Attachment)

 

필요한 개념


<캐릭터에다 무기 붙이는 작업>


클래스를 만들고 BP를 만들고 Mesh를 붙여서 모양을 만들어서 붙임
CAttachment의 OnEquip() 함수를 CEquipment에 연결해 놓을 것임
CAttachment는 되게 여러가지로 되어야 한다. 불덩이는 해당 위치에, Melee는 칼을 위치에 ...
CEquipment가 콜될때 CAttachment에 OnEquip()가 콜되는(델리게이트) 원하는 위치에 붙도록 정의함

CAttachment : 해당 무기가 장착될때의 역할을 수행할 클래스

// CAttachment.h

// 재정의 용으로 연결될 함수(BP에서 정의함)
// 무기들은 모양과 행동이 다양
public:
	UFUNCTION(BlueprintImplementableEvent)
		void OnEquip();

	UFUNCTION(BlueprintImplementableEvent)
		void OnUnequip();




// CEquipment.h
// CAttachment에 연결될 델리게이트
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FEquipmentDelegate)
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FUnequipmentDelegate)

UCActionData에서 데이터를 관리하기 때문에 여기서 CAttachment BP를 불러오고(BlueprintReadOnly, EditAnywhere로 변수로 BP에서 세팅가능하도록), 멀티캐스트 AddDynamic세팅을 해준다.


// UCActionData.cpp


void UCActionData::BeginPlay(class ACharacter* InOwnerCharacter)
{
	//Attachment
	{
		// 등장만 시켜놈
		Attachment = InOwnerCharacter->GetWorld()->SpawnActorDeferred<ACAttachment>(AttachmentClass, transform, InOwnerCharacter);
		// 어디에 붙일지는 CAttachment의 OnEquip()때 결정
		// Attachment->AttachToComponent(InOwnerCharacter->GetMesh(), FAttachmentTransformRules(EAttachmentRule::KeepRelative, true));
		UGameplayStatics::FinishSpawningActor(Attachment, transform);
	}


	//Equipment
	{
		// 객체 생성만 완료됨, 등장은 안 시킴
		// 여기서 transform은 확정시키지 않으면 BeginPlay()가 끝나면 확정이 됨, 그때 등장할 위치
		Equipment = InOwnerCharacter->GetWorld()->SpawnActorDeferred<ACEquipment>(EquipmentClass, transform, InOwnerCharacter);
		// mesh에다가 붙여넣음
		Equipment->AttachToComponent(InOwnerCharacter->GetMesh(), FAttachmentTransformRules(EAttachmentRule::KeepRelative, true));
		// 할당 받은 데이터를 Equipment로 넘겨줌
		Equipment->SetData(EquipmentData);

		// FinishSpawningActor() : 확정해 등장시킬 액터와 Transform을 입력
		// transform : 확정되서 등장시킬 위치
		UGameplayStatics::FinishSpawningActor(Equipment, transform);

		// 연결해줌
		Equipment->OnEquipmentDelegate.AddDynamic(Attachment, &ACAttachment::OnEquip);
		Equipment->OnUnequipmentDelegate.AddDynamic(Attachment, &ACAttachment::OnUnequip);
	}
}



* 게임을 실행해보면, BP_CAttachment_OneHand, CEquipment가 world에 스폰된다고 뜨지만, 이름이 뭉쳐져 있지 않고 누구 소속인지 모름(헷갈림)

-> Label(레이블), 이름 지정해줌
-> 각 클래스의 이름을 구분해서 보기 편하게 액터 레이블에 스폰될 해당 클래스의 이름을 연결해줌(즉, 출력되는 밖에 이름만 바꾸고, 안에 툴팁 부분에 Name은 그대로로 유지해둠)
SetActorLabel()로 레이블 세팅이 가능하다.

ex)
Attachment->SetActorLabel(InOwnerCharacter->GetActorLabel() + "_Attachment");
Equipment->SetActorLabel(InOwnerCharacter->GetActorLabel() + "_Equipment");


이런식으로 어떤 캐릭터의 클래스인지 알수가 있다.


이런식으로 나온다.

(GetName()으로 하면 숫자가 붙을 수 있어서 Label으로 구하면 보기가 편함)

CAttachment에서 BlueprintCallable(블루프린트에서 함수 콜 할수 있도록) 속성 세팅하고 AttachTo()함수를 정의(캐릭터의 Mesh 특정 소켓에 이 클래스를 붙이도록)

void ACAttachment::AttachTo(FName InSoketName)
{
	// 캐릭터 Mesh에 바로 Attach 시킴
	AttachToComponent(OwnerCharacter->GetMesh(), FAttachmentTransformRules(EAttachmentRule::KeepRelative, true), InSoketName);
}

 

 

* BP에서 정의
BP에서 BP_CAttachment_OneHand 정의함
BeginPlay()시에는 AttachTo() Holster소켓에 붙이고
OnEquip() 재정의해서 AttachTo() Hand_Sword 소켓에 붙임
UnEquip()에서는 재정의해서 AttachTo() 다시 Holster소켓에 붙여줌

 

 

 



Tip)
크래쉬 이후에 프로젝트를 열때에는 컴파일이 모두 완료된 뒤에 열어야 컴파일 결과와 에디터가 꼬이는 경우를 방지할 수 있음

 

 

 

 

 

CAttachment.h


더보기
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "CAttachment.generated.h"

UCLASS()
class UONLINE_04_ACTION_API ACAttachment : public AActor
{
	GENERATED_BODY()

protected:
	// BP에서 접근할 수 있도록
	// 붙일 소켓이름을 받아서 캐릭터 Mesh에 붙여버림
	UFUNCTION(BlueprintCallable)
		void AttachTo(FName InSoketName);
	
public:	
	ACAttachment();

protected:
	virtual void BeginPlay() override;

	// 재정의 용으로 연결될 함수(BP에서 정의함)
	// 무기들은 모양과 행동이 다양
public:
	UFUNCTION(BlueprintImplementableEvent)
		void OnEquip();

	UFUNCTION(BlueprintImplementableEvent)
		void OnUnequip();

	// BP 접근위해
protected:
	UPROPERTY(BlueprintReadOnly)
		class ACharacter* OwnerCharacter;

	UPROPERTY(BlueprintReadOnly)
		class UCStateComponent* State;

	UPROPERTY(BlueprintReadOnly)
		class UCStatusComponent* Status;
};

 

 

 

 

 

 

 

CAttachment.cpp


더보기
#include "CAttachment.h"
#include "Global.h"
#include "GameFramework/Character.h"
#include "Components/SkeletalMeshComponent.h"
#include "Components/CStateComponent.h"
#include "Components/CStatusComponent.h"

ACAttachment::ACAttachment()
{
}

void ACAttachment::BeginPlay()
{
	// 찾아놓음
	OwnerCharacter = Cast<ACharacter>(GetOwner());
	State = CHelpers::GetComponent<UCStateComponent>(OwnerCharacter);
	Status = CHelpers::GetComponent<UCStatusComponent>(OwnerCharacter);


	Super::BeginPlay();
}

void ACAttachment::AttachTo(FName InSoketName)
{
	// 캐릭터 Mesh에 바로 Attach 시킴
	AttachToComponent(OwnerCharacter->GetMesh(), FAttachmentTransformRules(EAttachmentRule::KeepRelative, true), InSoketName);
}

 

 

CActionData.h 추가된 내용


더보기
...

UCLASS()
class UONLINE_04_ACTION_API UCActionData : public UDataAsset
{
	GENERATED_BODY()
    ...

public:
	...
	// BP에서 해당 클래스BP 세팅가능
	UPROPERTY(BlueprintReadOnly, EditAnywhere)
		TSubclassOf<class ACAttachment> AttachmentClass;

private:
	...
	class ACAttachment* Attachment;
};

 

 

 

 

 

 

 

CActionData.cpp 추가된 내용


더보기
...
#include "CAttachment.h"

void UCActionData::BeginPlay(class ACharacter* InOwnerCharacter)
{
	FTransform transform;

	//Attachment
	{
		// 등장만 시켜놈
		Attachment = InOwnerCharacter->GetWorld()->SpawnActorDeferred<ACAttachment>(AttachmentClass, transform, InOwnerCharacter);
		Attachment->SetActorLabel(InOwnerCharacter->GetActorLabel() + "_Attachment");
		// 어디에 붙일지는 CAttachment의 OnEquip()때 결정
		// Attachment->AttachToComponent(InOwnerCharacter->GetMesh(), FAttachmentTransformRules(EAttachmentRule::KeepRelative, true));
		UGameplayStatics::FinishSpawningActor(Attachment, transform);
	}
    
    ...
}

 

 

 

 

 

결과