Tune adaptive extrapolation defaults, add AdaptiveMinSpeed property, fix debug visuals
- Add AdaptiveMinSpeed UPROPERTY (default 30 cm/s) to avoid false deceleration at low speeds - Update default values: BufferTime=300ms, DiscardTime=40ms, Sensitivity=1.5, Damping=8.0 - Replace debug spheres with points to not obstruct aiming view - Add detailed debug logs with [LOW]/[DZ]/[DEC] tags for dead zone diagnosis - Convert buffer/discard time units to milliseconds - Set AdaptiveExtrapolation as default AntiRecoil mode - Fix DLL copy error handling in DinkeyPlugin and ViveVBS build scripts - Add AimStabilization dead zone with smooth transition (no hard jumps) - Add AimSmoothingSpeed property for temporal aim smoothing Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
83188b1fa1
commit
48737b60c9
@ -92,8 +92,9 @@ void UEBBarrel::UpdateTransformHistory()
|
||||
|
||||
TransformHistory.Add(Sample);
|
||||
|
||||
// Trim buffer: remove samples older than AntiRecoilBufferTime
|
||||
// Trim buffer: remove samples older than AntiRecoilBufferTimeMs
|
||||
// During calibration, keep a larger buffer (0.5s min) for reliable 3-sigma analysis
|
||||
const float AntiRecoilBufferTime = AntiRecoilBufferTimeMs / 1000.0f;
|
||||
float EffectiveBufferTime = CalibrateAntiRecoil
|
||||
? FMath::Max(AntiRecoilBufferTime, 0.5f)
|
||||
: AntiRecoilBufferTime;
|
||||
@ -128,7 +129,7 @@ void UEBBarrel::ComputeAntiRecoilTransform()
|
||||
|
||||
case EAntiRecoilMode::ARM_LinearExtrapolation:
|
||||
{
|
||||
int32 SafeN = GetSafeCount(TransformHistory, GetWorld()->GetTimeSeconds(), AntiRecoilDiscardTime);
|
||||
int32 SafeN = GetSafeCount(TransformHistory, GetWorld()->GetTimeSeconds(), AntiRecoilDiscardTimeMs / 1000.0f);
|
||||
if (SafeN >= 2)
|
||||
{
|
||||
PredictLinearExtrapolation(GetWorld()->GetTimeSeconds(), Location, Aim);
|
||||
@ -148,7 +149,7 @@ void UEBBarrel::ComputeAntiRecoilTransform()
|
||||
|
||||
case EAntiRecoilMode::ARM_WeightedRegression:
|
||||
{
|
||||
int32 SafeN = GetSafeCount(TransformHistory, GetWorld()->GetTimeSeconds(), AntiRecoilDiscardTime);
|
||||
int32 SafeN = GetSafeCount(TransformHistory, GetWorld()->GetTimeSeconds(), AntiRecoilDiscardTimeMs / 1000.0f);
|
||||
if (SafeN >= 2)
|
||||
{
|
||||
PredictWeightedRegression(GetWorld()->GetTimeSeconds(), Location, Aim);
|
||||
@ -168,7 +169,7 @@ void UEBBarrel::ComputeAntiRecoilTransform()
|
||||
|
||||
case EAntiRecoilMode::ARM_WeightedLinearRegression:
|
||||
{
|
||||
int32 SafeN = GetSafeCount(TransformHistory, GetWorld()->GetTimeSeconds(), AntiRecoilDiscardTime);
|
||||
int32 SafeN = GetSafeCount(TransformHistory, GetWorld()->GetTimeSeconds(), AntiRecoilDiscardTimeMs / 1000.0f);
|
||||
if (SafeN >= 2)
|
||||
{
|
||||
PredictWeightedLinearRegression(GetWorld()->GetTimeSeconds(), Location, Aim);
|
||||
@ -188,7 +189,7 @@ void UEBBarrel::ComputeAntiRecoilTransform()
|
||||
|
||||
case EAntiRecoilMode::ARM_KalmanFilter:
|
||||
{
|
||||
int32 SafeN = GetSafeCount(TransformHistory, GetWorld()->GetTimeSeconds(), AntiRecoilDiscardTime);
|
||||
int32 SafeN = GetSafeCount(TransformHistory, GetWorld()->GetTimeSeconds(), AntiRecoilDiscardTimeMs / 1000.0f);
|
||||
if (SafeN > 0)
|
||||
{
|
||||
// Feed only the latest SAFE sample to the Kalman filter
|
||||
@ -211,7 +212,7 @@ void UEBBarrel::ComputeAntiRecoilTransform()
|
||||
|
||||
case EAntiRecoilMode::ARM_AdaptiveExtrapolation:
|
||||
{
|
||||
int32 SafeN = GetSafeCount(TransformHistory, GetWorld()->GetTimeSeconds(), AntiRecoilDiscardTime);
|
||||
int32 SafeN = GetSafeCount(TransformHistory, GetWorld()->GetTimeSeconds(), AntiRecoilDiscardTimeMs / 1000.0f);
|
||||
if (SafeN >= 2)
|
||||
{
|
||||
PredictAdaptiveExtrapolation(GetWorld()->GetTimeSeconds(), Location, Aim);
|
||||
@ -237,7 +238,7 @@ void UEBBarrel::ComputeAntiRecoilTransform()
|
||||
|
||||
void UEBBarrel::PredictLinearExtrapolation(double CurrentTime, FVector& OutLocation, FVector& OutAim) const
|
||||
{
|
||||
const int32 SafeN = GetSafeCount(TransformHistory, GetWorld()->GetTimeSeconds(), AntiRecoilDiscardTime);
|
||||
const int32 SafeN = GetSafeCount(TransformHistory, GetWorld()->GetTimeSeconds(), AntiRecoilDiscardTimeMs / 1000.0f);
|
||||
if (SafeN < 2)
|
||||
{
|
||||
OutLocation = TransformHistory[0].Location;
|
||||
@ -317,7 +318,7 @@ void UEBBarrel::PredictLinearExtrapolation(double CurrentTime, FVector& OutLocat
|
||||
|
||||
void UEBBarrel::PredictWeightedLinearRegression(double CurrentTime, FVector& OutLocation, FVector& OutAim) const
|
||||
{
|
||||
const int32 SafeN = GetSafeCount(TransformHistory, GetWorld()->GetTimeSeconds(), AntiRecoilDiscardTime);
|
||||
const int32 SafeN = GetSafeCount(TransformHistory, GetWorld()->GetTimeSeconds(), AntiRecoilDiscardTimeMs / 1000.0f);
|
||||
if (SafeN < 2)
|
||||
{
|
||||
OutLocation = TransformHistory[0].Location;
|
||||
@ -390,7 +391,7 @@ void UEBBarrel::PredictWeightedLinearRegression(double CurrentTime, FVector& Out
|
||||
|
||||
void UEBBarrel::PredictWeightedRegression(double CurrentTime, FVector& OutLocation, FVector& OutAim) const
|
||||
{
|
||||
const int32 SafeN = GetSafeCount(TransformHistory, GetWorld()->GetTimeSeconds(), AntiRecoilDiscardTime);
|
||||
const int32 SafeN = GetSafeCount(TransformHistory, GetWorld()->GetTimeSeconds(), AntiRecoilDiscardTimeMs / 1000.0f);
|
||||
if (SafeN < 2)
|
||||
{
|
||||
OutLocation = TransformHistory[0].Location;
|
||||
@ -706,7 +707,7 @@ void UEBBarrel::PredictKalmanFilter(double CurrentTime, FVector& OutLocation, FV
|
||||
|
||||
void UEBBarrel::PredictAdaptiveExtrapolation(double CurrentTime, FVector& OutLocation, FVector& OutAim) const
|
||||
{
|
||||
const int32 SafeN = GetSafeCount(TransformHistory, GetWorld()->GetTimeSeconds(), AntiRecoilDiscardTime);
|
||||
const int32 SafeN = GetSafeCount(TransformHistory, GetWorld()->GetTimeSeconds(), AntiRecoilDiscardTimeMs / 1000.0f);
|
||||
if (SafeN < 2)
|
||||
{
|
||||
OutLocation = TransformHistory[0].Location;
|
||||
@ -782,30 +783,68 @@ void UEBBarrel::PredictAdaptiveExtrapolation(double CurrentTime, FVector& OutLoc
|
||||
float RecentAimSpeed = RecentAimVel.Size();
|
||||
|
||||
// Confidence: ratio of recent speed to average speed.
|
||||
// Dead zone: ratios above AdaptiveDeadZone are treated as 1.0 (normal fluctuations).
|
||||
// Remapped ratio: (ratio - deadzone) / (1 - deadzone), clamped to [0, 1].
|
||||
// AdaptiveSensitivity is the power exponent on the remapped ratio.
|
||||
// Dead zone: ratios above AdaptiveDeadZone are treated as 1.0 (no correction).
|
||||
// Only ratios BELOW AdaptiveDeadZone trigger extrapolation reduction.
|
||||
// Below dead zone: remap [0, deadzone] → [0, 1] then apply sensitivity exponent.
|
||||
// Minimum speed threshold: below this, speed ratios are unreliable (noise dominates),
|
||||
// so we keep full confidence to avoid false deceleration detection.
|
||||
const float MinSpeedThreshold = AdaptiveMinSpeed;
|
||||
|
||||
float PosRatio = 1.0f;
|
||||
float PosConfidence = 1.0f;
|
||||
if (AvgPosSpeed > SMALL_NUMBER)
|
||||
float PosRemapped = 1.0f;
|
||||
if (AvgPosSpeed > MinSpeedThreshold)
|
||||
{
|
||||
PosRatio = FMath::Clamp(RecentPosSpeed / AvgPosSpeed, 0.0f, 1.0f);
|
||||
float PosRemapped = (AdaptiveDeadZone < 1.0f)
|
||||
? FMath::Clamp((PosRatio - AdaptiveDeadZone) / (1.0f - AdaptiveDeadZone), 0.0f, 1.0f)
|
||||
: (PosRatio >= 1.0f ? 1.0f : 0.0f);
|
||||
if (PosRatio >= AdaptiveDeadZone)
|
||||
{
|
||||
// Inside dead zone: no correction, full confidence
|
||||
PosRemapped = 1.0f;
|
||||
PosConfidence = 1.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Below dead zone: real deceleration detected
|
||||
// Remap [0, deadzone] → [0, 1]
|
||||
PosRemapped = (AdaptiveDeadZone > SMALL_NUMBER)
|
||||
? FMath::Clamp(PosRatio / AdaptiveDeadZone, 0.0f, 1.0f)
|
||||
: 0.0f;
|
||||
PosConfidence = FMath::Pow(PosRemapped, AdaptiveSensitivity);
|
||||
}
|
||||
}
|
||||
// else: AvgPosSpeed <= MinSpeedThreshold → keep defaults (Ratio=1, Conf=1)
|
||||
|
||||
float AimRatio = 1.0f;
|
||||
float AimConfidence = 1.0f;
|
||||
if (AvgAimSpeed > SMALL_NUMBER)
|
||||
float AimRemapped = 1.0f;
|
||||
if (AvgAimSpeed > MinSpeedThreshold)
|
||||
{
|
||||
AimRatio = FMath::Clamp(RecentAimSpeed / AvgAimSpeed, 0.0f, 1.0f);
|
||||
float AimRemapped = (AdaptiveDeadZone < 1.0f)
|
||||
? FMath::Clamp((AimRatio - AdaptiveDeadZone) / (1.0f - AdaptiveDeadZone), 0.0f, 1.0f)
|
||||
: (AimRatio >= 1.0f ? 1.0f : 0.0f);
|
||||
if (AimRatio >= AdaptiveDeadZone)
|
||||
{
|
||||
// Inside dead zone: no correction, full confidence
|
||||
AimRemapped = 1.0f;
|
||||
AimConfidence = 1.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Below dead zone: real deceleration detected
|
||||
// Remap [0, deadzone] → [0, 1]
|
||||
AimRemapped = (AdaptiveDeadZone > SMALL_NUMBER)
|
||||
? FMath::Clamp(AimRatio / AdaptiveDeadZone, 0.0f, 1.0f)
|
||||
: 0.0f;
|
||||
AimConfidence = FMath::Pow(AimRemapped, AdaptiveSensitivity);
|
||||
}
|
||||
}
|
||||
// else: AvgAimSpeed <= MinSpeedThreshold → keep defaults (Ratio=1, Conf=1)
|
||||
|
||||
// Debug logs for dead zone diagnosis
|
||||
UE_LOG(LogTemp, Log, TEXT("[AdaptiveExtrap] DZ=%.3f Sens=%.2f MinSpd=%.1f | Pos: Ratio=%.4f Remap=%.4f Conf=%.4f (Avg=%.2f Recent=%.2f %s) | Aim: Ratio=%.4f Remap=%.4f Conf=%.4f (Avg=%.2f Recent=%.2f %s)"),
|
||||
AdaptiveDeadZone, AdaptiveSensitivity, MinSpeedThreshold,
|
||||
PosRatio, PosRemapped, PosConfidence, AvgPosSpeed, RecentPosSpeed,
|
||||
(AvgPosSpeed <= MinSpeedThreshold) ? TEXT("[LOW]") : (PosRatio >= AdaptiveDeadZone ? TEXT("[DZ]") : TEXT("[DEC]")),
|
||||
AimRatio, AimRemapped, AimConfidence, AvgAimSpeed, RecentAimSpeed,
|
||||
(AvgAimSpeed <= MinSpeedThreshold) ? TEXT("[LOW]") : (AimRatio >= AdaptiveDeadZone ? TEXT("[DZ]") : TEXT("[DEC]")));
|
||||
|
||||
// Extrapolate from last safe sample
|
||||
const FTimestampedTransform& LastSafe = TransformHistory[SafeN - 1];
|
||||
@ -814,6 +853,8 @@ void UEBBarrel::PredictAdaptiveExtrapolation(double CurrentTime, FVector& OutLoc
|
||||
// Write debug values for HUD display
|
||||
DbgPosRatio = PosRatio;
|
||||
DbgAimRatio = AimRatio;
|
||||
DbgPosRemapped = PosRemapped;
|
||||
DbgAimRemapped = AimRemapped;
|
||||
DbgPosConfidence = PosConfidence;
|
||||
DbgAimConfidence = AimConfidence;
|
||||
DbgAvgPosSpeed = AvgPosSpeed;
|
||||
|
||||
@ -17,6 +17,7 @@ void UEBBarrel::Shoot(bool Trigger, int nextFireID) {
|
||||
if (ClientSideAim && GetOwner()->GetRemoteRole() == ROLE_Authority && Trigger) {
|
||||
Aim = GetComponentTransform().GetUnitAxis(EAxis::X);
|
||||
Location = GetComponentTransform().GetLocation();
|
||||
ApplyAimStabilization();
|
||||
nextFireEventID = nextFireID;
|
||||
ShootRepCSA(Trigger, UGameplayStatics::RebaseLocalOriginOntoZero(GetWorld(), Location), Aim, nextFireID);
|
||||
}
|
||||
|
||||
@ -29,7 +29,7 @@ FPrimitiveSceneProxy* UEBBarrel::CreateSceneProxy() {
|
||||
const FLinearColor DrawColor = GetViewSelectionColor(FColor::Green, *View, IsSelected(), IsHovered(), true, IsIndividuallySelected());
|
||||
|
||||
FPrimitiveDrawInterface* PDI = Collector.GetPDI(ViewIndex);
|
||||
DrawDirectionalArrow(PDI, Transform, DrawColor, Component->DebugArrowSize, Component->DebugArrowSize*0.1f, 16, Component->DebugArrowSize*0.01f);
|
||||
DrawDirectionalArrow(PDI, Transform, DrawColor, Component->DebugArrowSize, Component->DebugArrowSize*0.1f, 16, 0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,6 +45,41 @@ void UEBBarrel::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
Super::EndPlay(EndPlayReason);
|
||||
}
|
||||
|
||||
void UEBBarrel::ApplyAimStabilization()
|
||||
{
|
||||
if (AimDeadZoneDegrees <= 0.0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bStabilizedAimInitialized)
|
||||
{
|
||||
StabilizedAim = Aim;
|
||||
bStabilizedAimInitialized = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Compute angle between current aim and last stable aim
|
||||
float AngleDeg = FMath::RadiansToDegrees(FMath::Acos(FMath::Clamp(FVector::DotProduct(Aim, StabilizedAim), -1.0f, 1.0f)));
|
||||
if (AngleDeg <= AimDeadZoneDegrees)
|
||||
{
|
||||
// Within dead zone: keep previous stable aim (filter jitter)
|
||||
Aim = StabilizedAim;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Outside dead zone: smooth transition to avoid jumps
|
||||
// Subtract the dead zone from the angle so movement starts from zero
|
||||
float ExcessDeg = AngleDeg - AimDeadZoneDegrees;
|
||||
float BlendAlpha = FMath::Clamp(ExcessDeg / AngleDeg, 0.0f, 1.0f);
|
||||
|
||||
// Slerp from stabilized aim toward real aim, removing the dead zone portion
|
||||
StabilizedAim = FMath::Lerp(StabilizedAim, Aim, BlendAlpha).GetSafeNormal();
|
||||
Aim = StabilizedAim;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UEBBarrel::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
|
||||
{
|
||||
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
|
||||
@ -58,6 +93,7 @@ void UEBBarrel::TickComponent(float DeltaTime, ELevelTick TickType, FActorCompon
|
||||
if (TimeSinceAimUpdate >= 1.0f / ClientAimUpdateFrequency) {
|
||||
|
||||
ComputeAntiRecoilTransform();
|
||||
ApplyAimStabilization();
|
||||
|
||||
ClientAim(UGameplayStatics::RebaseLocalOriginOntoZero(GetWorld(),Location), Aim);
|
||||
TimeSinceAimUpdate = FMath::Fmod(TimeSinceAimUpdate, 1.0f / ClientAimUpdateFrequency);
|
||||
@ -65,6 +101,7 @@ void UEBBarrel::TickComponent(float DeltaTime, ELevelTick TickType, FActorCompon
|
||||
}else{
|
||||
if (!RemoteAimReceived) {
|
||||
ComputeAntiRecoilTransform();
|
||||
ApplyAimStabilization();
|
||||
}
|
||||
else {
|
||||
FVector LocOffset = (Location - GetComponentLocation());
|
||||
@ -77,6 +114,7 @@ void UEBBarrel::TickComponent(float DeltaTime, ELevelTick TickType, FActorCompon
|
||||
}
|
||||
else {
|
||||
ComputeAntiRecoilTransform();
|
||||
ApplyAimStabilization();
|
||||
}
|
||||
|
||||
// Debug visualization: raw tracker (green) vs predicted aim (red)
|
||||
@ -104,9 +142,9 @@ void UEBBarrel::TickComponent(float DeltaTime, ELevelTick TickType, FActorCompon
|
||||
DrawDebugLine(GetWorld(), Location, Location + Aim * DebugAntiRecoilLineLength,
|
||||
FColor::Red, false, -1.0f, 0, DebugAntiRecoilLineThickness);
|
||||
|
||||
// Small spheres at origins for clarity
|
||||
DrawDebugSphere(GetWorld(), RawLocation, 1.5f, 6, FColor::Green, false, -1.0f, 0, DebugAntiRecoilLineThickness * 0.5f);
|
||||
DrawDebugSphere(GetWorld(), Location, 1.5f, 6, FColor::Red, false, -1.0f, 0, DebugAntiRecoilLineThickness * 0.5f);
|
||||
// Small dots at origins for clarity
|
||||
DrawDebugPoint(GetWorld(), RawLocation, 3.0f, FColor::Green, false, -1.0f, 0);
|
||||
DrawDebugPoint(GetWorld(), Location, 3.0f, FColor::Red, false, -1.0f, 0);
|
||||
|
||||
// Yellow line: shows where shot would land WITHOUT anti-recoil correction
|
||||
// Captures raw aim at shock onset and persists for DebugIMUShockDisplayTime seconds
|
||||
@ -131,7 +169,7 @@ void UEBBarrel::TickComponent(float DeltaTime, ELevelTick TickType, FActorCompon
|
||||
DrawDebugLine(GetWorld(), DebugIMUShockCapturedLocation,
|
||||
DebugIMUShockCapturedLocation + DebugIMUShockCapturedAim * DebugAntiRecoilLineLength,
|
||||
FColor::Green, false, -1.0f, 0, DebugAntiRecoilLineThickness);
|
||||
DrawDebugSphere(GetWorld(), DebugIMUShockCapturedLocation, 3.0f, 8, FColor::Green, false, -1.0f, 0, DebugAntiRecoilLineThickness);
|
||||
DrawDebugPoint(GetWorld(), DebugIMUShockCapturedLocation, 3.0f, FColor::Green, false, -1.0f, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -147,7 +185,7 @@ void UEBBarrel::TickComponent(float DeltaTime, ELevelTick TickType, FActorCompon
|
||||
DrawDebugLine(GetWorld(), DebugCorrectedShotCapturedLocation,
|
||||
DebugCorrectedShotCapturedLocation + DebugCorrectedShotCapturedAim * DebugAntiRecoilLineLength,
|
||||
FColor::Red, false, -1.0f, 0, DebugAntiRecoilLineThickness);
|
||||
DrawDebugSphere(GetWorld(), DebugCorrectedShotCapturedLocation, 3.0f, 8, FColor::Red, false, -1.0f, 0, DebugAntiRecoilLineThickness);
|
||||
DrawDebugPoint(GetWorld(), DebugCorrectedShotCapturedLocation, 3.0f, FColor::Red, false, -1.0f, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -185,7 +223,7 @@ void UEBBarrel::TickComponent(float DeltaTime, ELevelTick TickType, FActorCompon
|
||||
FVector RealPos = GetComponentTransform().GetLocation();
|
||||
FVector RealAim = GetComponentTransform().GetUnitAxis(EAxis::X);
|
||||
// Count safe samples (same logic as GetSafeCount in AntiRecoilPredict.cpp)
|
||||
double SafeCutoff = GetWorld()->GetTimeSeconds() - AntiRecoilDiscardTime;
|
||||
double SafeCutoff = GetWorld()->GetTimeSeconds() - (AntiRecoilDiscardTimeMs / 1000.0f);
|
||||
int32 SafeN = 0;
|
||||
for (int32 si = 0; si < TransformHistory.Num(); si++)
|
||||
{
|
||||
@ -240,17 +278,26 @@ void UEBBarrel::TickComponent(float DeltaTime, ELevelTick TickType, FActorCompon
|
||||
GEngine->AddOnScreenDebugMessage(HudKey--, 0.0f, HudTitle,
|
||||
TEXT("====== ADAPTIVE EXTRAPOLATION ======"));
|
||||
|
||||
// Dead zone info
|
||||
GEngine->AddOnScreenDebugMessage(HudKey--, 0.0f, HudVal,
|
||||
FString::Printf(TEXT(" DeadZone=%.2f Sensitivity=%.1f"),
|
||||
AdaptiveDeadZone, AdaptiveSensitivity));
|
||||
|
||||
// Speed ratio + confidence (position)
|
||||
FColor PosColor = (DbgPosConfidence > 0.9f) ? HudGood : HudWarn;
|
||||
bool bPosInDeadZone = (DbgPosRatio >= AdaptiveDeadZone);
|
||||
FColor PosColor = bPosInDeadZone ? HudGood : HudWarn;
|
||||
GEngine->AddOnScreenDebugMessage(HudKey--, 0.0f, PosColor,
|
||||
FString::Printf(TEXT(" Pos: ratio=%.2f conf=%.2f speed=%.1f/%.1f cm/s"),
|
||||
DbgPosRatio, DbgPosConfidence, DbgRecentPosSpeed, DbgAvgPosSpeed));
|
||||
FString::Printf(TEXT(" Pos: ratio=%.3f %s remap=%.3f conf=%.3f spd=%.1f/%.1f"),
|
||||
DbgPosRatio, bPosInDeadZone ? TEXT("[DZ]") : TEXT("[!!]"),
|
||||
DbgPosRemapped, DbgPosConfidence, DbgRecentPosSpeed, DbgAvgPosSpeed));
|
||||
|
||||
// Speed ratio + confidence (aim)
|
||||
FColor AimColor = (DbgAimConfidence > 0.9f) ? HudGood : HudWarn;
|
||||
bool bAimInDeadZone = (DbgAimRatio >= AdaptiveDeadZone);
|
||||
FColor AimColor = bAimInDeadZone ? HudGood : HudWarn;
|
||||
GEngine->AddOnScreenDebugMessage(HudKey--, 0.0f, AimColor,
|
||||
FString::Printf(TEXT(" Aim: ratio=%.2f conf=%.2f speed=%.4f/%.4f /s"),
|
||||
DbgAimRatio, DbgAimConfidence, DbgRecentAimSpeed, DbgAvgAimSpeed));
|
||||
FString::Printf(TEXT(" Aim: ratio=%.3f %s remap=%.3f conf=%.3f spd=%.4f/%.4f"),
|
||||
DbgAimRatio, bAimInDeadZone ? TEXT("[DZ]") : TEXT("[!!]"),
|
||||
DbgAimRemapped, DbgAimConfidence, DbgRecentAimSpeed, DbgAvgAimSpeed));
|
||||
|
||||
// Extrapolation time
|
||||
GEngine->AddOnScreenDebugMessage(HudKey--, 0.0f, HudVal,
|
||||
@ -320,11 +367,11 @@ void UEBBarrel::TickComponent(float DeltaTime, ELevelTick TickType, FActorCompon
|
||||
LastCalibrationResult.AvgPeakAngleDeviation,
|
||||
LastCalibrationResult.AvgPeakPositionDeviation));
|
||||
GEngine->AddOnScreenDebugMessage(CalKey--, 0.0f, CalWarn,
|
||||
FString::Printf(TEXT(" >> DiscardTime: %.4fs"),
|
||||
LastCalibrationResult.RecommendedDiscardTime));
|
||||
FString::Printf(TEXT(" >> DiscardTime: %.1f ms"),
|
||||
LastCalibrationResult.RecommendedDiscardTime * 1000.0f));
|
||||
GEngine->AddOnScreenDebugMessage(CalKey--, 0.0f, CalWarn,
|
||||
FString::Printf(TEXT(" >> BufferTime: %.4fs"),
|
||||
LastCalibrationResult.RecommendedBufferTime));
|
||||
FString::Printf(TEXT(" >> BufferTime: %.1f ms"),
|
||||
LastCalibrationResult.RecommendedBufferTime * 1000.0f));
|
||||
GEngine->AddOnScreenDebugMessage(CalKey--, 0.0f, CalWarn,
|
||||
FString::Printf(TEXT(" >> KalmanProcessNoise: %.3f"),
|
||||
LastCalibrationResult.RecommendedKalmanProcessNoise));
|
||||
|
||||
@ -120,6 +120,8 @@ public:
|
||||
// Debug HUD state (written by const prediction functions, read by TickComponent)
|
||||
mutable float DbgPosRatio = 0.0f;
|
||||
mutable float DbgAimRatio = 0.0f;
|
||||
mutable float DbgPosRemapped = 0.0f;
|
||||
mutable float DbgAimRemapped = 0.0f;
|
||||
mutable float DbgPosConfidence = 0.0f;
|
||||
mutable float DbgAimConfidence = 0.0f;
|
||||
mutable float DbgAvgPosSpeed = 0.0f;
|
||||
@ -142,13 +144,13 @@ public:
|
||||
float DebugIMUShockDisplayTime = 3.0f;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "AntiRecoil", meta = (ToolTip = "Selects the anti-recoil compensation algorithm. Hover over each option in the dropdown for a detailed description of how it works."))
|
||||
EAntiRecoilMode AntiRecoilMode = EAntiRecoilMode::ARM_KalmanFilter;
|
||||
EAntiRecoilMode AntiRecoilMode = EAntiRecoilMode::ARM_AdaptiveExtrapolation;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "AntiRecoil", meta = (ToolTip = "Total time window (seconds) of tracker history to keep. Determines how far back in time samples are stored. Must be greater than DiscardTime. Example: 0.2s at 60fps stores ~12 samples.", ClampMin = "0.05"))
|
||||
float AntiRecoilBufferTime = 0.15f;
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "AntiRecoil", meta = (ToolTip = "Total time window (ms) of tracker history to keep. Determines how far back in time samples are stored. Must be greater than DiscardTime. Example: 200ms at 60fps stores ~12 samples.", ClampMin = "5"))
|
||||
float AntiRecoilBufferTimeMs = 300.0f;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "AntiRecoil", meta = (ToolTip = "Time window (seconds) of most recent samples to exclude as potentially contaminated by IMU recoil shock. The prediction algorithms only use samples older than this. Increase if the shock lasts longer. Safe window = BufferTime - DiscardTime.", ClampMin = "0.0"))
|
||||
float AntiRecoilDiscardTime = 0.03f;
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "AntiRecoil", meta = (ToolTip = "Time window (ms) of most recent samples to exclude as potentially contaminated by IMU recoil shock. The prediction algorithms only use samples older than this. Increase if the shock lasts longer. Safe window = BufferTime - DiscardTime.", ClampMin = "0.0"))
|
||||
float AntiRecoilDiscardTimeMs = 40.0f;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "AntiRecoil", meta = (ToolTip = "Controls how the weight curve grows across safe samples in regression modes. 1.0 = linear growth, >1.0 = recent samples weighted much more heavily (convex curve), <1.0 = more uniform weighting (concave curve), 0.0 = all samples weighted equally. Formula: weight = pow(sampleIndex+1, exponent).", EditCondition = "AntiRecoilMode == EAntiRecoilMode::ARM_WeightedRegression || AntiRecoilMode == EAntiRecoilMode::ARM_WeightedLinearRegression", ClampMin = "0.0", ClampMax = "5.0"))
|
||||
float RegressionWeightExponent = 2.0f;
|
||||
@ -160,13 +162,19 @@ public:
|
||||
float KalmanMeasurementNoise = 0.01f;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "AntiRecoil", meta = (ToolTip = "Power curve exponent for deceleration detection. Controls how aggressively slowing down reduces extrapolation. confidence = (remappedRatio)^sensitivity. 1.0 = linear (gentle). 2.0 = quadratic (aggressive). 0.5 = square root (very gentle). During steady movement, ratio is ~1 so confidence is always 1 regardless of this value.", EditCondition = "AntiRecoilMode == EAntiRecoilMode::ARM_AdaptiveExtrapolation", ClampMin = "0.1", ClampMax = "5.0"))
|
||||
float AdaptiveSensitivity = 1.0f;
|
||||
float AdaptiveSensitivity = 1.5f;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "AntiRecoil", meta = (ToolTip = "Dead zone for deceleration detection. Speed ratios (recent/avg) above this value are treated as 1.0 (no correction). Only ratios below trigger extrapolation reduction. Higher = more tolerant to natural speed fluctuations (less false positives). Lower = more sensitive to deceleration. 0.8 = ignore normal jitter, only react to real braking.", EditCondition = "AntiRecoilMode == EAntiRecoilMode::ARM_AdaptiveExtrapolation", ClampMin = "0.0", ClampMax = "0.95"))
|
||||
float AdaptiveDeadZone = 0.8f;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "AntiRecoil", meta = (ToolTip = "Minimum average speed (cm/s) required for deceleration detection. Below this threshold, speed ratios are unreliable due to noise, so confidence stays at 1.0 (full extrapolation). Prevents false deceleration detection during slow/small movements. 0 = disabled.", EditCondition = "AntiRecoilMode == EAntiRecoilMode::ARM_AdaptiveExtrapolation", ClampMin = "0.0", ClampMax = "200.0"))
|
||||
float AdaptiveMinSpeed = 30.0f;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "AntiRecoil", meta = (ToolTip = "Velocity damping during extrapolation. 0 = disabled (default). Higher values cause extrapolated velocity to decay exponentially toward zero over the discard window. Reduces overshoot on fast draw-aim-fire sequences where the user stops moving before firing. Applies to all prediction modes except Buffer. Typical range: 5-15.", ClampMin = "0.0", ClampMax = "50.0"))
|
||||
float ExtrapolationDamping = 0.0f;
|
||||
float ExtrapolationDamping = 8.0f;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "AimStabilization", meta = (ToolTip = "Angular dead zone in degrees. If the aim direction changes by less than this angle since the last stable aim, the change is ignored (aim stays locked). Eliminates micro-jitter from VR tracker vibrations. 0 = disabled. Typical: 0.1 to 0.5 degrees.", ClampMin = "0.0", ClampMax = "5.0"))
|
||||
float AimDeadZoneDegrees = 0.0f;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Velocity", meta = (ToolTip = "Bullet inherits barrel velocity, only works with physics enabled or with additional velocity set")) float InheritVelocity = 1.0f;
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Velocity", meta = (ToolTip = "Amount of recoil applied to the barrel, only works with physics enabled")) float RecoilMultiplier = 1.0f;
|
||||
@ -296,6 +304,10 @@ private:
|
||||
FVector Aim;
|
||||
FVector Location;
|
||||
|
||||
// Aim stabilization: last accepted aim direction (outside dead zone)
|
||||
FVector StabilizedAim = FVector::ForwardVector;
|
||||
bool bStabilizedAimInitialized = false;
|
||||
|
||||
TArray<FTimestampedTransform> TransformHistory;
|
||||
|
||||
// Debug IMU shock simulation state
|
||||
@ -336,6 +348,7 @@ private:
|
||||
|
||||
void UpdateTransformHistory();
|
||||
void ComputeAntiRecoilTransform();
|
||||
void ApplyAimStabilization();
|
||||
void PredictLinearExtrapolation(double CurrentTime, FVector& OutLocation, FVector& OutAim) const;
|
||||
void PredictWeightedLinearRegression(double CurrentTime, FVector& OutLocation, FVector& OutAim) const;
|
||||
void PredictWeightedRegression(double CurrentTime, FVector& OutLocation, FVector& OutAim) const;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user