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