zoraki

Aktif üye
12 Şub 2018
142
42
28
(6) Ankara
Karakterlerin darbe alıp savrulması (knockback) mekaniklerini kurgularken karşılaştığımız performans ve ağ trafiği sorunlarına yönelik geliştirdiğim optimize yöntemi paylaşmak istiyorum. Standart Launch Character gibi fonksiyonların sunucu yükünü %5-10 artırması ve ağ trafiğini yorması nedeniyle böyle bir yapıya geçiş yaptım.

-Sunucu Güvenliği (Anti-Cheat): Tüm itme mesafesi ve dinamik Impact Distance hesaplamaları tamamen sunucuda (Server) yapılıyor. Hile riskini minimize etmek için istemciden gelen hiçbir veri konumlama için baz alınmıyor.
-Ağ Verimliliği: Yaptığım testlerde, tek kişilik 5 dakikalık bir oyun oturumunda toplam ağ paketi trafiğini 630 KB gibi oldukça minimal bir seviyeye indirmeyi başardım.
-İstemci Tarafı Smoothing: Sunucu karakteri anında yeni konumuna ışınladığında (Teleport), istemcide oluşan "sıçrama" (jitter) etkisini yok etmek için Mesh'i geçici olarak kapsülden bağımsız (Absolute) hale getiriyorum. Bir Timeline ve Lerp yardımıyla görseli yeni konuma pürüzsüzce kaydırıyorum.
Ek Optimizasyon: Client CPU yükünü daha da düşürmek için her karede çalışan Timeline yerine, kontrol edilebilir bir Timer yapısına geçmeyi planlıyorum.

Sunucu Tarafı (Hit Hesaplama ve Teleport):
- https://blueprintue.com/blueprint/nn9s4r08/
- https://blueprintue.com/blueprint/hydnzpjr/

İstemci Görsel Kaydırma (Timeline & Absolute Mesh):
- https://blueprintue.com/blueprint/bg191m28/
- https://blueprintue.com/blueprint/elgj7z0w/

Videonun 0:16 saniyesinde yavaşlatılmış çekimde pürüzsüzleşme farkı net olarak görülmektedir.
Video: https://streamable.com/1rsdtu
 
Son düzenleme:
Oldukça güzel duruyor tebrik ederim. Şans eseri yeni bir projede itme olayına benim de ihtiyacım oldu :) Eğer ileride projeyi cpp ye geçirirsen tavsiye edebileceğim yöntem

void OnHit(bool bIsStun, const FVector& Force, const float& Duration)
{
if (!bCanMove) return;

UAnimMontage* AnimMontage = bIsStun ? StunAnim : KnockbackAnim;
if (!AnimMontage) return;
AIController->StopMovement();
if (const float MontageDuration = PlayAnimMontage(AnimMontage); MontageDuration > 0.f)
{
bCanMove = false;

FRootMotionSource_ConstantForce* RMS = new FRootMotionSource_ConstantForce();
RMS->Force = Force;
RMS->Duration = Duration;
RMS->StrengthOverTime = nullptr;
RMS->AccumulateMode = ERootMotionAccumulateMode::Additive;
GetCharacterMovement()->ApplyRootMotionSource(TSharedPtr<FRootMotionSource>(RMS));

GetWorld()->GetTimerManager().SetTimer(TimerHandle, [this]()
{
bCanMove = true;
}, MontageDuration, false);
}
}

RootMotionSource tanımladığın süre içinde uyguladığın force yönünde ve değerinde karakteri ileriye itiyor, denediklerim arasında bunun kadar verimli çalışan olmadı, tam olarak istediğim etkiyi verdi, tavsiyemdir ;)
 
  • Beğen
Tepkiler: zoraki
Eyvallah hocam, çok teşekkür ederim bu güzel bilgilendirme ve tavsiye için. :)

Henüz yolun başında sayılırım, biraz acemilik var tabii ama bu tarz profesyonel dokunuşları görmek ufkumu açıyor. Şu an Blueprint ile sistemi en azından ağ trafiği ve hile koruması açısından sağlama almaya çalışıyorum. Nasip olur da ileride projeyi belli bir noktaya getirip tutulur bir hale sokarsak, mutlaka dediğin gibi C++ tarafına geçip bu RootMotionSource olayına gireceğim.

Paylaştığın kod örneğini de notlarımın arasına aldım, o gün geldiğinde çok işime yarayacak. Teşekkürler eline sağlık
 
  • Beğen
Tepkiler: nonlinear
Eninde sonunda projeyi cpp taşıyacaktım ve taşıdım. Dediğin gibi RMS yapısını kullandım. Gerçekten de itme hissiyatı ve ağ senkronizasyonu konusunda denediğim en verimli yöntem bu oldu. Tavsiyen üzerine yaptığım bp üzerinden dinamik olarak kontrol edilen yapıyı aşağıya bırakıyorum. Teşekkürler

void UImpactPhysicsLibrary::ApplyLaunch(ACharacter* Target, FVector LaunchVector, float Duration, bool bResetVelocity, EMovementMode MovementMode)
{
if (!Target || !Target->GetCharacterMovement()) return;

if (bResetVelocity)
{
Target->GetCharacterMovement()->StopMovementImmediately();
}

if (AController* Con = Target->GetController())
{
Con->StopMovement();
}

FName UniqueName = FName(*FString:rintf(TEXT("Impact_%f"), Target->GetWorld()->GetTimeSeconds()));

TSharedPtr<FRootMotionSource_ConstantForce> ConstantForce = MakeShared<FRootMotionSource_ConstantForce>();
ConstantForce->InstanceName = UniqueName;
ConstantForce->AccumulateMode = ERootMotionAccumulateMode::Additive;
ConstantForce->Priority = 10;
ConstantForce->Force = LaunchVector;
ConstantForce->Duration = FMath::Max(0.01f, Duration);

Target->GetCharacterMovement()->SetMovementMode(MovementMode);
Target->GetCharacterMovement()->DefaultLandMovementMode = MovementMode;

Target->GetCharacterMovement()->ApplyRootMotionSource(ConstantForce);
}
Ekran_goruntusu_2026-01-07_122355.png
 
Son düzenleme:
  • Beğen
Tepkiler: nonlinear