콘텐츠로 이동

UE5 PCG Framework — 절차적 콘텐츠 생성

PCG Framework는 UE5 5.2에서 정식 도입된 노드 그래프 기반 절차적 콘텐츠 생성 시스템입니다. 포레스트, 도시 블록, 던전 등을 규칙 기반으로 자동 배치하고 월드 파티션과 통합되어 대규모 오픈 월드에서도 효율적으로 동작합니다.


PCGVolume(액터) → PCGComponent → PCGGraph(에셋)

에디터에서:

Content Browser → 우클릭 → Miscellaneous → PCG Graph
레벨에 PCGVolume 배치 → PCG Graph 에셋 할당

[Surface Sampler] → [Density Filter] → [Static Mesh Spawner]
↓ ↓ ↓
포인트 생성 밀도 기반 필터 메시 배치

주요 노드:

노드역할
Surface Sampler지형 위에 포인트 생성
Landscape Layer Sampler랜드스케이프 레이어별 포인트
Point Filter조건으로 포인트 걸러내기
Transform Points위치/회전/스케일 랜덤화
Static Mesh Spawner포인트에 메시 배치
Copy Points포인트 복제

MyCPPPCGNode.h
#pragma once
#include "PCGSettings.h"
#include "MyCPPPCGNode.generated.h"
UCLASS(BlueprintType, ClassGroup=(Custom))
class UMyCPPPCGSettings : public UPCGSettings
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Settings")
float HeightThreshold = 500.0f;
#if WITH_EDITOR
virtual FName GetDefaultNodeName() const override
{ return FName("Height Filter Node"); }
#endif
virtual TArray<FPCGPinProperties> InputPinProperties() const override;
virtual TArray<FPCGPinProperties> OutputPinProperties() const override;
protected:
virtual FPCGElementPtr CreateElement() const override;
};
// Element 클래스 (실제 실행 로직)
class FMyCPPPCGElement : public FSimplePCGElement
{
protected:
virtual bool ExecuteInternal(
FPCGContext* Context) const override;
};
MyCPPPCGNode.cpp
bool FMyCPPPCGElement::ExecuteInternal(FPCGContext* Context) const
{
const UMyCPPPCGSettings* Settings =
Context->GetInputSettings<UMyCPPPCGSettings>();
TArray<FPCGTaggedData> Inputs =
Context->InputData.GetInputs();
TArray<FPCGTaggedData>& Outputs = Context->OutputData.TaggedData;
for (const FPCGTaggedData& Input : Inputs)
{
const UPCGPointData* PointData =
Cast<UPCGPointData>(Input.Data);
if (!PointData) continue;
UPCGPointData* OutputData = NewObject<UPCGPointData>();
OutputData->InitializeFromData(PointData);
TArray<FPCGPoint>& OutPoints = OutputData->GetMutablePoints();
// 높이 임계값 이하 포인트만 통과
for (const FPCGPoint& Point : PointData->GetPoints())
{
if (Point.Transform.GetLocation().Z < Settings->HeightThreshold)
OutPoints.Add(Point);
}
FPCGTaggedData& Output = Outputs.Add_GetRef(Input);
Output.Data = OutputData;
}
return true;
}

// PCG 그래프를 런타임에 트리거
UCLASS()
class AProceduralForest : public AActor
{
GENERATED_BODY()
UPROPERTY(VisibleAnywhere)
UPCGComponent* PCGComponent;
UPROPERTY(EditAnywhere)
UPCGGraph* ForestGraph;
public:
void RegenerateForest()
{
PCGComponent->SetGraph(ForestGraph);
PCGComponent->GenerateLocal(false); // true = 비동기
}
void CleanForest()
{
PCGComponent->CleanupLocal(false);
}
};

[Landscape Layer Sampler]
Layer: Grass (레이어 이름)
Points Per Square Meter: 2.0
[Transform Points]
Scale: Random (0.8 ~ 1.2)
Rotation: Random Z
[Static Mesh Spawner]
Mesh Entries:
- GrassMesh_01 (weight 0.6)
- GrassMesh_02 (weight 0.4)

// PCGComponent 설정
PCGComponent->bActivated = true;
PCGComponent->GenerationTrigger =
EPCGComponentGenerationTrigger::GenerateOnDemand; // 요청 시만 생성
// 월드 파티션 연동 (대규모 오픈 월드)
PCGComponent->bIsPartitioned = true;

콘솔 변수:

pcg.DebugGraph 1 ; 노드별 실행 시간 표시
pcg.GraphCache.Enable 1 ; 그래프 결과 캐시

7. PCG Attribute — 포인트에 게임플레이 메타데이터 추가

섹션 제목: “7. PCG Attribute — 포인트에 게임플레이 메타데이터 추가”

PCG 포인트에 커스텀 속성(Attribute)을 추가하면 배치 후 스폰된 액터에 게임플레이 데이터를 전달할 수 있습니다.

// 커스텀 노드: 포인트에 "EnemyType" 속성 추가
bool FEnemyTagPCGElement::ExecuteInternal(FPCGContext* Context) const
{
const UEnemyTagPCGSettings* Settings =
Context->GetInputSettings<UEnemyTagPCGSettings>();
for (const FPCGTaggedData& Input : Context->InputData.GetInputs())
{
const UPCGPointData* InPoints = Cast<UPCGPointData>(Input.Data);
if (!InPoints) continue;
UPCGPointData* OutPoints = NewObject<UPCGPointData>();
OutPoints->InitializeFromData(InPoints);
OutPoints->GetMutablePoints() = InPoints->GetPoints();
// 포인트 밀도(Density)에 따라 적 타입 결정
FPCGMetadataAttribute<int32>* EnemyTypeAttr =
OutPoints->Metadata->CreateAttribute<int32>(
FName("EnemyType"), 0, true, true);
const TArray<FPCGPoint>& Points = OutPoints->GetPoints();
for (int32 i = 0; i < Points.Num(); ++i)
{
int32 EnemyType = (Points[i].Density > 0.7f) ? 2 // 엘리트
: (Points[i].Density > 0.4f) ? 1 // 일반
: 0; // 잡몹
EnemyTypeAttr->SetValue(
OutPoints->Metadata->GetItemKeyFromIndex(i), EnemyType);
}
FPCGTaggedData& Out = Context->OutputData.TaggedData.Add_GetRef(Input);
Out.Data = OutPoints;
}
return true;
}
// Static Mesh Spawner 대신 커스텀 Actor Spawner에서 속성 읽기
void AEnemySpawnerActor::OnSpawnedByPCG(
const FPCGPoint& Point, const UPCGMetadata* Metadata)
{
// 스폰 후 콜백에서 EnemyType 속성 읽기
int64 ItemKey = Point.MetadataEntry;
int32 EnemyType = 0;
if (const auto* Attr = Metadata->GetConstTypedAttribute<int32>(FName("EnemyType")))
{
Attr->GetValue(ItemKey, EnemyType);
}
// 적 타입별 AI 설정 적용
SetEnemyTier(EnemyType);
}

PCG Framework는 반복 배치 작업을 노드 그래프로 자동화하고 월드 파티션과 통합해 대규모 오픈 월드 콘텐츠 생성에 적합합니다. 기본 노드 조합만으로도 포레스트, 암석 분포 등 대부분의 자연 배치가 가능하며, C++ 커스텀 노드로 게임 특화 규칙을 추가할 수 있습니다.