본문 바로가기

Unreal Engine 4/C++

<Unreal C++> 47 - Action RPG (Character Interface)

 

필요한 개념


<무기 선택에 따라 캐릭터 Material 색 바꿔놓기>

 

플레이어와 적에게도 모두 색 변경을 할것임 -> 인터페이스(Unreal Interface)로 만드는 것이 좋다.

 

* UnrealInterface를 상속받는 ICharacter만듬

// 이 함수로 순수가상함수로 해서 정의 및 구현하도록 강제함
virtual void ChangeColor(FLinearColor InColor) = 0;

 


* CEquipment의 데이터를 관리하는 것UDataAsset 상속받는 CActionData여서 CActionData에 색 변수를 추가한다.

UPROPERTY(BlueprintReadOnly, EditAnywhere)
	FLinearColor EquipmentColor;




// CEquipment.cpp

void ACEquipment::Equip_Implementation()
{
	// IICharacter로 캐스팅 가능하다면 색이 있다는 얘기
	IICharacter* character = Cast<IICharacter>(OwnerCharacter);
	CheckNull(character);
	character->ChangeColor(Color);
}

 

 

// CActionData.cpp

Equipment->SetColor(EquipmentColor);



<Unarmed때는 원래의 색으로 돌아올 수 있게 만듬>

 

* DA_Unarmed 생성

Unarmed는 무기가 따로 없어서, 결국 Unarmed도 DataAsset을 만들어서
Attachment를 None으로 하고 EquipmentClass는 기본 클래스로 둔다.

 

* DA_Unarmed 값 세팅



// CActionComponent.cpp

void UCActionComponent::SetUnarmedMode()
{
	// 이미 무기가 장착되어 있다면 해제하고
	if (!!Datas[(int32)Type])
	{
		ACEquipment* equipment = Datas[(int32)Type]->GetEquipment();
		if (!!equipment)
			equipment->Unequip();
	}

	// 이제 UnamedMode도 장비가 있는 것으로 처리된다.
	ACEquipment* equipment = Datas[(int32)EActionType::Unarmed]->GetEquipment();
	CheckNull(equipment);

	// 색바꾸기 위해(Unarmed도 장비가 있는 것으로 바꿈)
	equipment->Equip();

	ChangeType(EActionType::Unarmed);
}



* BP_CPlayer ActionComponent 변수에 DA_Unarmed 세팅해준다.



* Attachment Spawn해줄때 AttachmentClass, EquipmentClass, DoActionClass Null체크 해줌

 

 

Why?)

Unarmed의 경우에는 따로 Attachment가 없기 때문에, None으로 들어올 것이고, 이에 대해 Null 체크가 필요함

// CActionData.cpp

ex) if (!!AttachmentClass) // 있을 경우만 실행

 

 

 

Tip)
언리얼도 BreakPoint 거는거 기존 하던거랑 똑같다.
BreakPoint걸고 F5누르면 됨
그 상태에서 새로운 창이 뜨게 되고 게임 플레이누르고 플레이하면 BreakPoint에 걸린다.

새로뜬 창에서 값 수정하면 기존값 수정하면 안됨, 기존꺼에 영향미침
값만 확인 후에 Shift+F5로 디버깅모드 중지해야함

만약 디버깅모드에서 BreakPoint가 걸리지 않는다면
언리얼엔진과 VisualStudio 모두 끄고, 다시 열면 정상 작동함


* 이제 UnamedMode도 무기가 됨, CPlayer에서 시작때 UnamedMode로 세팅해준다.

 

이전 까지는 Unarmed로 세팅하면 에러가 있었음, 그런데 이제는 Unarmed도 무기가 되어서 상관이 없다.

 

// CPlayer.cpp

void ACPlayer::BeginPlay()
{
	...
	Action->SetUnarmedMode();
}

 

 

CPlayer.h 추가된 내용


더보기
#pragma once

...
#include "Characters/ICharacter.h"

UCLASS()
class UONLINE_04_ACTION_API ACPlayer : public ACharacter, public IICharacter
{
	GENERATED_BODY()
...

public:
	virtual void ChangeColor(FLinearColor InColor) override;
	
private:
	class UMaterialInstanceDynamic* BodyMaterial;
	class UMaterialInstanceDynamic* LogoMaterial;
};

 

 

 

 

 

 

 

CPlayer.cpp 추가된 내용


더보기
...
#include "Materials/MaterialInstanceConstant.h"
#include "Materials/MaterialInstanceDynamic.h"

