본문 바로가기
C++ 개발 일지

231211 [C++][UE] 디자인 (설계)패턴 - 1. 컴포넌트 기반 패턴

by the_cat_Soy 2023. 12. 11.

design pattern c++ 이란?

- 프로그래밍과 소프트웨어 개발에서 자주 발생하는 문제를 해결하기 위한 일련의 재사용 가능한 해결책

- 일종의 설계 템플릿으로, 특정 상황에서 일반적으로 발생하는 문제들을 해결하기 위한 구조화된 솔루션을 제공

개발자가 사람이기에 발생하는 문제들

  • 컴포넌트 기반 패턴 (Component-based Pattern):  [Unreal 한정]
  • 팩토리 메서드 패턴 (Factory Method Pattern):
  • 옵저버 패턴 (Observer Pattern):
  • 프록시 패턴 (Proxy Pattern):
  • 커맨드 패턴 (Command Pattern):
  • 프로토타입 패턴 (Prototype Pattern):

 

컴포넌트 기반 패턴 (Component-based Pattern):  [Unreal 한정]

  • 각 컴포넌트는 특정한 기능을 수행하는 독립적인 단위로 분리
  • 컴포넌트들은 게임 오브젝트에 추가되어 조합
  • 다양한 컴포넌트들을 조합하여 하나의 게임 오브젝트를 만들어내며, 이를 통해 유연하고 재사용 가능한 코드를 작성 가능

ex) Pawn에 Charator movement Component 추가하면 Charactor처럼 사용할 수 있다!

구현을 쪼개어 만들어서 사용한다! Stun = Slow는 기능적으로 같다.

 

 

예제1) 인벤토리 컴포넌트 만들기

변수를 private로 설정하여 직접적으로 영향 받지 않도록 함수를 수정하도록 한다.

 

<Header>

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

#pragma once

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

UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class THIRDPERSONTEMPLATE_API UInventoryComp : public UActorComponent
{
	GENERATED_BODY()

public:	
	// Sets default values for this component's properties
	UInventoryComp();

	UFUNCTION(BlueprintCallabel)
	void AddItem(const FName& ItemName, int32 Qauntity);

	UFUNCTION(BlueprintCallabel)
	void RemoveItem(const FName& ItemName, int32 Qauntity);

	UFUNCTION(BlueprintPure)
	int32 GetItemQuantity(const FName& ItemName) const;

protected:
	// Called when the game starts
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;

private:
	TMap<FName, int32> InventoryMap;
};

 

<cpp>

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


#include "InventoryComp.h"

// Sets default values for this component's properties
UInventoryComp::UInventoryComp()
{
	// Set this component to be initialized when the game starts, and to be ticked every frame.  You can turn these features
	// off to improve performance if you don't need them.
	PrimaryComponentTick.bCanEverTick = true;

	// ...
}

void UInventoryComp::AddItem(const FName& ItemName, int32 Quantity)
{
	int32* ExistingQauntity = InventoryMap.Find(ItemName);
	
	if (ExistingQauntity)
	{
		//이미 해당 아이템이 인벤토리에 있는 경우 수량 증가
		ExistingQauntity += Quantity;

	}
	else
	{ 
		//새로운 아이템을 인벤토리에 추가
		InventoryMap.Add(ItemName, Quantity);
	
	}
}

void UInventoryComp::RemoveItem(const FName& ItemName, int32 Quantity)
{
	int32* ExistingQauntity = InventoryMap.Find(ItemName);

	if (ExistingQauntity)
	{
		//이미 해당 아이템이 인벤토리에 있는 경우 수량 감소
		ExistingQauntity -= Quantity;
		if (*ExistingQauntity <= 0)
		{
			InventoryMap.Remove(ItemName);
		}
	}
	
}

int32 UInventoryComp::GetItemQuantity(const FName& ItemName) const
{
	const int32* ExistingQauntity = InventoryMap.Find(ItemName);
	
	return ExistingQauntity ? *ExistingQauntity : 0;
	/*
	삼항 연산자
	
	조건식 ? 값1, 값2 
	true -> 값1
	false -> 값2
	*/
}


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

	// ...
	
}


// Called every frame
void UInventoryComp::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	// ...
}

 

 

예제2) LineTrace Detecting

<Header>

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

#pragma once

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

UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class THIRDPERSONTEMPLATE_API ULineTraceDetector : public UActorComponent
{
	GENERATED_BODY()

public:	
	// Sets default values for this component's properties
	ULineTraceDetector();

protected:
	// Called when the game starts
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;

	UFUNCTION(BlueprintCallable)
	void PerformLineTrace();

private:
	UPROPERTY(EditAnyWhere)
	float TraceDistance = 1000.0f; // 라인 트레이스 거리

	UPROPERTY(EditAnyWhere)
	TSubclassOf<AActor> ActorClassToTrace; //찾고자 하는 액터 클래스

	UFUNCTION()
	void OnLineTraceHit(FHitResult HitResult);

		
};

 

<cpp>

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

#include "LineTraceDetector.h"
#include "DrawDebugHelpers.h"

// Sets default values for this component's properties
ULineTraceDetector::ULineTraceDetector()
{
	// Set this component to be initialized when the game starts, and to be ticked every frame.  You can turn these features
	// off to improve performance if you don't need them.
	PrimaryComponentTick.bCanEverTick = true;

	// ...
}

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

	// ...
	
}

// Called every frame
void ULineTraceDetector::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	// ...
}

void ULineTraceDetector::PerformLineTrace()
{
	FVector StartLocation = GetOwner()->GetActorLocation();
	FVector EndLocation = StartLocation + GetOwner()->GetActorForwardVector() * TraceDistance;

	FHitResult HitResult;
	FCollisionQueryParams CollisionParams;
	CollisionParams.AddIgnoredActor(GetOwner());

	bool bHit = GetWorld()->LineTraceSingleByChannel(
		HitResult,
		StartLocation,
		EndLocation,
		ECollisionChannel::ECC_Visibility,
		CollisionParams);

	DrawDebugLine(GetWorld(), StartLocation, EndLocation, FColor::Magenta, false, 3.0f); //true일 경우 메세지가 메모리에 누적..남아있음

	if (bHit)
	{
		OnLineTraceHit(HitResult);
		
	}
}

void ULineTraceDetector::OnLineTraceHit(FHitResult HitResult)
{
	if (HitResult.GetActor() && HitResult.GetActor()->IsA(ActorClassToTrace))
	{
		//원하는 클래스의 액터를 찾은 경우
		AActor* FoundActor = HitResult.GetActor();
		//여기서 원하는 작업을 수행하거나 해당 액터를 사용할 수 있음
		if (FoundActor)
		{
			GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Green
				, (TEXT("%s"),*FoundActor->GetName()));

		}
	}
}

 

블루프린트로 인풋 액션 / LineTrace 셋팅하기