1. 싱글톤 패턴 (Singleton Pattern)
정의: 싱글톤 패턴은 특정 클래스의 인스턴스가 하나만 존재하도록 보장하고, 이를 전역적으로 접근 가능하게 만드는 디자인 패턴입니다. 이 패턴은 자원의 낭비를 방지하고 전역적으로 상태를 유지해야 하는 객체를 관리할 때 유용합니다.
언리얼에서의 활용: 언리얼 엔진에서는 주로 GameInstance와 같이 전체 게임에서 공유되어야 하는 데이터를 관리할 때 싱글톤 패턴을 사용합니다. GameInstance는 게임의 라이프 사이클 동안 지속되는 객체로, 각종 설정, 네트워크 상태, 전역 변수 등을 관리하는 데 사용됩니다.
샘플 코드:
// GameInstance를 사용하여 싱글톤과 같은 기능 구현
UGameInstance* MyGameInstance = GetWorld()->GetGameInstance();
UMyGameInstanceClass* CustomInstance = Cast<UMyGameInstanceClass>(MyGameInstance);
if (CustomInstance)
{
CustomInstance->DoSomethingGlobal();
}
이 샘플은 GetWorld()->GetGameInstance()를 통해 싱글톤 인스턴스를 가져와 사용하는 방식으로, 게임 전반에 걸쳐 동일한 인스턴스를 접근하는 것을 보장합니다.
2. 옵저버 패턴 (Observer Pattern)
정의: 옵저버 패턴은 한 객체의 상태 변화가 있을 때, 그 변화를 관찰하는 다른 객체들에게 알림을 보내는 패턴입니다. 주로 이벤트 시스템을 구현할 때 많이 사용되며, 객체 간의 느슨한 결합을 유지하면서도 상호작용을 가능하게 합니다.
언리얼에서의 활용: 언리얼에서는 **델리게이트(Delegate)**를 통해 옵저버 패턴을 구현합니다. 델리게이트는 이벤트가 발생할 때 여러 리스너에게 알림을 주는 방식으로 작동하며, 주로 캐릭터의 체력 변화나 게임 이벤트 발생 시 이를 UI 등에 알리기 위해 사용됩니다.
샘플 코드:
// 델리게이트 정의
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnHealthChanged);
// 델리게이트 선언
FOnHealthChanged OnHealthChanged;
// 델리게이트 호출 예제
void AMyCharacter::TakeDamage(float DamageAmount)
{
Health -= DamageAmount;
OnHealthChanged.Broadcast(); // 모든 구독자에게 알림
}
// 델리게이트 바인딩 예제
OnHealthChanged.AddDynamic(this, &AMyCharacter::UpdateHealthUI);
이 예제에서 OnHealthChanged 델리게이트를 통해 체력 변화 시 관련 UI를 자동으로 업데이트하도록 알림을 보내는 구조를 볼 수 있습니다.
3. 컴포넌트 패턴 (Component Pattern)
정의: 컴포넌트 패턴은 객체의 기능을 여러 컴포넌트 단위로 분리하고, 이를 동적으로 조합하여 객체의 기능을 확장하는 방식입니다. 이 패턴은 객체를 작고 독립적인 단위로 나누어 유지 보수성과 재사용성을 높입니다.
언리얼에서의 활용: 언리얼 엔진에서는 ActorComponent와 SceneComponent를 활용하여 컴포넌트 패턴을 구현합니다. 예를 들어, 캐릭터에 이동, 공격, 방어 등 다양한 기능을 추가할 때 이러한 컴포넌트를 사용합니다. 이를 통해 코드의 모듈화를 높이고, 필요에 따라 컴포넌트를 추가하거나 제거함으로써 객체의 기능을 유연하게 구성할 수 있습니다.
샘플 코드:
// 컴포넌트를 사용하여 기능을 추가하는 예제
AMyCharacter::AMyCharacter()
{
// 캐릭터에 이동 컴포넌트 추가
MovementComponent = CreateDefaultSubobject<UCharacterMovementComponent>(TEXT("MovementComponent"));
}
// 컴포넌트를 통해 이동 처리
void AMyCharacter::MoveForward(float Value)
{
if (Value != 0.0f)
{
AddMovementInput(GetActorForwardVector(), Value);
}
}
이 코드는 캐릭터에 이동 기능을 추가하는 MovementComponent를 사용하여 컴포넌트 패턴을 적용한 예입니다. 각 기능을 독립적인 컴포넌트로 구현함으로써 기능의 추가나 수정이 용이해집니다.
4. MVC 패턴 (Model-View-Controller Pattern)
정의: MVC 패턴은 애플리케이션을 Model, View, Controller 세 부분으로 분리하여 개발하는 디자인 패턴입니다. Model은 데이터와 비즈니스 로직을 관리하고, View는 사용자 인터페이스를, Controller는 사용자 입력을 처리하여 Model과 View를 연결하는 역할을 합니다.
언리얼에서의 활용: 언리얼에서는 UI 시스템인 UMG와 게임 로직을 분리하여 MVC 패턴을 활용합니다. 예를 들어, 캐릭터의 체력 상태를 관리하는 Model과 이를 화면에 표시하는 View, 그리고 사용자의 입력을 처리하는 Controller를 독립적으로 관리함으로써 코드의 재사용성과 유지보수성을 높입니다.
샘플 코드:
// Model: 데이터와 로직 관리
class UPlayerStats : public UObject
{
public:
int32 Health;
int32 Stamina;
void ModifyHealth(int32 Amount)
{
Health += Amount;
}
};
// View: UMG 위젯을 통한 UI 구현
class UPlayerStatsWidget : public UUserWidget
{
public:
void UpdateHealthDisplay(int32 Health)
{
// UI 요소 업데이트
HealthText->SetText(FText::AsNumber(Health));
}
private:
UPROPERTY(meta = (BindWidget))
UTextBlock* HealthText;
};
// Controller: 입력 처리 및 모델-뷰 연결
class APlayerController : public AController
{
public:
void SetPlayerStats(UPlayerStats* Stats, UPlayerStatsWidget* StatsWidget)
{
PlayerStats = Stats;
PlayerStatsWidget = StatsWidget;
}
void TakeDamage(int32 DamageAmount)
{
PlayerStats->ModifyHealth(-DamageAmount);
PlayerStatsWidget->UpdateHealthDisplay(PlayerStats->Health);
}
private:
UPlayerStats* PlayerStats;
UPlayerStatsWidget* PlayerStatsWidget;
};
이 예제에서 Model, View, Controller가 각각 독립적으로 구현되어 데이터 관리, UI 업데이트, 사용자 입력 처리를 명확하게 분리하고 있습니다.
5. 팩토리 패턴 (Factory Pattern)
정의: 팩토리 패턴은 객체 생성 과정을 캡슐화하여 다양한 종류의 객체를 생성할 수 있게 만드는 디자인 패턴입니다. 이 패턴을 사용하면 객체 생성 로직을 클래스 내부에 감추고, 상위 클래스의 인터페이스를 통해 객체를 생성할 수 있습니다.
언리얼에서의 활용: 언리얼 엔진에서는 UObject나 Actor 클래스를 동적으로 생성할 때 팩토리 패턴을 사용합니다. 예를 들어, NewObject나 SpawnActor 함수를 통해 객체를 생성함으로써 코드의 유연성을 높입니다.
샘플 코드:
// UObject를 생성하는 팩토리 함수
UWeapon* NewWeapon = NewObject<UWeapon>(this);
if (NewWeapon)
{
NewWeapon->Initialize();
}
// Actor를 스폰하는 팩토리 함수
FActorSpawnParameters SpawnParams;
AMyActor* SpawnedActor = GetWorld()->SpawnActor<AMyActor>(AMyActor::StaticClass(), SpawnLocation, SpawnRotation, SpawnParams);
if (SpawnedActor)
{
SpawnedActor->DoSomething();
}
이 코드에서는 팩토리 패턴을 통해 다양한 객체를 동적으로 생성하여 관리할 수 있는 유연성을 보여줍니다.
6. 상태 패턴 (State Pattern)
정의: 상태 패턴은 객체의 상태에 따라 그 객체의 행동을 변경하는 디자인 패턴입니다. 객체가 다양한 상태를 가질 수 있을 때, 각 상태에 맞는 행동을 상태 객체에 위임함으로써 코드의 복잡성을 줄일 수 있습니다.
언리얼에서의 활용: 언리얼 엔진에서는 캐릭터의 상태를 관리할 때 이 패턴을 사용합니다. 예를 들어, 캐릭터가 대기 상태, 이동 상태, 공격 상태 등 다양한 상태를 가질 수 있으며, 이러한 상태를 별도의 클래스로 나누어 관리할 수 있습니다.
샘플 코드:
class UCharacterState
{
public:
virtual void HandleInput(class AMyCharacter* Character) = 0;
};
class UIdleState : public UCharacterState
{
public:
virtual void HandleInput(AMyCharacter* Character) override
{
UE_LOG(LogTemp, Log, TEXT("캐릭터가 대기 상태입니다."));
}
};
class UMovingState : public UCharacterState
{
public:
virtual void HandleInput(AMyCharacter* Character) override
{
UE_LOG(LogTemp, Log, TEXT("캐릭터가 이동 중입니다."));
}
};
이 패턴을 사용하면 캐릭터의 행동을 상태에 따라 쉽게 변경할 수 있고, 각 상태별 행동을 독립적으로 관리할 수 있어 코드의 가독성과 유지 보수성이 향상됩니다.
7. 전략 패턴 (Strategy Pattern)
정의: 전략 패턴은 알고리즘을 캡슐화하여 동적으로 교체할 수 있게 만드는 디자인 패턴입니다. 다양한 전략을 클래스 형태로 정의하고, 런타임에 이들 중 하나를 선택하여 사용할 수 있도록 합니다.
언리얼에서의 활용: 언리얼에서는 AI의 행동 전략을 동적으로 변경하거나 무기 시스템의 공격 방식을 변경하는 데 전략 패턴을 사용할 수 있습니다. 이를 통해 행동을 모듈화하고 쉽게 교체할 수 있습니다.
샘플 코드:
// 전략 인터페이스 정의
class IAttackStrategy
{
public:
virtual void Attack() = 0;
};
// 근접 공격 전략 클래스
class MeleeAttack : public IAttackStrategy
{
public:
virtual void Attack() override
{
UE_LOG(LogTemp, Log, TEXT("근접 공격을 수행합니다."));
}
};
// 원거리 공격 전략 클래스
class RangedAttack : public IAttackStrategy
{
public:
virtual void Attack() override
{
UE_LOG(LogTemp, Log, TEXT("원거리 공격을 수행합니다."));
}
};
// 캐릭터가 전략을 설정하는 예제
class AMyCharacter : public ACharacter
{
public:
void SetAttackStrategy(IAttackStrategy* NewStrategy)
{
CurrentStrategy = NewStrategy;
}
void PerformAttack()
{
if (CurrentStrategy)
{
CurrentStrategy->Attack();
}
}
private:
IAttackStrategy* CurrentStrategy;
};
이 코드는 전략을 쉽게 변경할 수 있는 유연성을 제공하며, 다양한 공격 방식을 모듈화하여 관리할 수 있게 해줍니다.
8. 컴포지트 패턴 (Composite Pattern)
정의: 컴포지트 패턴은 객체를 트리 구조로 만들어 개별 객체와 객체 그룹을 동일하게 취급할 수 있도록 하는 패턴입니다. 이는 계층적인 객체 구조를 표현하는 데 유용하며, 개별 객체와 복합 객체를 동일하게 다룰 수 있습니다.
언리얼에서의 활용: 언리얼에서는 UI 요소를 계층적으로 구성할 때 컴포지트 패턴을 사용합니다. 예를 들어, 여러 개의 UI 요소를 하나의 컨테이너에 넣고, 이를 트리 형태로 관리하여 쉽게 조작할 수 있습니다.
샘플 코드:
// 컴포지트 위젯 생성
UVerticalBox* VerticalBox = WidgetTree->ConstructWidget<UVerticalBox>(UVerticalBox::StaticClass());
UTextBlock* TextBlock = WidgetTree->ConstructWidget<UTextBlock>(UTextBlock::StaticClass());
VerticalBox->AddChild(TextBlock);
이 예제에서는 여러 개의 UI 요소를 계층적으로 배치하여 쉽게 관리할 수 있는 구조를 보여줍니다. 컴포지트 패턴을 통해 UI 요소를 그룹화하고, 그룹 전체에 동일한 작업을 적용할 수 있습니다.
9. 커맨드 패턴 (Command Pattern)
정의: 커맨드 패턴은 요청을 객체로 캡슐화하여 호출자와 수행자 간의 결합을 느슨하게 만드는 패턴입니다. 이 패턴을 사용하면 명령을 추상화하여 실행, 취소, 기록 등의 기능을 구현할 수 있습니다.
언리얼에서의 활용: 언리얼에서는 플레이어의 입력을 처리하는 데 커맨드 패턴을 사용할 수 있습니다. 입력과 행동을 분리하여 명령 객체로 처리함으로써, 다양한 입력 방식에 쉽게 대응할 수 있습니다.
샘플 코드:
// 커맨드 인터페이스 정의
class ICommand
{
public:
virtual void Execute() = 0;
};
// 점프 명령 클래스
class JumpCommand : public ICommand
{
public:
virtual void Execute() override
{
UE_LOG(LogTemp, Log, TEXT("캐릭터가 점프했습니다."));
}
};
// 입력에 커맨드를 바인딩하는 예제
void AMyCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
ICommand* JumpCmd = new JumpCommand();
PlayerInputComponent->BindAction("Jump", IE_Pressed, this, [JumpCmd]() { JumpCmd->Execute(); });
}
이 코드에서는 입력과 행동을 명령 객체로 분리하여 관리하는 방식을 보여줍니다. 이를 통해 다양한 입력 처리 로직을 쉽게 추가하거나 수정할 수 있습니다.
10. 심플 팩토리 패턴 (Simple Factory Pattern)
정의: 심플 팩토리 패턴은 객체 생성을 전담하는 클래스를 통해 객체를 생성하는 간단한 형태의 팩토리 패턴입니다. 다양한 객체 생성의 책임을 한 클래스에 집중시켜 코드의 복잡성을 줄이는 장점이 있습니다.
언리얼에서의 활용: 언리얼에서는 특정 타입의 객체를 조건에 따라 생성할 때 심플 팩토리 패턴을 활용할 수 있습니다.
샘플 코드:
class WeaponFactory
{
public:
static UWeapon* CreateWeapon(EWeaponType Type)
{
switch (Type)
{
case EWeaponType::Sword:
return NewObject<USword>();
case EWeaponType::Bow:
return NewObject<UBow>();
default:
return nullptr;
}
}
};
이 예제에서는 무기 타입에 따라 다른 객체를 생성하여 반환하는 역할을 WeaponFactory가 수행합니다.
11. 팩토리 메서드 패턴 (Factory Method Pattern)
정의: 팩토리 메서드 패턴은 객체 생성의 책임을 서브클래스에 위임하여 다양한 객체의 생성을 처리하는 디자인 패턴입니다. 이 패턴은 팩토리 메서드를 오버라이드하여 객체 생성 방식을 사용자 정의할 수 있게 합니다.
언리얼에서의 활용: 언리얼에서 특정 행동을 정의할 때 하위 클래스를 통해 객체를 동적으로 생성해야 할 경우 팩토리 메서드 패턴을 사용할 수 있습니다.
샘플 코드:
class AWeaponCreator
{
public:
virtual UWeapon* CreateWeapon() = 0;
};
class ASwordCreator : public AWeaponCreator
{
public:
virtual UWeapon* CreateWeapon() override
{
return NewObject<USword>();
}
};
이 패턴을 통해 상위 클래스인 AWeaponCreator는 생성의 책임을 하위 클래스인 ASwordCreator에게 위임합니다.
12. 추상 팩토리 패턴 (Abstract Factory Pattern)
정의: 추상 팩토리 패턴은 관련된 객체들의 군을 생성하는 인터페이스를 제공하는 패턴입니다. 상호 호환성이 있는 객체군을 생성하는 데 적합하며, 일관된 객체 생성을 보장할 수 있습니다.
언리얼에서의 활용: 언리얼에서 상호 호환성이 있는 여러 종류의 객체를 생성해야 할 경우, 예를 들어 무기와 방어구를 동시에 만들어야 하는 상황에서 사용될 수 있습니다.
샘플 코드:
class IWeaponFactory
{
public:
virtual UWeapon* CreateMeleeWeapon() = 0;
virtual UWeapon* CreateRangedWeapon() = 0;
};
class MedievalWeaponFactory : public IWeaponFactory
{
public:
virtual UWeapon* CreateMeleeWeapon() override
{
return NewObject<USword>();
}
virtual UWeapon* CreateRangedWeapon() override
{
return NewObject<UBow>();
}
};
IWeaponFactory는 다양한 무기군을 생성할 수 있는 추상 팩토리 역할을 수행하며, 이를 상속하는 MedievalWeaponFactory는 무기 종류를 정의합니다.
13. 원형 패턴 (Prototype Pattern)
정의: 원형 패턴은 기존 객체를 복제하여 새로운 객체를 생성하는 패턴입니다. 언리얼에서 여러 유사한 객체를 효율적으로 생성할 때 사용할 수 있습니다.
언리얼에서의 활용: 언리얼에서 기존에 있는 객체를 복제하여 새로운 객체를 생성할 때 사용됩니다. 이는 특히 오브젝트의 초기화 과정이 복잡하거나 많은 속성을 포함할 때 유용합니다.
샘플 코드:
UWeapon* OriginalWeapon = GetSomeWeapon();
UWeapon* ClonedWeapon = DuplicateObject<UWeapon>(OriginalWeapon, nullptr);
이 코드에서는 기존 무기 객체인 OriginalWeapon을 복제하여 새로운 무기 객체 ClonedWeapon을 생성합니다.
14. 경량 패턴 (Flyweight Pattern)
정의: 경량 패턴은 공유를 통해 메모리 사용을 줄이는 패턴입니다. 많은 수의 유사 객체가 필요할 때, 객체의 중복된 데이터를 공유하여 메모리 사용을 절감할 수 있습니다.
언리얼에서의 활용: 언리얼 엔진에서는 많은 수의 비슷한 오브젝트(예: 나무, 돌 등)를 렌더링해야 할 때 경량 패턴을 사용해 메모리를 절약할 수 있습니다.
샘플 코드:
class TreeFlyweight
{
public:
UStaticMesh* TreeMesh; // 공유되는 데이터
};
class Tree
{
public:
FVector Position;
TreeFlyweight* SharedTreeData;
};
여기서 TreeMesh와 같은 데이터는 많은 Tree 객체들 사이에서 공유되어 메모리 사용을 절감합니다.
15. 오브젝트 풀 패턴 (Object Pool Pattern)
정의: 오브젝트 풀 패턴은 객체 생성 비용을 줄이기 위해 미리 생성해 둔 객체들을 재사용하는 방식의 패턴입니다. 주로 반복적으로 생성 및 소멸되는 객체에서 사용됩니다.
언리얼에서의 활용: 언리얼에서는 총알, 몬스터와 같이 자주 생성되고 삭제되는 객체를 관리하는 데 오브젝트 풀 패턴을 사용할 수 있습니다.
샘플 코드:
class BulletPool
{
public:
UBullet* GetBullet()
{
if (PooledBullets.Num() > 0)
{
return PooledBullets.Pop();
}
return NewObject<UBullet>();
}
void ReturnBullet(UBullet* Bullet)
{
PooledBullets.Push(Bullet);
}
private:
TArray<UBullet*> PooledBullets;
};
BulletPool은 총알 객체를 미리 생성하고, 사용 후 반환하여 재사용함으로써 생성 비용을 절감합니다.
16. 빌더 패턴 (Builder Pattern)
정의: 빌더 패턴은 복잡한 객체를 단계별로 생성할 수 있도록 하는 패턴입니다. 이 패턴은 객체 생성 과정을 세분화하여 각 단계별로 설정할 수 있게 합니다.
언리얼에서의 활용: 언리얼에서는 복잡한 게임 오브젝트나 여러 단계의 초기화가 필요한 UI를 만들 때 빌더 패턴을 사용할 수 있습니다.
샘플 코드:
class HouseBuilder
{
public:
void BuildWalls() { /* 벽 생성 코드 */ }
void BuildRoof() { /* 지붕 생성 코드 */ }
AHouse* GetHouse() { return House; }
private:
AHouse* House;
};
class Director
{
public:
void Construct(HouseBuilder* Builder)
{
Builder->BuildWalls();
Builder->BuildRoof();
}
};
이 예제에서는 HouseBuilder가 집을 단계별로 생성하며, Director는 집을 구성하는 과정을 제어합니다.
17. 어댑터 패턴 (Adapter Pattern)
정의: 어댑터 패턴은 기존 클래스의 인터페이스를 변환하여 클라이언트에서 사용할 수 있도록 하는 패턴입니다. 언리얼에서 기존 API나 클래스를 다른 형태로 사용해야 할 때 유용하게 활용할 수 있습니다.
언리얼에서의 활용: 예를 들어, 언리얼에서 기존의 움직임 시스템을 새로운 방식으로 사용하고자 할 때 어댑터 패턴을 활용할 수 있습니다.
샘플 코드:
class OldMovementSystem
{
public:
void MoveCharacter(float Speed)
{
// 오래된 이동 방식
}
};
class NewMovementSystemAdapter
{
public:
NewMovementSystemAdapter(OldMovementSystem* OldSystem) : OldSystem(OldSystem) {}
void Move(float Velocity)
{
OldSystem->MoveCharacter(Velocity);
}
private:
OldMovementSystem* OldSystem;
};
NewMovementSystemAdapter는 OldMovementSystem을 감싸서 새 인터페이스와 호환되도록 만들어줍니다.
'Unreal > GPT with Unreal' 카테고리의 다른 글
4.모던 C++ (0) | 2024.12.02 |
---|---|
3.언리얼 멀티 쓰레드 (1) | 2024.11.30 |
2.블루프린트 (1) | 2024.11.29 |
1.UHT - UClass - CDO (2) | 2024.11.28 |
0.현대 게임 개발의 기본적인 모토 (2) | 2024.11.28 |