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