void ACPlayer::BeginPlay()
{
	Super::BeginPlay();

	...
    
	UMaterialInstanceConstant* body;
	UMaterialInstanceConstant* logo;

	CHelpers::GetAssetDynamic(&body, "MaterialInstanceConstant'/Game/Materials/M_UE4Man_Body_Inst.M_UE4Man_Body_Inst'");
	CHelpers::GetAssetDynamic(&logo, "MaterialInstanceConstant'/Game/Materials/M_UE4Man_ChestLogo_Inst.M_UE4Man_ChestLogo_Inst'");

	BodyMaterial = UMaterialInstanceDynamic::Create(body, this);
	LogoMaterial = UMaterialInstanceDynamic::Create(logo, this);

	GetMesh()->SetMaterial(0, BodyMaterial);
	GetMesh()->SetMaterial(1, LogoMaterial);
    
	Action->SetUnarmedMode();
}

void ACPlayer::ChangeColor(FLinearColor InColor)
{
	BodyMaterial->SetVectorParameterValue("BodyColor", InColor);
	LogoMaterial->SetVectorParameterValue("BodyColor", InColor);
}

 

 

ICharacter.h


더보기
#pragma once

#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "ICharacter.generated.h"

UINTERFACE(MinimalAPI)
class UICharacter : public UInterface
{
	GENERATED_BODY()
};

class UONLINE_04_ACTION_API IICharacter
{
	GENERATED_BODY()

public:
	virtual void ChangeColor(FLinearColor InColor) = 0;
};

 

 

 

 

 

 

CActionData.h 추가된 내용


더보기
UCLASS()
class UONLINE_04_ACTION_API UCActionData : public UDataAsset
{
	GENERATED_BODY()

public:
	...
	FORCEINLINE FLinearColor GetEquipmentColor() { return EquipmentColor; }

public:
	UPROPERTY(BlueprintReadOnly, EditAnywhere)
		FLinearColor EquipmentColor;

private:
	// 이름 출력해 줄 애
	FString GetLableName(class ACharacter* InOwnerCharacter, FString InName);
};

 

CActionData.cpp 수정된 내용


더보기
...

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

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


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

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

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

	if(!!DoActionClass)
	{
		DoAction = InOwnerCharacter->GetWorld()->SpawnActorDeferred<ACDoAction>(DoActionClass, transform, InOwnerCharacter);
		DoAction->AttachToComponent(InOwnerCharacter->GetMesh(), FAttachmentTransformRules(EAttachmentRule::KeepRelative, true));
		DoAction->SetActorLabel(GetLableName(InOwnerCharacter, "DoAction"));
		DoAction->SetDatas(DoActionDatas);
		UGameplayStatics::FinishSpawningActor(DoAction, transform);
	}
}

FString UCActionData::GetLableName(class ACharacter* InOwnerCharacter, FString InName)
{
	FString str;
	str.Append(InOwnerCharacter->GetActorLabel());
	str.Append("_");
	str.Append(InName);
	str.Append("_");
	// DataAsset은 DA로 world에 나타나는데 DA_를 없앰
	str.Append(GetName().Replace(L"DA_", L""));

	return str;
}

 

 

 

 

 

 

 

CEquipment.h 추가된 내용


더보기
#pragma once

...

UCLASS()
class UONLINE_04_ACTION_API ACEquipment : public AActor
{
	GENERATED_BODY()
	
public:
	...
	FORCEINLINE void SetColor(FLinearColor InColor) { Color = InColor; }

private:
	// 무기에 따른 캐릭터 색
	FLinearColor Color;
};

 

CEquipment.cpp 추가된 내용


더보기
...

void ACEquipment::Equip_Implementation()
{
	...
    
	// IICharacter로 캐스팅 가능하다면 색이 있다는 얘기
	IICharacter* character = Cast<IICharacter>(OwnerCharacter);
	CheckNull(character);
	
	character->ChangeColor(Color);
}

 

CActionComponent.cpp 수정된 내용


더보기
void UCActionComponent::SetUnarmedMode()
{
	// 이미 무기가 장착되어 있다면 해제하고
	if (!!Datas[(int32)Type])
	{
		ACEquipment* equipment = Datas[(int32)Type]->GetEquipment();
		if (!!equipment)
			equipment->Unequip();
	}


	// 이제 UnamedMode도 장비가 있는 것으로 처리된다.
	ACEquipment* equipment = Datas[(int32)EActionType::Unarmed]->GetEquipment();
	CheckNull(equipment);
	
	// 색바꾸기 위해(Unarmed도 장비가 있는 것으로 바꿈)
	equipment->Equip();

	ChangeType(EActionType::Unarmed);

}

 

 

 

 

 

결과