콘텐츠로 이동

UE5 전투 시스템 설계 패턴

입력 → Ability (GAS) → 애니메이션 → HitBox 활성화
충돌 감지 → DamageEvent
Target ASC → GameplayEffect (데미지)
Attribute (Health 감소) → 사망 처리
UCLASS()
class UHitboxComponent : public UCapsuleComponent {
GENERATED_BODY()
bool bActive = false;
TSet<AActor*> HitActors; // 이미 히트한 액터 (한 공격에 중복 방지)
public:
UFUNCTION() void OnOverlapBegin(UPrimitiveComponent* Comp, AActor* Other, ...);
void Activate() {
bActive = true;
HitActors.Empty();
SetCollisionEnabled(ECollisionEnabled::QueryOnly);
}
void Deactivate() {
bActive = false;
SetCollisionEnabled(ECollisionEnabled::NoCollision);
}
};
void UHitboxComponent::OnOverlapBegin(UPrimitiveComponent* Comp, AActor* Other, ...) {
if (!bActive || HitActors.Contains(Other)) return;
HitActors.Add(Other);
// GAS를 통한 데미지 적용
FGameplayEventData EventData;
EventData.Instigator = GetOwner();
EventData.Target = Other;
EventData.EventMagnitude = DamageAmount;
UAbilitySystemBlueprintLibrary::SendGameplayEventToActor(
Other, FGameplayTag::RequestGameplayTag("Event.HitReact"), EventData);
}
// GE_Damage.cpp — 즉시 체력 감소
UCLASS()
class UGE_Damage : public UGameplayEffect {
GENERATED_BODY()
UGE_Damage() {
DurationPolicy = EGameplayEffectDurationType::Instant;
// Modifier: Health -= Magnitude
FGameplayModifierInfo Modifier;
Modifier.Attribute = UMyAttributeSet::GetHealthAttribute();
Modifier.ModifierOp = EGameplayModOp::Additive;
Modifier.ModifierMagnitude =
FScalableFloat(-1.f); // Execution 또는 SetByCaller로 실제 값 결정
Modifiers.Add(Modifier);
}
};
// 적용 코드
void ApplyDamage(UAbilitySystemComponent* TargetASC, float DamageAmount) {
FGameplayEffectSpecHandle Spec = SourceASC->MakeOutgoingSpec(
UGE_Damage::StaticClass(), Level, SourceASC->MakeEffectContext());
Spec.Data->SetSetByCallerMagnitude(
FGameplayTag::RequestGameplayTag("Data.Damage"), DamageAmount);
TargetASC->ApplyGameplayEffectSpecToSelf(*Spec.Data);
}
UCLASS()
class UComboComponent : public UActorComponent {
GENERATED_BODY()
int32 ComboIndex = 0;
bool bCanCombo = false;
FTimerHandle ComboResetTimer;
TArray<TSubclassOf<UGameplayAbility>> ComboAbilities;
public:
void OnAttackInput() {
if (ComboIndex == 0 || bCanCombo) {
ExecuteCombo();
}
}
void ExecuteCombo() {
if (ComboIndex >= ComboAbilities.Num()) ComboIndex = 0;
ASC->TryActivateAbilityByClass(ComboAbilities[ComboIndex]);
ComboIndex++;
bCanCombo = false;
// 콤보 윈도우 종료 타이머
GetWorld()->GetTimerManager().SetTimer(
ComboResetTimer, this, &UComboComponent::ResetCombo, 1.5f);
}
// AnimNotify에서 호출: 다음 입력 허용 구간
void OpenComboWindow() { bCanCombo = true; }
void CloseComboWindow() { bCanCombo = false; }
void ResetCombo() {
ComboIndex = 0;
bCanCombo = false;
}
};
// 막기 상태 태그로 데미지 감소
void UDamageExecutionCalculation::Execute_Implementation(
const FGameplayEffectCustomExecutionParameters& Params,
FGameplayEffectCustomExecutionOutput& OutExecOutput) const {
float Damage = 0.f;
Params.AttemptCalculateCapturedAttributeMagnitude(DamageDef, EvalParams, Damage);
// 막기 태그가 있으면 데미지 80% 감소
FGameplayTagContainer TargetTags;
Params.GetTargetAbilitySystemComponent()->GetOwnedGameplayTags(TargetTags);
if (TargetTags.HasTag(FGameplayTag::RequestGameplayTag("State.Blocking")))
Damage *= 0.2f;
OutExecOutput.AddOutputModifier(
FGameplayModifierEvaluatedData(HealthAttr, EGameplayModOp::Additive, -Damage));
}
  • HitBox는 컴포넌트로 분리, 활성화 구간 제어 + 중복 히트 방지
  • 데미지는 GAS GameplayEffect + SetByCaller로 유연하게 전달
  • 콤보는 입력 큐잉 없이 “콤보 윈도우” 플래그로 단순하게 구현
  • 막기/회피는 Gameplay Tag로 상태 표현 → Execution에서 감소
  • 모든 전투 로직을 GAS Ability 안으로 집중시켜 재사용성 확보