반응형

 

 

이번 포스팅은 이전에 만든 Dissolve 마테리얼과 Refraction 마테리얼을 조작하는 C++ 컴포넌트를 작성해서 키보드를 누르면 클록킹 발동, 해제를 구현하겠습니다.

 

 

우선 Actor Component C++ 클래스를 생성합니다.

 

UCpt_Cloaking.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "Cpt_Cloaking.generated.h"


UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class CLOAKINGTUTORIAL_API UCpt_Cloaking : public UActorComponent
{
	GENERATED_BODY()

public:
	//! SKMesh
	UPROPERTY()
		class USkeletalMeshComponent* m_pSkeletal_Mesh;

	UPROPERTY()
		class USkeletalMeshComponent* m_pSkeletal_Mesh2Pass;

	//! Material Instance 저장용 Array
	UPROPERTY()
		TArray<class UMaterialInstanceDynamic*> m_pSpawnMaterial;

public :
	UPROPERTY(EditAnywhere)
		float m_fCloakingAmount_Max = 1.5f;	//! 마테리얼의 Amount의 Max값입니다.

	UPROPERTY(EditAnywhere)
		float m_fCloakingAmount_Min = -0.5f;	//! 마테리얼의 Amount의 Min값입니다.

	UPROPERTY(EditAnywhere)
		float m_fCloakingSpeed = 0.3f;		//! 클로킹 변할때 속도 조절값입니다.

private:
	bool m_bInit = false;			//! Check Init
	bool m_bIsCloaking = false;		//! 클로킹 발동중 체크
	int m_nState_Cloaking = -1;		//! 클로킹 상태 체크(0 = 무상태, 1 = 클로킹 발동중, 2 = 클로킹 해제중)


	float m_fCurrAmount = 0.0f;		//! 클로킹 수치 변수 현재값
	float m_fDestAmount = 0.0f;		//! 클로킹 수치 변수 목표값
public:
	UCpt_Cloaking();
	virtual void BeginPlay() override;
	virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;

	void Init(class USkeletalMeshComponent* pMainMesh, class USkeletalMeshComponent* p2PassMesh);

	UFUNCTION(BlueprintCallable, Category = "Event")
		void Active_Cloaking();
	UFUNCTION(BlueprintCallable, Category = "Event")
		void DeActive_Cloaking();
	bool GetIsCloaking() { return m_bIsCloaking; }
};

 

 

UCpt_Cloaking.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "Cpt_Cloaking.h"
#include "Materials/MaterialInstanceDynamic.h"
#include "Materials/MaterialInterface.h"
#include "Kismet/KismetMaterialLibrary.h"
#include "Components/SkeletalMeshComponent.h"
#include "Kismet/KismetMathLibrary.h"
#include "Kismet/GameplayStatics.h"


UCpt_Cloaking::UCpt_Cloaking()
{
	PrimaryComponentTick.bCanEverTick = true;
}


// Called when the game starts
void UCpt_Cloaking::BeginPlay()
{
	Super::BeginPlay();
}


void UCpt_Cloaking::Init(class USkeletalMeshComponent* pMainMesh, class USkeletalMeshComponent* p2PassMesh)
{
	//! Init variables
	//! 변수들을 초기화합니다.
	m_bInit = false;
	m_bIsCloaking = false;

	m_nState_Cloaking = 0;

	m_fCurrAmount = 0.0f;
	m_fDestAmount = 0.0f;

	m_pSkeletal_Mesh = pMainMesh;
	m_pSkeletal_Mesh2Pass = p2PassMesh;

	//! 인자 값으로 들어온 SKMesh를 점검합니다.
	if (m_pSkeletal_Mesh != nullptr && m_pSkeletal_Mesh2Pass != nullptr)
	{
		m_bInit = true;
	}

	//! SKMesh에 들어있는 마테리얼들을 추출해서 m_pSpawnMaterial List에 추가합니다.
	TArray<UMaterialInterface*> pMaterials = m_pSkeletal_Mesh->GetMaterials();
	for (int i = 0; i < pMaterials.Num(); i++)
	{
		UMaterialInstanceDynamic* pSpawnMaterial = UKismetMaterialLibrary::CreateDynamicMaterialInstance(m_pSkeletal_Mesh->GetWorld(), pMaterials[i]);

		m_pSkeletal_Mesh->SetMaterial(i, pSpawnMaterial);
		m_pSpawnMaterial.Add(pSpawnMaterial);
	}

	//! 위와 마찬가지로 2Pass용 SKMesh의 마테리얼을 추출합니다.
	TArray<UMaterialInterface*> pMaterials2 = m_pSkeletal_Mesh2Pass->GetMaterials();
	for (int i = 0; i < pMaterials.Num(); i++)
	{
		UMaterialInstanceDynamic* pSpawnMaterial = UKismetMaterialLibrary::CreateDynamicMaterialInstance(m_pSkeletal_Mesh2Pass->GetWorld(), pMaterials2[i]);

		m_pSkeletal_Mesh2Pass->SetMaterial(i, pSpawnMaterial);
		m_pSpawnMaterial.Add(pSpawnMaterial);
	}
}


