PART 6 - 강의 3/3

GAS 고급 최적화

네트워크 대역폭과 성능 최적화

01

NetExecutionPolicy

어빌리티 실행 위치

EGameplayAbilityNetExecutionPolicy UENUM() enum class EGameplayAbilityNetExecutionPolicy : uint8 { // 서버에서만 실행 (보안 중요 어빌리티) ServerOnly, // 클라이언트에서 예측 실행, 서버에서 확정 LocalPredicted, // 로컬에서만 실행 (시각 효과 등) LocalOnly, // 서버에서 실행하고 오너 클라이언트에도 실행 ServerInitiated }; UCLASS() class UGA_DamageAbility : public UGameplayAbility { public: UGA_DamageAbility() { // 데미지는 서버에서만 계산 NetExecutionPolicy = EGameplayAbilityNetExecutionPolicy::ServerOnly; } }; UCLASS() class UGA_Jump : public UGameplayAbility { public: UGA_Jump() { // 점프는 로컬 예측 NetExecutionPolicy = EGameplayAbilityNetExecutionPolicy::LocalPredicted; } }; UCLASS() class UGA_EmoteAnimation : public UGameplayAbility { public: UGA_EmoteAnimation() { // 이모트는 로컬에서만 NetExecutionPolicy = EGameplayAbilityNetExecutionPolicy::LocalOnly; } };
02

NetSecurityPolicy

클라이언트 신뢰 수준

EGameplayAbilityNetSecurityPolicy UENUM() enum class EGameplayAbilityNetSecurityPolicy : uint8 { // 클라이언트 또는 서버가 실행 가능 ClientOrServer, // 서버만 실행/취소 가능 ServerOnlyExecution, // 서버만 취소 가능 ServerOnlyTermination, // 서버만 실행 및 취소 가능 ServerOnly }; // 보안 민감 어빌리티 UCLASS() class UGA_PurchaseItem : public UGameplayAbility { public: UGA_PurchaseItem() { // 구매는 서버만 실행 가능 NetSecurityPolicy = EGameplayAbilityNetSecurityPolicy::ServerOnly; NetExecutionPolicy = EGameplayAbilityNetExecutionPolicy::ServerOnly; } }; // 치트 방지가 필요한 어빌리티 UCLASS() class UGA_SpecialAttack : public UGameplayAbility { public: UGA_SpecialAttack() { // 클라이언트가 취소하는 것 방지 NetSecurityPolicy = EGameplayAbilityNetSecurityPolicy::ServerOnlyTermination; } };
보안 고려사항

경제 시스템이나 랭킹에 영향을 주는 어빌리티는 반드시 ServerOnly로 설정하세요. 클라이언트에서 실행되는 모든 것은 조작 가능합니다.

03

Attribute 복제 최적화

필요한 것만 복제

선택적 Attribute 복제 // AttributeSet에서 복제 제어 UCLASS() class UMyAttributeSet : public UAttributeSet { GENERATED_BODY() public: // 복제되는 어트리뷰트 UPROPERTY(ReplicatedUsing = OnRep_Health) FGameplayAttributeData Health; UPROPERTY(ReplicatedUsing = OnRep_MaxHealth) FGameplayAttributeData MaxHealth; // 복제하지 않는 어트리뷰트 (서버 전용) UPROPERTY() FGameplayAttributeData InternalDamageMultiplier; // 복제 조건 제어 virtual void GetLifetimeReplicatedProps( TArray<FLifetimeProperty>& OutLifetimeProps) const override { Super::GetLifetimeReplicatedProps(OutLifetimeProps); // 오너에게만 복제 DOREPLIFETIME_CONDITION(UMyAttributeSet, Health, COND_OwnerOnly); DOREPLIFETIME_CONDITION(UMyAttributeSet, MaxHealth, COND_OwnerOnly); // 모든 클라이언트에 복제 (체력바 표시용) // DOREPLIFETIME(UMyAttributeSet, Health); } UFUNCTION() void OnRep_Health(const FGameplayAttributeData& OldHealth) { GAMEPLAYATTRIBUTE_REPNOTIFY(UMyAttributeSet, Health, OldHealth); } UFUNCTION() void OnRep_MaxHealth(const FGameplayAttributeData& OldMaxHealth) { GAMEPLAYATTRIBUTE_REPNOTIFY(UMyAttributeSet, MaxHealth, OldMaxHealth); } };
04

Effect 복제 최소화

불필요한 복제 제거

Effect 복제 최적화 // GameplayEffect에서 복제 제어 UCLASS() class UGE_InternalCalculation : public UGameplayEffect { public: UGE_InternalCalculation() { // 복제하지 않음 (서버 전용 계산) bExecutePeriodicEffectOnApplication = true; // Duration Policy DurationPolicy = EGameplayEffectDurationType::Instant; } }; // EffectContext 최소화 FGameplayEffectContextHandle CreateMinimalContext() { FGameplayEffectContextHandle ContextHandle = ASC->MakeEffectContext(); // 불필요한 데이터 설정하지 않음 // ContextHandle.AddHitResult(...); // 필요시에만 // ContextHandle.AddSourceObject(...); // 필요시에만 return ContextHandle; } // Periodic Effect 주의 // Periodic은 매 틱마다 RPC 발생 가능 // 가능하면 Duration + Modifier 사용 UCLASS() class UGE_OptimizedDot : public UGameplayEffect { public: UGE_OptimizedDot() { // Periodic 대신 총 데미지를 Duration으로 나누어 적용 DurationPolicy = EGameplayEffectDurationType::HasDuration; DurationMagnitude = FScalableFloat(5.0f); // Modifier로 초당 데미지 // 총 50 데미지를 5초에 걸쳐 = 초당 10 FGameplayModifierInfo DamageModifier; DamageModifier.Attribute = UMyAttributeSet::GetHealthAttribute(); DamageModifier.ModifierOp = EGameplayModOp::Additive; DamageModifier.ModifierMagnitude = FScalableFloat(-10.0f); Modifiers.Add(DamageModifier); } };
05

대역폭 모니터링

네트워크 프로파일링

네트워크 디버깅 // 콘솔 명령어로 GAS 네트워크 디버깅 // stat Net - 전체 네트워크 통계 // stat NetActor - 액터별 복제 통계 // Net.ListActorNetworkProperties [ActorName] - 복제 프로퍼티 목록 // GAS 전용 디버깅 // AbilitySystem.Debug.NextCategory - 디버그 카테고리 전환 // AbilitySystem.Debug.NextTarget - 디버그 타겟 전환 // 커스텀 통계 추가 DECLARE_STATS_GROUP(TEXT("GAS"), STATGROUP_GAS, STATCAT_Advanced); DECLARE_CYCLE_STAT(TEXT("GAS Effect Application"), STAT_GASEffectApplication, STATGROUP_GAS); void UMyASC::ApplyGameplayEffectToSelf(...) { SCOPE_CYCLE_COUNTER(STAT_GASEffectApplication); // 원래 로직 Super::ApplyGameplayEffectToSelf(...); } // 대역폭 측정 // Network Profiler 사용 (Editor > Window > Network Profiler) // GAS 관련 RPC 확인: // - ServerSetReplicatedEventWithPayload // - NetMulticast_InvokeGameplayCueExecuted // - Client_NotifyAbilityFailed
최적화 우선순위

1. 불필요한 Effect 복제 제거, 2. Minimal Replication Mode 사용, 3. Local Only Cue 활용, 4. Attribute 복제 조건 설정 순으로 최적화하세요.

SUMMARY

핵심 요약

  • NetExecutionPolicy: 어빌리티 실행 위치 제어
  • NetSecurityPolicy: 클라이언트 권한 제한
  • COND_OwnerOnly: Attribute를 오너에게만 복제
  • Periodic 주의: 매 틱마다 RPC 발생 가능
  • Network Profiler: GAS 대역폭 모니터링
PRACTICE

도전 과제

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

실습 1: GAS 네트워크 프로파일링

stat abilitysystem과 stat net 명령어로 GAS의 네트워크 사용량을 프로파일링하세요. 가장 많은 대역폭을 사용하는 GE와 어트리뷰트를 식별하세요.

실습 2: Minimal Replication Tags

MinimalReplicationTags를 설정하여 비소유 클라이언트에게 전달되는 태그를 제한하세요. 적의 내부 상태 태그가 다른 클라이언트에 노출되지 않도록 하세요.

심화 과제

undefined