PART 7 - 강의 1/4

엘리먼트 반응 시스템

원신 스타일 속성 조합 시스템

01

엘리먼트 시스템 설계

Tag 기반 속성 관리

엘리먼트 반응 시스템은 두 개 이상의 속성이 조합되어 추가 효과를 발생시키는 시스템입니다. GAS의 GameplayTag를 활용하여 구현합니다.

엘리먼트 태그 정의 // DefaultGameplayTags.ini [/Script/GameplayTags.GameplayTagsSettings] +GameplayTagList=(Tag="Element.Fire",DevComment="불 속성") +GameplayTagList=(Tag="Element.Water",DevComment="물 속성") +GameplayTagList=(Tag="Element.Electric",DevComment="전기 속성") +GameplayTagList=(Tag="Element.Ice",DevComment="얼음 속성") +GameplayTagList=(Tag="Element.Wind",DevComment="바람 속성") +GameplayTagList=(Tag="Element.Earth",DevComment="땅 속성") // 반응 태그 +GameplayTagList=(Tag="Reaction.Vaporize",DevComment="증발 (불+물)") +GameplayTagList=(Tag="Reaction.Overload",DevComment="과부하 (불+전기)") +GameplayTagList=(Tag="Reaction.Melt",DevComment="융해 (불+얼음)") +GameplayTagList=(Tag="Reaction.Freeze",DevComment="빙결 (물+얼음)") +GameplayTagList=(Tag="Reaction.Electro-Charged",DevComment="감전 (물+전기)") +GameplayTagList=(Tag="Reaction.Superconduct",DevComment="초전도 (얼음+전기)")
02

엘리먼트 상태 관리

부착된 속성 추적