void UCpt_Cloaking::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	//! 상태 변수 기반으로 클록킹 값 업데이트합니다.
	if (m_nState_Cloaking != 0)
	{
		m_fCurrAmount += DeltaTime * m_fCloakingSpeed;
		float fLerp = 0.0f;
		if (m_nState_Cloaking == 1)
		{
			fLerp = UKismetMathLibrary::Lerp(m_fCloakingAmount_Min, m_fDestAmount, m_fCurrAmount);
		}
		else
		{
			fLerp = UKismetMathLibrary::Lerp(m_fCloakingAmount_Max, m_fDestAmount, m_fCurrAmount);

		}

		//! SKMesh에서 추출한 마테리얼의 값을 조작합니다.
		for (int i = 0; i < m_pSpawnMaterial.Num(); i++)
		{
			m_pSpawnMaterial[i]->SetScalarParameterValue("Amount", fLerp);
		}

		if (m_fCurrAmount >= 1.0f)
		{
			m_fCurrAmount = 0.0f;
			m_nState_Cloaking = 0;
		}
	}
}

//! 클로킹 발동
void UCpt_Cloaking::Active_Cloaking()
{
	if (m_bInit == false) return;
	if (m_bIsCloaking == true) return;
	if (m_nState_Cloaking != 0) return;

	m_bIsCloaking = !m_bIsCloaking;
	m_nState_Cloaking = 1;
	m_fDestAmount = m_fCloakingAmount_Max;
}

//! 클로킹 해제
void UCpt_Cloaking::DeActive_Cloaking()
{
	if (m_bInit == false) return;
	if (m_bIsCloaking == false) return;
	if (m_nState_Cloaking != 0) return;
	
	m_bIsCloaking = !m_bIsCloaking;
	m_nState_Cloaking = 2;
	m_fDestAmount = m_fCloakingAmount_Min;
}

 

 

위 코드 다 작성하시면 컴파일을 하여 오류가 없는지 점검합니다.

 

 

Cpt_Cloaking 컴포넌트를 기본 3인칭 샘플 프로젝트의 캐릭터 클래스에 추가해보겠습니다.

 

 

AThirdPersonCharacter.h

class ACloakingTutorialCharacter : public ACharacter
{
	GENERATED_BODY()

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
		class UCpt_Cloaking* m_pCloaking;
        
 protected:
	virtual void BeginPlay() override;
	virtual void Tick(float DeltaTime) override;

헤더파일에 UCpt_Cloaking 컴포넌트 변수를 위와 같이 작성합니다.

 

 

AThirdPersonCharacter.cpp

#include "CloakingTutorialCharacter.h"
#include "......."
#include "Kismet/GameplayStatics.h"
#include "Cpt_Cloaking.h"

ACloakingTutorialCharacter::ACloakingTutorialCharacter()
{
	...
	...
    //! 클로킹 컴포넌트 생성
	m_pCloaking = CreateDefaultSubobject<UCpt_Cloaking>(TEXT("Cloaking"));
}

void ACloakingTutorialCharacter::BeginPlay()
{
	Super::BeginPlay();
    //! 클로킹 컴포넌트 초기화 변수 호출
	m_pCloaking->Init(GetMesh(), m_p2PassMesh);
}

void ACloakingTutorialCharacter::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
    //! 키보드 R 누르면 클로킹 발동/해제를 합니다.
	if (UGameplayStatics::GetPlayerController(GetWorld(), 0)->WasInputKeyJustPressed(EKeys::R) == true)
	{
		if (m_pCloaking->GetIsCloaking() == true)
		{
			m_pCloaking->DeActive_Cloaking();
		}
		else
		{
			m_pCloaking->Active_Cloaking();
		}
	}
}

 

위 처럼 코드를 작성하고 컴파일하여 기능 테스트하면 끝입니다.

 

 

다음 포스팅은 Cascade이펙트 추가와 포스트 프로세스로 효과를 추가하여 더욱 이펙트를 이쁘게 만드는 것에 대해 포스팅하겠습니다.

 

반응형

WRITTEN BY
CatDarkGame
Technical Artist dhwlgn12@gmail.com

,