UActorComponentUSceneComponent: 블루프린트에서 Components 탭에서 직접 추가하는 방법
장점
단점
: 클래스 생성자에서 CreateDefaultSubobject() 함수를 통하여 추가하는 방법
장점
단점
: 동적으로 특정 조건 시에 NewObject() 함수를 통해 컴포넌트를 생성한 후에, RegisterComponentWithWorld() 함수를 통해 등록하는 방법
: Character에는 PawnExtComponent인 하나의 컴포넌트만 가지고 있음
ALyraCharacter::ALyraCharacter()
{
PawnExtComponent = CreateDefaultSubobject<ULyraPawnExtensionComponent>("PawnExt");
}
: Component 조립 설계도로, Component랑 Ability를 가지고 있는 데이터 에셋임
UCLASS()
class ULyraPawnData : public UPrimaryDataAsset
{
UPROPERTY(EditDefaultsOnly)
TArray<TSubclassOf<UActorComponent>> Components;
UPROPERTY(EditDefaultsOnly)
TArray<TSubclassOf<ULyraAbilitySet>> AbilitySets;
}
: 직업 변경 시에 Component 및 Ability를 동적으로 붙임
void ULyraPawnExtensionComponent::SetupPawn(const ULyraPawnData* PawnData)
{
for (TSubclassOf<UActorComponent> CompClass : PawnData->Components)
{
UActorComponent* NewComp = NewObject<UActorComponent>(GetOwner(), CompClass);
NewComp->RegisterComponentWithWorld(GetWorld());
}
for (TSubclassOf<ULyraAbilitySet> AbilitySet : PawnData->AbilitySets)
{
GrantAbilitySet(AbilitySet);
}
}
ULyraPawnData* PaladinData = LoadObject<ULyraPawnData>("Paladin");
void TransformToPaladin(ALyraCharacter* Character)
{
auto* PawnExt = Character->GetPawnExtension();
PawnExt->RemoveAllComponents();
PawnExt->SetupPawn(PaladinData);
}
| 문제 | 해결 방식 |
|---|---|
| 캐릭터마다 다른 기능 | 기능을 런타임에 붙임 |
| 구조가 복잡하고 고정됨 | 데이터 기반 유연 구성 |
| 코드 확장 어려움 | 에셋만 바꿔도 구조 확장 |
| 디자이너 작업 어려움 | 에디터에서 캐릭터 구성 가능 |
| 협업 시 충돌 | 컴포넌트 단위 분리 -> 충돌 최소화 |
: 이벤트가 발생했을 때, 미리 등록된 함수들을 호출하는 시스템
단일 대상
가장 빠름
호출 비용이 거의 없음
블루프린트랑 연동 안 됨
사용 시기
// 델리게이트 선언
DECLARE_DELEGATE(FOnActionDone);
FOnActionDone OnDone;
// 델리게이트 바인딩
OnDone.BindUObject(this, &UMyClass::Handler);
// 델리게이트 호출
if (OnDone.IsBound()){
OnDone.Execute();
}
여러 구독자
빠르고 유연함
블루프린트랑 연동 안 됨
사용 시기
// 델리게이트 선언
DECLARE_MULTICAST_DELEGATE_OneParam(FOnDamageTaken, float);
FOnDamageTaken OnDamage;
// 델리게이트 바인딩
OnDamage.AddUObject(this, &UMyClass::ReactToDamage);
OnDamage.AddLambda([](float Damage) {
UE_LOG(LogTemp, Warning, TEXT("Damage: %.1f"), Damage);
});
// 델리게이트 호출
OnDamage.BroadCast(Amount);
블루프린트에서 바인딩 가능
호출은 느리지만, 확장성 최강
런타임 성능은 다른 델리게이트보다 떨어짐
사용 시기
// 델리게이트 선언
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnDeath, AActor*, Killer);
UPROPERTY(BlueprintAssignable)
FOnDeath OnDeath;
// 델리게이트 바인딩
OnDeath.AddDynamic(this, &UMyClass::ReactToDeath);
// 델리게이트 호출
OnDeath.BroadCast(Other);
특정 클래스에서만 Broadcast 가능하게 만들고 싶을 때
블루프린트랑 연동 안 됨
문법이 다소 특이하고 덜 쓰이지만, 고급 구조에서 유용
사용 시기
// 델리게이트 선언
DECLARE_EVENT(UGameStateSubsystem, FOnMatchStarted)
class UMyGameState : public UGameStateSubsystem
{
FOnMatchStarted OnMatchStarted;
public:
FOnMatchStarted& GetMatchStartedEvent() { return OnMatchStarted; }
}
// 델리게이트 바인딩
UMyGameState->GetMatchStartedEvent.Add(this, &UMyClass::OnMatchStart);
// 델리게이트 호출
FOnMatchStarted.BroadCast();
| 원칙 | 의미 | 구현 방법 |
|---|---|---|
| 단일 책임 | Component = 하나의 역할 | 하나의 Component에 하나의 기능만 |
| 이벤트 기반 | 직접 호출 금지 | 델리게이트 Broadcast |
| 데이터 중심 | 설정은 코드가 아닌 데이터 에셋 | DataAsset 활용 |
| 확장 가능 | 수정 없이 기능 추가 | 델리게이트에 구독만 추가하면 가능 |