엘리먼트 컴포넌트 UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent)) class UElementSystemComponent : public UActorComponent { GENERATED_BODY() public: // 현재 부착된 엘리먼트 UPROPERTY(BlueprintReadOnly, ReplicatedUsing = OnRep_AttachedElements) FGameplayTagContainer AttachedElements; // 엘리먼트 적용 UFUNCTION(BlueprintCallable, Server, Reliable) void ApplyElement(FGameplayTag ElementTag, float Duration, float Amount); // 반응 체크 UFUNCTION(BlueprintCallable) bool CheckReaction(FGameplayTag NewElement, FGameplayTag& OutReaction); // 반응 실행 UFUNCTION(BlueprintCallable) void ExecuteReaction(FGameplayTag ReactionTag, AActor* Instigator); DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams( FOnReactionTriggered, FGameplayTag, ReactionTag, AActor*, Instigator); UPROPERTY(BlueprintAssignable) FOnReactionTriggered OnReactionTriggered; protected: // 엘리먼트 양 (반응 시 소모) UPROPERTY() TMap<FGameplayTag, float> ElementAmounts; // 반응 규칙 테이블 UPROPERTY(EditDefaultsOnly) UDataTable* ReactionRulesTable; UFUNCTION() void OnRep_AttachedElements(); };
03

반응 규칙 테이블

데이터 드리븐 설계

반응 규칙 구조체 USTRUCT(BlueprintType) struct FElementReactionRule : public FTableRowBase { GENERATED_BODY() // 기존 엘리먼트 UPROPERTY(EditAnywhere) FGameplayTag ExistingElement; // 추가되는 엘리먼트 UPROPERTY(EditAnywhere) FGameplayTag AppliedElement; // 발생하는 반응 UPROPERTY(EditAnywhere) FGameplayTag ReactionTag; // 데미지 배율 UPROPERTY(EditAnywhere) float DamageMultiplier = 1.5f; // 반응 Effect UPROPERTY(EditAnywhere) TSubclassOf<UGameplayEffect> ReactionEffect; // 반응 Cue UPROPERTY(EditAnywhere) FGameplayTag ReactionCueTag; }; // 반응 체크 구현 bool UElementSystemComponent::CheckReaction( FGameplayTag NewElement, FGameplayTag& OutReaction) { if (!ReactionRulesTable) return false; // 현재 부착된 모든 엘리먼트와 비교 for (const FGameplayTag& ExistingElement : AttachedElements) { // 테이블에서 반응 규칙 검색 for (auto& Row : ReactionRulesTable->GetRowMap()) { FElementReactionRule* Rule = reinterpret_cast<FElementReactionRule*>(Row.Value); if (Rule->ExistingElement == ExistingElement && Rule->AppliedElement == NewElement) { OutReaction = Rule->ReactionTag; return true; } } } return false; }
04

GAS 연동

Effect와 통합

엘리먼트 GameplayEffect // 불 속성 부여 Effect UCLASS() class UGE_ApplyFireElement : public UGameplayEffect { public: UGE_ApplyFireElement() { DurationPolicy = EGameplayEffectDurationType::HasDuration; DurationMagnitude = FScalableFloat(10.0f); // 10초 지속 // 태그 부여 InheritableOwnedTagsContainer.AddTag( FGameplayTag::RequestGameplayTag(FName("Element.Fire"))); } }; // Execution Calculation에서 반응 처리 void UEC_ElementalDamage::Execute_Implementation( const FGameplayEffectCustomExecutionParameters& ExecutionParams, FGameplayEffectCustomExecutionOutput& OutExecutionOutput) const { // 타겟의 엘리먼트 컴포넌트 확인 AActor* TargetActor = ExecutionParams.GetTargetAbilitySystemComponent()->GetAvatarActor(); UElementSystemComponent* ElementComp = TargetActor->FindComponentByClass<UElementSystemComponent>(); if (!ElementComp) return; // 새 엘리먼트 태그 가져오기 const FGameplayTagContainer* SourceTags = ExecutionParams.GetOwningSpec().CapturedSourceTags.GetAggregatedTags(); FGameplayTag ElementTag = SourceTags->Filter( FGameplayTagContainer(FGameplayTag::RequestGameplayTag(FName("Element")))) .First(); // 반응 체크 FGameplayTag ReactionTag; if (ElementComp->CheckReaction(ElementTag, ReactionTag)) { // 반응 데미지 배율 적용 float ReactionMultiplier = GetReactionMultiplier(ReactionTag); float FinalDamage = BaseDamage * ReactionMultiplier; OutExecutionOutput.AddOutputModifier( FGameplayModifierEvaluatedData( UMyAttributeSet::GetHealthAttribute(), EGameplayModOp::Additive, -FinalDamage)); // 반응 실행 ElementComp->ExecuteReaction(ReactionTag, ExecutionParams.GetSourceAbilitySystemComponent()->GetAvatarActor()); } }
확장성

DataTable을 사용하면 코드 변경 없이 새로운 엘리먼트와 반응을 추가할 수 있습니다. 밸런스 조정도 에디터에서 바로 가능합니다.

05

시각적 피드백

GameplayCue 활용

반응 GameplayCue // 반응 실행 시 Cue 호출 void UElementSystemComponent::ExecuteReaction( FGameplayTag ReactionTag, AActor* Instigator) { // 반응 규칙에서 Cue 태그 가져오기 FGameplayTag CueTag = GetReactionCueTag(ReactionTag); // Cue 파라미터 설정 FGameplayCueParameters CueParams; CueParams.Location = GetOwner()->GetActorLocation(); CueParams.Instigator = Instigator; CueParams.RawMagnitude = GetReactionMagnitude(ReactionTag); // ASC를 통해 Cue 실행 if (UAbilitySystemComponent* ASC = GetOwner()->FindComponentByClass<UAbilitySystemComponent>()) { ASC->ExecuteGameplayCue(CueTag, CueParams); } // 델리게이트 브로드캐스트 OnReactionTriggered.Broadcast(ReactionTag, Instigator); // 소모된 엘리먼트 제거 ConsumeElementsForReaction(ReactionTag); } // 증발 반응 Cue UCLASS() class UGC_VaporizeReaction : public UGameplayCueNotify_Static { public: virtual bool OnExecute_Implementation( AActor* Target, const FGameplayCueParameters& Parameters) const override { // 증기 파티클 UNiagaraFunctionLibrary::SpawnSystemAtLocation( Target->GetWorld(), VaporizeParticle, Parameters.Location); // 사운드 UGameplayStatics::PlaySoundAtLocation( Target, VaporizeSound, Parameters.Location); // 카메라 쉐이크 if (APlayerController* PC = UGameplayStatics::GetPlayerController(Target, 0)) { PC->ClientStartCameraShake(VaporizeCameraShake); } return true; } };
SUMMARY

핵심 요약

  • GameplayTag로 엘리먼트 속성 정의
  • DataTable로 반응 규칙 데이터 드리븐 관리
  • ElementSystemComponent로 부착된 속성 추적
  • Execution Calculation에서 반응 데미지 처리
  • GameplayCue로 반응 시각/청각 피드백
PRACTICE

도전 과제

배운 내용을 직접 실습해보세요

실습 1: 원소 태그 시스템

Fire, Water, Ice, Electric 4가지 원소를 GameplayTag로 정의하고, 각 원소 공격에 해당하는 GE를 만들어 타겟에 원소 태그를 부여하세요.

실습 2: 원소 반응 트리거

PostGameplayEffectExecute에서 기존 원소 태그와 새 원소 태그의 조합을 체크하여 증발(Fire+Water), 과부하(Fire+Electric) 등 반응을 트리거하세요.

심화 과제

undefined