diff --git a/Source/Blaster/Components/LagCompensationComponent.cpp b/Source/Blaster/Components/LagCompensationComponent.cpp index 33c7708..1fe8cbf 100644 --- a/Source/Blaster/Components/LagCompensationComponent.cpp +++ b/Source/Blaster/Components/LagCompensationComponent.cpp @@ -59,6 +59,119 @@ FFramePackage ULagCompensationComponent::InterpBetweenFrames(const FFramePackage return InterpFramePackage; } +FServerSideRewindResult ULagCompensationComponent::ConfirmHit(const FFramePackage& Package, ABlasterCharacter* HitCharacter, + const FVector_NetQuantize& TraceStart, const FVector_NetQuantize HitLocation) +{ + if (HitCharacter == nullptr) return FServerSideRewindResult(); + + FFramePackage CurrentFrame; + CacheBoxPositions(HitCharacter, CurrentFrame); + MoveBoxes(HitCharacter, Package); + EnableCharacterMeshCollision(HitCharacter, ECollisionEnabled::NoCollision); + + // Enable collision for the head first + UBoxComponent* HeadBox = HitCharacter->HitCollisionBoxes[FName("head")]; + HeadBox->SetCollisionEnabled(ECollisionEnabled::QueryOnly); + HeadBox->SetCollisionResponseToChannel(ECC_Visibility, ECR_Block); + + FHitResult ConfirmHitResult; + const FVector TraceEnd = TraceStart + (HitLocation - TraceStart) * 1.25f; + UWorld* World = GetWorld(); + if (World) + { + World->LineTraceSingleByChannel( + ConfirmHitResult, + TraceStart, + TraceEnd, + ECC_Visibility + ); + + if (ConfirmHitResult.bBlockingHit) // we hit the head, return early + { + ResetHitBoxes(HitCharacter, CurrentFrame); + EnableCharacterMeshCollision(HitCharacter, ECollisionEnabled::QueryAndPhysics); + return FServerSideRewindResult{ true, true }; + } + + // Didn't hit the head, check the rest of the boxes + for (auto& HitBoxPair : HitCharacter->HitCollisionBoxes) + { + if (HitBoxPair.Value != nullptr) + { + HitBoxPair.Value->SetCollisionEnabled(ECollisionEnabled::QueryOnly); + HitBoxPair.Value->SetCollisionResponseToChannel(ECC_Visibility, ECR_Block); + } + } + + World->LineTraceSingleByChannel( + ConfirmHitResult, + TraceStart, + TraceEnd, + ECC_Visibility + ); + + if (ConfirmHitResult.bBlockingHit) + { + ResetHitBoxes(HitCharacter, CurrentFrame); + EnableCharacterMeshCollision(HitCharacter, ECollisionEnabled::QueryAndPhysics); + return FServerSideRewindResult{ true, false }; + } + + ResetHitBoxes(HitCharacter, CurrentFrame); + EnableCharacterMeshCollision(HitCharacter, ECollisionEnabled::QueryAndPhysics); + return FServerSideRewindResult{ false, false }; + } +} + +void ULagCompensationComponent::CacheBoxPositions(ABlasterCharacter* HitCharacter, FFramePackage& OutFramePackage) +{ + if (HitCharacter == nullptr) return; + + for (auto& HitBoxPair : HitCharacter->HitCollisionBoxes) + { + if (HitBoxPair.Value != nullptr) + { + FBoxInformation BoxInfo; + BoxInfo.Location = HitBoxPair.Value->GetComponentLocation(); + BoxInfo.Rotation = HitBoxPair.Value->GetComponentRotation(); + BoxInfo.BoxExtend = HitBoxPair.Value->GetScaledBoxExtent(); + + OutFramePackage.HitBoxInfo.Add(HitBoxPair.Key, BoxInfo); + } + } +} + +void ULagCompensationComponent::MoveBoxes(ABlasterCharacter* HitCharacter, const FFramePackage& Package) +{ + if (HitCharacter == nullptr) return; + + for (auto& HitBoxPair : HitCharacter->HitCollisionBoxes) + { + if (HitBoxPair.Value != nullptr) + { + HitBoxPair.Value->SetWorldLocation(Package.HitBoxInfo[HitBoxPair.Key].Location); + HitBoxPair.Value->SetWorldRotation(Package.HitBoxInfo[HitBoxPair.Key].Rotation); + HitBoxPair.Value->SetBoxExtent(Package.HitBoxInfo[HitBoxPair.Key].BoxExtend); + } + } +} + +void ULagCompensationComponent::ResetHitBoxes(ABlasterCharacter* HitCharacter, const FFramePackage& Package) +{ + if (HitCharacter == nullptr) return; + + for (auto& HitBoxPair : HitCharacter->HitCollisionBoxes) + { + if (HitBoxPair.Value != nullptr) + { + HitBoxPair.Value->SetWorldLocation(Package.HitBoxInfo[HitBoxPair.Key].Location); + HitBoxPair.Value->SetWorldRotation(Package.HitBoxInfo[HitBoxPair.Key].Rotation); + HitBoxPair.Value->SetBoxExtent(Package.HitBoxInfo[HitBoxPair.Key].BoxExtend); + HitBoxPair.Value->SetCollisionEnabled(ECollisionEnabled::NoCollision); + } + } +} + void ULagCompensationComponent::ShowFramePackage(const FFramePackage& Package, const FColor Color) { for (auto& BoxInfo : Package.HitBoxInfo) @@ -75,7 +188,7 @@ void ULagCompensationComponent::ShowFramePackage(const FFramePackage& Package, c } } -void ULagCompensationComponent::ServerSideRewind(ABlasterCharacter* HitCharacter, const FVector_NetQuantize& TraceStart, const FVector_NetQuantize& HitLocation, +FServerSideRewindResult ULagCompensationComponent::ServerSideRewind(ABlasterCharacter* HitCharacter, const FVector_NetQuantize& TraceStart, const FVector_NetQuantize& HitLocation, float HitTime) { bool bReturn = HitCharacter == nullptr || @@ -83,10 +196,10 @@ void ULagCompensationComponent::ServerSideRewind(ABlasterCharacter* HitCharacter HitCharacter->GetLagCompensation()->FrameHistory.GetHead() == nullptr || HitCharacter->GetLagCompensation()->FrameHistory.GetTail() == nullptr; - if (bReturn) return; + if (bReturn) FServerSideRewindResult(); // Frame package that we check to verify a hit - FFramePackage FrameToSheck; + FFramePackage FrameToCheck; bool bShouldInterpolate = true; const TDoubleLinkedList& History = HitCharacter->GetLagCompensation()->FrameHistory; @@ -95,16 +208,16 @@ void ULagCompensationComponent::ServerSideRewind(ABlasterCharacter* HitCharacter if (OldestHistoryTime > HitTime) { // Too far back, too laggy to do SSR - return; + FServerSideRewindResult(); } if (OldestHistoryTime == HitTime) { - FrameToSheck = History.GetTail()->GetValue(); + FrameToCheck = History.GetTail()->GetValue(); bShouldInterpolate = false; } if (NewestHistoryTime <= HitTime) { - FrameToSheck = History.GetHead()->GetValue(); + FrameToCheck = History.GetHead()->GetValue(); bShouldInterpolate = false; } auto Younger = History.GetHead(); @@ -120,13 +233,24 @@ void ULagCompensationComponent::ServerSideRewind(ABlasterCharacter* HitCharacter } if (Older->GetValue().Time == HitTime) { - FrameToSheck = Older->GetValue(); + FrameToCheck = Older->GetValue(); bShouldInterpolate = false; } if (bShouldInterpolate) // Why not just check if FrameToCheck == nullptr? { // Interpolate between Younger and Older + FrameToCheck = InterpBetweenFrames(Older->GetValue(), Younger->GetValue(), HitTime); + } + + return ConfirmHit(FrameToCheck, HitCharacter, TraceStart, HitLocation); +} + +void ULagCompensationComponent::EnableCharacterMeshCollision(ABlasterCharacter* HitCharacter, ECollisionEnabled::Type CollisionEnabled) +{ + if (HitCharacter && HitCharacter->GetMesh()) + { + HitCharacter->GetMesh()->SetCollisionEnabled(CollisionEnabled); } } diff --git a/Source/Blaster/Components/LagCompensationComponent.h b/Source/Blaster/Components/LagCompensationComponent.h index 3cc5408..e47b753 100644 --- a/Source/Blaster/Components/LagCompensationComponent.h +++ b/Source/Blaster/Components/LagCompensationComponent.h @@ -33,6 +33,18 @@ struct FFramePackage TMap HitBoxInfo; }; +USTRUCT(BlueprintType) +struct FServerSideRewindResult +{ + GENERATED_BODY() + + UPROPERTY() + bool bHitConfirmed; + + UPROPERTY() + bool bHeadShot; +}; + UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) ) class BLASTER_API ULagCompensationComponent : public UActorComponent { @@ -43,13 +55,17 @@ public: friend ABlasterCharacter; virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; void ShowFramePackage(const FFramePackage& Package, const FColor Color); - void ServerSideRewind(class ABlasterCharacter* HitCharacter, const FVector_NetQuantize& TraceStart, const FVector_NetQuantize& HitLocation, float HitTime); + FServerSideRewindResult ServerSideRewind(class ABlasterCharacter* HitCharacter, const FVector_NetQuantize& TraceStart, const FVector_NetQuantize& HitLocation, float HitTime); protected: virtual void BeginPlay() override; void SaveFramePackage(FFramePackage& Package); FFramePackage InterpBetweenFrames(const FFramePackage& OlderFrame, const FFramePackage& YoungerFrame, float HitTime); - + FServerSideRewindResult ConfirmHit(const FFramePackage& Package, ABlasterCharacter* HitCharacter, const FVector_NetQuantize& TraceStart, const FVector_NetQuantize HitLocation); + void CacheBoxPositions(ABlasterCharacter* HitCharacter, FFramePackage& OutFramePackage); + void MoveBoxes(ABlasterCharacter* HitCharacter, const FFramePackage& Package); + void ResetHitBoxes(ABlasterCharacter* HitCharacter, const FFramePackage& Package); + void EnableCharacterMeshCollision(ABlasterCharacter* HitCharacter, ECollisionEnabled::Type CollisionEnabled); private: UPROPERTY()