From 73ed206bc17ccc0a33e01d58cb669bcb4a383077 Mon Sep 17 00:00:00 2001 From: Ehret <jw210150@WIN.RZ.RWTH-AACHEN.DE> Date: Wed, 26 Jan 2022 14:04:39 +0100 Subject: [PATCH 1/8] use capsule traces instead of line traces and a lot of restructuring --- Source/RWTHVRToolkit/Public/Pawn/VRPawnMovement.h | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Source/RWTHVRToolkit/Public/Pawn/VRPawnMovement.h b/Source/RWTHVRToolkit/Public/Pawn/VRPawnMovement.h index 298799e5..43aeb782 100644 --- a/Source/RWTHVRToolkit/Public/Pawn/VRPawnMovement.h +++ b/Source/RWTHVRToolkit/Public/Pawn/VRPawnMovement.h @@ -47,7 +47,7 @@ public: float MaxStepHeight = 40.0f; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement") - float GravityAcceleration = 981.0f; + float GravityAcceleration = -981.0f; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement") float UpSteppingAcceleration = 500.0f; @@ -56,14 +56,13 @@ public: float CapsuleRadius = 40.0f; private: - FHitResult CreateLineTrace(FVector Direction, const FVector Start, bool Visibility); - FHitResult CreateMultiLineTrace(FVector Direction, const FVector Start, float Radius, bool Visibility); + //check for + FHitResult CreateCapsuleTrace(const FVector Start, FVector End, bool DrawDebug) const; void SetCapsuleColliderToUserSize(); void CheckForPhysWalkingCollision(); - bool CheckForVirtualMovCollision(FVector PositionChange, float DeltaTime); + bool CheckForVirtualSteerCollision(FVector PositionChange, float DeltaTime); void MoveByGravityOrStepUp(float DeltaSeconds); - void ShiftVertically(float DiffernceDistance, float VerticalAcceleration, float DeltaSeconds, int Direction); - //(direction = Down = -1), (direction = Up = 1) + void ShiftVertically(float Distance, float VerticalAcceleration, float DeltaSeconds); UPROPERTY(VisibleAnywhere) UCapsuleComponent* CapsuleColliderComponent = nullptr; UPROPERTY() USceneComponent* HeadComponent = nullptr; -- GitLab From 19efafceb3634f8de1544ffde2d5d745482100b5 Mon Sep 17 00:00:00 2001 From: Ehret <jw210150@WIN.RZ.RWTH-AACHEN.DE> Date: Wed, 26 Jan 2022 14:04:58 +0100 Subject: [PATCH 2/8] also the cpp file for previous commit --- .../Private/Pawn/VRPawnMovement.cpp | 170 +++++++++--------- 1 file changed, 82 insertions(+), 88 deletions(-) diff --git a/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp b/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp index 8c9df9f7..ca8cdb48 100644 --- a/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp +++ b/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp @@ -1,6 +1,7 @@  #include "Pawn/VRPawnMovement.h" #include "DrawDebugHelpers.h" +#include "Kismet/KismetSystemLibrary.h" UVRPawnMovement::UVRPawnMovement(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { @@ -19,6 +20,9 @@ void UVRPawnMovement::TickComponent(float DeltaTime, enum ELevelTick TickType, F if (NavigationMode == EVRNavigationModes::NAV_WALK) { + // you are only allowed to move horizontally in NAV_WALK + // everything else will be handled by stepping-up/gravity + // so remove Z component for the input vector of the UFloatingPawnMovement PositionChange.Z = 0.0f; ConsumeInputVector(); AddInputVector(PositionChange); @@ -26,17 +30,22 @@ void UVRPawnMovement::TickComponent(float DeltaTime, enum ELevelTick TickType, F if(NavigationMode == EVRNavigationModes::NAV_FLY || NavigationMode == EVRNavigationModes::NAV_WALK) { - MoveByGravityOrStepUp(DeltaTime); CheckForPhysWalkingCollision(); - - if(CheckForVirtualMovCollision(PositionChange, DeltaTime)) + if(CheckForVirtualSteerCollision(PositionChange, DeltaTime)) { + // if we would move into something if we apply this input (estimating distance by max speed) + // we consume the input so it is not applied ConsumeInputVector(); } + + // so we add stepping-up (for both walk and fly) + // and gravity for walking only + MoveByGravityOrStepUp(DeltaTime); } if(NavigationMode == EVRNavigationModes::NAV_NONE) { + //just remove whatever input is there ConsumeInputVector(); } @@ -45,18 +54,6 @@ void UVRPawnMovement::TickComponent(float DeltaTime, enum ELevelTick TickType, F LastHeadPosition = HeadComponent->GetComponentLocation(); } -bool UVRPawnMovement::CheckForVirtualMovCollision(FVector PositionChange, float DeltaTime) -{ - FVector ProbePosition = PositionChange.GetSafeNormal() * GetMaxSpeed() * DeltaTime; - FHitResult FHitResultVR; - CapsuleColliderComponent->AddWorldOffset(ProbePosition, true, &FHitResultVR); - if (FVector::Distance(FHitResultVR.Location, CapsuleColliderComponent->GetComponentLocation()) < CapsuleColliderComponent->GetScaledCapsuleRadius()) - { - return true; - } - return false; -} - void UVRPawnMovement::SetHeadComponent(USceneComponent* NewHeadComponent) { HeadComponent = NewHeadComponent; @@ -68,12 +65,30 @@ void UVRPawnMovement::SetHeadComponent(USceneComponent* NewHeadComponent) void UVRPawnMovement::SetCapsuleColliderToUserSize() { - float CharachterSize = abs(UpdatedComponent->GetComponentLocation().Z - HeadComponent->GetComponentLocation().Z); - if (CharachterSize > MaxStepHeight) + // the collider should be placed + // between head and floor + MaxStepHeight + // head + // / \ + // / \ + // | | + // | | + // |collider| + // | | + // | | + // \ / + // \ __ / + // | + // MaxStepHeight + // | + // floor: ______________ + + const float UserSize = HeadComponent->GetComponentLocation().Z - UpdatedComponent->GetComponentLocation().Z; + + if (UserSize > MaxStepHeight) { - float ColliderHeight = CharachterSize - MaxStepHeight; - float ColliderHalfHeight = ColliderHeight / 2.0f; + const float ColliderHeight = UserSize - MaxStepHeight; + const float ColliderHalfHeight = ColliderHeight / 2.0f; if (ColliderHalfHeight <= CapsuleRadius) {//Make the collider to a Sphere CapsuleColliderComponent->SetCapsuleSize(ColliderHalfHeight, ColliderHalfHeight); @@ -85,109 +100,88 @@ void UVRPawnMovement::SetCapsuleColliderToUserSize() CapsuleColliderComponent->SetWorldLocation(HeadComponent->GetComponentLocation()); CapsuleColliderComponent->AddWorldOffset(FVector(0, 0, -ColliderHalfHeight)); - CapsuleColliderComponent->SetWorldRotation(FRotator(0, 0, 1)); } else { CapsuleColliderComponent->SetWorldLocation(HeadComponent->GetComponentLocation()); - CapsuleColliderComponent->SetWorldRotation(FRotator(0, 0, 1)); } + CapsuleColliderComponent->SetWorldRotation(FRotator::ZeroRotator); } void UVRPawnMovement::CheckForPhysWalkingCollision() { - FVector CurrentHeadPosition = HeadComponent->GetComponentLocation(); - FVector Direction = CurrentHeadPosition - LastHeadPosition; - FHitResult FHitResultPhys; - CapsuleColliderComponent->AddWorldOffset(Direction, true, &FHitResultPhys); + const FHitResult HitResult = CreateCapsuleTrace(LastHeadPosition, HeadComponent->GetComponentLocation(), false); + + //if this was not possible move it the entire pawn to avoid the head collision + if (HitResult.bBlockingHit) + { + UpdatedComponent->AddLocalOffset(HitResult.Normal*HitResult.PenetrationDepth); + } +} - if (FHitResultPhys.bBlockingHit) +bool UVRPawnMovement::CheckForVirtualSteerCollision(FVector PositionChange, float DeltaTime) +{ + FVector ProbePosition = PositionChange.GetSafeNormal() * GetMaxSpeed() * DeltaTime; + const FHitResult HitResult = CreateCapsuleTrace(HeadComponent->GetComponentLocation(), ProbePosition, false); + if (HitResult.bBlockingHit) { - UpdatedComponent->AddLocalOffset(FHitResultPhys.Normal*FHitResultPhys.PenetrationDepth); + return true; } + return false; } void UVRPawnMovement::MoveByGravityOrStepUp(float DeltaSeconds) { - FVector StartLineTraceUnderCollider = CapsuleColliderComponent->GetComponentLocation(); - StartLineTraceUnderCollider.Z -= CapsuleColliderComponent->GetScaledCapsuleHalfHeight(); - FHitResult HitDetailsMultiLineTrace = CreateMultiLineTrace(FVector(0, 0, -1), StartLineTraceUnderCollider, CapsuleColliderComponent->GetScaledCapsuleRadius() / 4.0f, false); - float DistanceDifference = abs(MaxStepHeight - HitDetailsMultiLineTrace.Distance); + const FVector DownTraceStart = CapsuleColliderComponent->GetComponentLocation(); + const float DownTraceDist = 1000.0f; + const FVector DownTraceDir = FVector(0,0,-1); + const FVector DownTraceEnd = DownTraceStart + DownTraceDist * DownTraceDir; + + const FHitResult DownTraceHitResult = CreateCapsuleTrace(DownTraceStart, DownTraceEnd, true); + float HeightDifference = 0.0f; + + if(DownTraceHitResult.bBlockingHit) + { + HeightDifference = DownTraceHitResult.ImpactPoint.Z - UpdatedComponent->GetComponentLocation().Z; + //so for HeightDifference>0, we have to move the pawn up; for HeightDifference<0 we have to move it down + } //Going up (in Fly and Walk Mode) - if ((HitDetailsMultiLineTrace.bBlockingHit && HitDetailsMultiLineTrace.Distance < MaxStepHeight)) + if (HeightDifference>0.0f && HeightDifference<=MaxStepHeight) + { + ShiftVertically(HeightDifference, UpSteppingAcceleration, DeltaSeconds); + } + + if(NavigationMode!=EVRNavigationModes::NAV_WALK) { - ShiftVertically(DistanceDifference, UpSteppingAcceleration, DeltaSeconds, 1); + return; } + //Gravity (only in Walk Mode) - else if (NavigationMode==EVRNavigationModes::NAV_WALK && ((HitDetailsMultiLineTrace.bBlockingHit && HitDetailsMultiLineTrace.Distance > MaxStepHeight) || (HitDetailsMultiLineTrace.Actor == nullptr && HitDetailsMultiLineTrace.Distance != -1.0f))) + if (!DownTraceHitResult.bBlockingHit || HeightDifference<0.0f) { - ShiftVertically(DistanceDifference, GravityAcceleration, DeltaSeconds, -1); + ShiftVertically(HeightDifference, GravityAcceleration, DeltaSeconds); } } -void UVRPawnMovement::ShiftVertically(float DiffernceDistance, float VerticalAcceleration, float DeltaSeconds, int Direction) +void UVRPawnMovement::ShiftVertically(float Distance, float VerticalAcceleration, float DeltaSeconds) { VerticalSpeed += VerticalAcceleration * DeltaSeconds; - if (VerticalSpeed*DeltaSeconds < DiffernceDistance) + if (abs(VerticalSpeed*DeltaSeconds) < abs(Distance)) { - UpdatedComponent->AddLocalOffset(FVector(0.f, 0.f, Direction * VerticalSpeed * DeltaSeconds)); + UpdatedComponent->AddLocalOffset(FVector(0.f, 0.f, VerticalSpeed * DeltaSeconds)); } else { - UpdatedComponent->AddLocalOffset(FVector(0.f, 0.f, Direction * DiffernceDistance)); + UpdatedComponent->AddLocalOffset(FVector(0.f, 0.f, Distance)); VerticalSpeed = 0; } } -FHitResult UVRPawnMovement::CreateLineTrace(FVector Direction, const FVector Start, bool Visibility) +FHitResult UVRPawnMovement::CreateCapsuleTrace(const FVector Start, FVector End, bool DrawDebug) const { - //Re-initialize hit info - FHitResult HitDetails = FHitResult(ForceInit); - - FVector End = ((Direction * 1000.f) + Start); - // additional trace parameters - FCollisionQueryParams TraceParams(FName(TEXT("InteractTrace")), true, NULL); - TraceParams.bTraceComplex = true; //to use complex collision on whatever we interact with to provide better precision. - TraceParams.bReturnPhysicalMaterial = true; //to provide details about the physical material, if one exists on the thing we hit, to come back in our hit result. - - if (Visibility) - DrawDebugLine(GetWorld(), Start, End, FColor::Green, false, 1, 0, 1); + const EDrawDebugTrace::Type DrawType = DrawDebug ? EDrawDebugTrace::Type::ForDuration : EDrawDebugTrace::Type::None; - if (GetWorld()->LineTraceSingleByChannel(HitDetails, Start, End, ECC_Visibility, TraceParams)) - { - if (HitDetails.bBlockingHit) - { - } - } - return HitDetails; + FHitResult Hit; + UKismetSystemLibrary::CapsuleTraceSingle(GetWorld(), Start, End, CapsuleColliderComponent->GetScaledCapsuleRadius(), CapsuleColliderComponent->GetScaledCapsuleHalfHeight() ,UEngineTypes::ConvertToTraceType(ECollisionChannel::ECC_Visibility), true, {}, DrawType, Hit, true); + return Hit; } - -FHitResult UVRPawnMovement::CreateMultiLineTrace(FVector Direction, const FVector Start, float Radius, bool Visibility) -{ - TArray<FVector> StartVectors; - TArray<FHitResult> OutHits; - FHitResult HitDetailsMultiLineTrace; - HitDetailsMultiLineTrace.Distance = -1.0f;//(Distance=-1) not existing, but to know if this Variable not Initialized(when all Traces not compatible) - - StartVectors.Add(Start); //LineTraceCenter - StartVectors.Add(Start + FVector(0, -Radius, 0)); //LineTraceLeft - StartVectors.Add(Start + FVector(0, +Radius, 0)); //LineTraceRight - StartVectors.Add(Start + FVector(+Radius, 0, 0)); //LineTraceFront - StartVectors.Add(Start + FVector(-Radius, 0, 0)); //LineTraceBehind - - bool IsBlockingHitAndSameActor = true; - bool IsAllNothingHiting = true; - // loop through TArray - for (FVector& Vector : StartVectors) - { - FHitResult OutHit = CreateLineTrace(Direction, Vector, Visibility); - OutHits.Add(OutHit); - IsBlockingHitAndSameActor &= (OutHit.Actor == OutHits[0].Actor); //If all Hiting the same Object, then you are (going up/down) or (walking) - IsAllNothingHiting &= (OutHit.Actor == nullptr); //If all Hiting nothing, then you are falling - } - - if (IsBlockingHitAndSameActor || IsAllNothingHiting) - HitDetailsMultiLineTrace = OutHits[0]; - - return HitDetailsMultiLineTrace; -} \ No newline at end of file -- GitLab From d45251bb8870e55b31ed283a1ca2e49c9f66f273 Mon Sep 17 00:00:00 2001 From: Ehret <jw210150@WIN.RZ.RWTH-AACHEN.DE> Date: Wed, 26 Jan 2022 18:07:30 +0100 Subject: [PATCH 3/8] bugfixes for desktop mode --- .../Private/Pawn/VRPawnMovement.cpp | 136 ++++++++++++++---- .../Public/Pawn/VRPawnMovement.h | 24 +++- 2 files changed, 123 insertions(+), 37 deletions(-) diff --git a/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp b/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp index ca8cdb48..76a16740 100644 --- a/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp +++ b/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp @@ -10,37 +10,56 @@ UVRPawnMovement::UVRPawnMovement(const FObjectInitializer& ObjectInitializer) : CapsuleColliderComponent->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Overlap); CapsuleColliderComponent->SetCollisionResponseToChannel(ECollisionChannel::ECC_WorldStatic, ECollisionResponse::ECR_Block); CapsuleColliderComponent->SetCapsuleSize(CapsuleRadius, 80.0f); + + //set some defaults for the UFloatingPawnMovement component, which are more reasonable for usage in VR + MaxSpeed = 300.f; + Acceleration = 800.f; + Deceleration = 2000.f; +} + +void UVRPawnMovement::BeginPlay() +{ + Super::BeginPlay(); + LastCapsulePosition=FVector(0,0,0); + LastSteeringCollisionVector = FVector(0,0,0); } + void UVRPawnMovement::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction){ SetCapsuleColliderToUserSize(); - FVector PositionChange = GetPendingInputVector(); + FVector InputVector = GetPendingInputVector(); if (NavigationMode == EVRNavigationModes::NAV_WALK) { // you are only allowed to move horizontally in NAV_WALK // everything else will be handled by stepping-up/gravity // so remove Z component for the input vector of the UFloatingPawnMovement - PositionChange.Z = 0.0f; + InputVector.Z = 0.0f; ConsumeInputVector(); - AddInputVector(PositionChange); + AddInputVector(InputVector); } if(NavigationMode == EVRNavigationModes::NAV_FLY || NavigationMode == EVRNavigationModes::NAV_WALK) { - CheckForPhysWalkingCollision(); - if(CheckForVirtualSteerCollision(PositionChange, DeltaTime)) - { - // if we would move into something if we apply this input (estimating distance by max speed) - // we consume the input so it is not applied - ConsumeInputVector(); + if(InputVector.Size()>0.001){ + const FVector SafeSteeringInput = GetCollisionSafeVirtualSteeringVec(InputVector, DeltaTime); + if(SafeSteeringInput != InputVector) + { + // if we would move into something if we apply this input (estimating distance by max speed) + // we only apply its perpendicular part (unless it is facing away from the collision) + ConsumeInputVector(); + AddInputVector(SafeSteeringInput); + } } // so we add stepping-up (for both walk and fly) // and gravity for walking only MoveByGravityOrStepUp(DeltaTime); + + //if we physically (in the tracking space) walked into something, move the world away (by moving the pawn) + CheckForPhysWalkingCollision(); } if(NavigationMode == EVRNavigationModes::NAV_NONE) @@ -50,8 +69,6 @@ void UVRPawnMovement::TickComponent(float DeltaTime, enum ELevelTick TickType, F } Super::TickComponent(DeltaTime, TickType, ThisTickFunction); - - LastHeadPosition = HeadComponent->GetComponentLocation(); } void UVRPawnMovement::SetHeadComponent(USceneComponent* NewHeadComponent) @@ -90,54 +107,98 @@ void UVRPawnMovement::SetCapsuleColliderToUserSize() const float ColliderHeight = UserSize - MaxStepHeight; const float ColliderHalfHeight = ColliderHeight / 2.0f; if (ColliderHalfHeight <= CapsuleRadius) - {//Make the collider to a Sphere + { + //the capsule will actually be compressed to a sphere CapsuleColliderComponent->SetCapsuleSize(ColliderHalfHeight, ColliderHalfHeight); } else - {//Make the collider to a Capsule + { CapsuleColliderComponent->SetCapsuleSize(CapsuleRadius, ColliderHalfHeight); } - CapsuleColliderComponent->SetWorldLocation(HeadComponent->GetComponentLocation()); - CapsuleColliderComponent->AddWorldOffset(FVector(0, 0, -ColliderHalfHeight)); + CapsuleColliderComponent->SetWorldLocation(HeadComponent->GetComponentLocation() - FVector(0, 0, ColliderHalfHeight)); } else { CapsuleColliderComponent->SetWorldLocation(HeadComponent->GetComponentLocation()); } + CapsuleColliderComponent->SetWorldRotation(FRotator::ZeroRotator); } void UVRPawnMovement::CheckForPhysWalkingCollision() { - const FHitResult HitResult = CreateCapsuleTrace(LastHeadPosition, HeadComponent->GetComponentLocation(), false); + if(LastCapsulePosition.IsNearlyZero()) + { + //probably not yet set, so do nothing than setting it + LastCapsulePosition = GetCapsuleLocationFromHead(); + return; + } - //if this was not possible move it the entire pawn to avoid the head collision + const FVector CapsuleLocation = GetCapsuleLocationFromHead(); + const FHitResult HitResult = CreateCapsuleTrace(LastCapsulePosition, CapsuleLocation); + + //if this was not possible move the entire pawn away to avoid the head collision if (HitResult.bBlockingHit) { - UpdatedComponent->AddLocalOffset(HitResult.Normal*HitResult.PenetrationDepth); + const FVector MoveOutVector = HitResult.Location-CapsuleLocation; + UpdatedComponent->AddWorldOffset(MoveOutVector); + } + else + { + //only update if not in collision + if(!CreateCapsuleTrace(CapsuleLocation, CapsuleLocation).bBlockingHit) + { + LastCapsulePosition = CapsuleLocation; + } } } -bool UVRPawnMovement::CheckForVirtualSteerCollision(FVector PositionChange, float DeltaTime) +FVector UVRPawnMovement::GetCollisionSafeVirtualSteeringVec(FVector InputVector, float DeltaTime) { - FVector ProbePosition = PositionChange.GetSafeNormal() * GetMaxSpeed() * DeltaTime; - const FHitResult HitResult = CreateCapsuleTrace(HeadComponent->GetComponentLocation(), ProbePosition, false); - if (HitResult.bBlockingHit) + + const float SafetyFactor = 3.0f; //so we detect collision a bit earlier + const FVector CapsuleLocation = GetCapsuleLocationFromHead(); + FVector ProbePosition = SafetyFactor * InputVector.GetSafeNormal() * GetMaxSpeed() * DeltaTime + CapsuleLocation; + const FHitResult TraceResult = CreateCapsuleTrace(CapsuleLocation, ProbePosition); + if (!TraceResult.bBlockingHit) { - return true; + //everything is fine, use that vector + return InputVector; } - return false; + + //otherwise remove the component of that vector that goes towards the collision + FVector CollisionVector = TraceResult.Location - CapsuleLocation; + + //sometimes (if by chance we already moved into collision entirely CollisionVector is 0 + if(! CollisionVector.Normalize()) + { + //then we probably start already in collision, so we use the last one + CollisionVector = LastSteeringCollisionVector; + } + else + { + LastSteeringCollisionVector = CollisionVector; + } + + FVector SafeInput = InputVector; + const float DotProduct = FVector::DotProduct(InputVector, CollisionVector); + if(DotProduct>0.0f) + { + // only keep perpendicular part of the input vector (remove anything towards hit) + SafeInput -= DotProduct * CollisionVector; + } + return SafeInput; } void UVRPawnMovement::MoveByGravityOrStepUp(float DeltaSeconds) { const FVector DownTraceStart = CapsuleColliderComponent->GetComponentLocation(); - const float DownTraceDist = 1000.0f; + const float DownTraceDist = MaxFallingDepth < 0.0f ? 1000.0f : MaxFallingDepth; const FVector DownTraceDir = FVector(0,0,-1); const FVector DownTraceEnd = DownTraceStart + DownTraceDist * DownTraceDir; - const FHitResult DownTraceHitResult = CreateCapsuleTrace(DownTraceStart, DownTraceEnd, true); + const FHitResult DownTraceHitResult = CreateCapsuleTrace(DownTraceStart, DownTraceEnd); float HeightDifference = 0.0f; if(DownTraceHitResult.bBlockingHit) @@ -145,6 +206,7 @@ void UVRPawnMovement::MoveByGravityOrStepUp(float DeltaSeconds) HeightDifference = DownTraceHitResult.ImpactPoint.Z - UpdatedComponent->GetComponentLocation().Z; //so for HeightDifference>0, we have to move the pawn up; for HeightDifference<0 we have to move it down } + //Going up (in Fly and Walk Mode) if (HeightDifference>0.0f && HeightDifference<=MaxStepHeight) { @@ -156,8 +218,13 @@ void UVRPawnMovement::MoveByGravityOrStepUp(float DeltaSeconds) return; } + if(!DownTraceHitResult.bBlockingHit && MaxFallingDepth<0.0f) + { + HeightDifference = -1000.0f; //just fall + } + //Gravity (only in Walk Mode) - if (!DownTraceHitResult.bBlockingHit || HeightDifference<0.0f) + if (HeightDifference<0.0f) { ShiftVertically(HeightDifference, GravityAcceleration, DeltaSeconds); } @@ -168,20 +235,29 @@ void UVRPawnMovement::ShiftVertically(float Distance, float VerticalAcceleration VerticalSpeed += VerticalAcceleration * DeltaSeconds; if (abs(VerticalSpeed*DeltaSeconds) < abs(Distance)) { - UpdatedComponent->AddLocalOffset(FVector(0.f, 0.f, VerticalSpeed * DeltaSeconds)); + UpdatedComponent->AddWorldOffset(FVector(0.f, 0.f, VerticalSpeed * DeltaSeconds)); } else { - UpdatedComponent->AddLocalOffset(FVector(0.f, 0.f, Distance)); + UpdatedComponent->AddWorldOffset(FVector(0.f, 0.f, Distance)); VerticalSpeed = 0; } } +FVector UVRPawnMovement::GetCapsuleLocationFromHead() +{ + return HeadComponent->GetComponentLocation() - FVector(0, 0, CapsuleColliderComponent->GetScaledCapsuleHalfHeight()); +} + FHitResult UVRPawnMovement::CreateCapsuleTrace(const FVector Start, FVector End, bool DrawDebug) const { const EDrawDebugTrace::Type DrawType = DrawDebug ? EDrawDebugTrace::Type::ForDuration : EDrawDebugTrace::Type::None; + TArray<AActor*> ActorsToIgnore; + ActorsToIgnore.Add(GetOwner()); + + //UE_LOG(LogTemp, Warning, TEXT("Capsule from %s to %s"), *Start.ToString(), *End.ToString()) FHitResult Hit; - UKismetSystemLibrary::CapsuleTraceSingle(GetWorld(), Start, End, CapsuleColliderComponent->GetScaledCapsuleRadius(), CapsuleColliderComponent->GetScaledCapsuleHalfHeight() ,UEngineTypes::ConvertToTraceType(ECollisionChannel::ECC_Visibility), true, {}, DrawType, Hit, true); + UKismetSystemLibrary::CapsuleTraceSingle(GetWorld(), Start, End, CapsuleColliderComponent->GetScaledCapsuleRadius(), CapsuleColliderComponent->GetScaledCapsuleHalfHeight() ,UEngineTypes::ConvertToTraceType(ECollisionChannel::ECC_Visibility), true, ActorsToIgnore, DrawType, Hit, true); return Hit; } diff --git a/Source/RWTHVRToolkit/Public/Pawn/VRPawnMovement.h b/Source/RWTHVRToolkit/Public/Pawn/VRPawnMovement.h index 43aeb782..13d659bb 100644 --- a/Source/RWTHVRToolkit/Public/Pawn/VRPawnMovement.h +++ b/Source/RWTHVRToolkit/Public/Pawn/VRPawnMovement.h @@ -35,6 +35,8 @@ class RWTHVRTOOLKIT_API UVRPawnMovement : public UFloatingPawnMovement public: + virtual void BeginPlay() override; + virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; @@ -43,30 +45,38 @@ public: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement") EVRNavigationModes NavigationMode = EVRNavigationModes::NAV_WALK; - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement") + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement", meta = (ClampMin="0.0")) float MaxStepHeight = 40.0f; + // if the height that the pawn would fall (in walking mode) is higher + // it is not falling, set to <0.0f if you want to fall infinitely UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement") + float MaxFallingDepth = 1000.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement", meta = (ClampMax="0.0")) float GravityAcceleration = -981.0f; - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement") - float UpSteppingAcceleration = 500.0f; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement", meta = (ClampMin="0.0")) + float UpSteppingAcceleration = 981.0f; - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement") + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement", meta = (ClampMin="0.0")) float CapsuleRadius = 40.0f; private: //check for - FHitResult CreateCapsuleTrace(const FVector Start, FVector End, bool DrawDebug) const; + FHitResult CreateCapsuleTrace(const FVector Start, FVector End, bool DrawDebug=false) const; void SetCapsuleColliderToUserSize(); void CheckForPhysWalkingCollision(); - bool CheckForVirtualSteerCollision(FVector PositionChange, float DeltaTime); + FVector GetCollisionSafeVirtualSteeringVec(FVector InputVector, float DeltaTime); void MoveByGravityOrStepUp(float DeltaSeconds); void ShiftVertically(float Distance, float VerticalAcceleration, float DeltaSeconds); + FVector GetCapsuleLocationFromHead(); + UPROPERTY(VisibleAnywhere) UCapsuleComponent* CapsuleColliderComponent = nullptr; UPROPERTY() USceneComponent* HeadComponent = nullptr; float VerticalSpeed = 0.0f; - FVector LastHeadPosition; + FVector LastCapsulePosition; + FVector LastSteeringCollisionVector; }; -- GitLab From 240e4625eb7179dd3c77dfb6d223955fb63701e3 Mon Sep 17 00:00:00 2001 From: Ehret <jw210150@WIN.RZ.RWTH-AACHEN.DE> Date: Wed, 26 Jan 2022 21:37:47 +0100 Subject: [PATCH 4/8] minor improvements --- .../Private/Pawn/VRPawnMovement.cpp | 16 +++++++--------- .../RWTHVRToolkit/Public/Pawn/VRPawnMovement.h | 2 -- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp b/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp index 76a16740..b1791321 100644 --- a/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp +++ b/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp @@ -5,6 +5,8 @@ UVRPawnMovement::UVRPawnMovement(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { + // the capsule is used to store the players size and position, e.g., for other interactions and as starting point + // for the capsule trace (which however not use the capsule component directly) CapsuleColliderComponent = CreateDefaultSubobject<UCapsuleComponent>(TEXT("CapsuleCollider")); CapsuleColliderComponent->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics); CapsuleColliderComponent->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Overlap); @@ -131,18 +133,19 @@ void UVRPawnMovement::CheckForPhysWalkingCollision() if(LastCapsulePosition.IsNearlyZero()) { //probably not yet set, so do nothing than setting it - LastCapsulePosition = GetCapsuleLocationFromHead(); + LastCapsulePosition = CapsuleColliderComponent->GetComponentLocation(); return; } - const FVector CapsuleLocation = GetCapsuleLocationFromHead(); + const FVector CapsuleLocation = CapsuleColliderComponent->GetComponentLocation(); const FHitResult HitResult = CreateCapsuleTrace(LastCapsulePosition, CapsuleLocation); //if this was not possible move the entire pawn away to avoid the head collision if (HitResult.bBlockingHit) { const FVector MoveOutVector = HitResult.Location-CapsuleLocation; - UpdatedComponent->AddWorldOffset(MoveOutVector); + //move it out twice as far, to avoid getting stuck situations + UpdatedComponent->AddWorldOffset(2*MoveOutVector); } else { @@ -158,7 +161,7 @@ FVector UVRPawnMovement::GetCollisionSafeVirtualSteeringVec(FVector InputVector, { const float SafetyFactor = 3.0f; //so we detect collision a bit earlier - const FVector CapsuleLocation = GetCapsuleLocationFromHead(); + const FVector CapsuleLocation = CapsuleColliderComponent->GetComponentLocation(); FVector ProbePosition = SafetyFactor * InputVector.GetSafeNormal() * GetMaxSpeed() * DeltaTime + CapsuleLocation; const FHitResult TraceResult = CreateCapsuleTrace(CapsuleLocation, ProbePosition); if (!TraceResult.bBlockingHit) @@ -244,11 +247,6 @@ void UVRPawnMovement::ShiftVertically(float Distance, float VerticalAcceleration } } -FVector UVRPawnMovement::GetCapsuleLocationFromHead() -{ - return HeadComponent->GetComponentLocation() - FVector(0, 0, CapsuleColliderComponent->GetScaledCapsuleHalfHeight()); -} - FHitResult UVRPawnMovement::CreateCapsuleTrace(const FVector Start, FVector End, bool DrawDebug) const { const EDrawDebugTrace::Type DrawType = DrawDebug ? EDrawDebugTrace::Type::ForDuration : EDrawDebugTrace::Type::None; diff --git a/Source/RWTHVRToolkit/Public/Pawn/VRPawnMovement.h b/Source/RWTHVRToolkit/Public/Pawn/VRPawnMovement.h index 13d659bb..53ea5ecd 100644 --- a/Source/RWTHVRToolkit/Public/Pawn/VRPawnMovement.h +++ b/Source/RWTHVRToolkit/Public/Pawn/VRPawnMovement.h @@ -71,8 +71,6 @@ private: void MoveByGravityOrStepUp(float DeltaSeconds); void ShiftVertically(float Distance, float VerticalAcceleration, float DeltaSeconds); - FVector GetCapsuleLocationFromHead(); - UPROPERTY(VisibleAnywhere) UCapsuleComponent* CapsuleColliderComponent = nullptr; UPROPERTY() USceneComponent* HeadComponent = nullptr; -- GitLab From ba9700f42b8260b607af6c879c16013f99560a50 Mon Sep 17 00:00:00 2001 From: David Gilbert <gilbert@vr.rwth-aachen.de> Date: Mon, 21 Mar 2022 08:15:37 +0000 Subject: [PATCH 5/8] Apply improvement suggestions by dgilbert --- .../Private/Pawn/VRPawnMovement.cpp | 522 +++++++++--------- 1 file changed, 261 insertions(+), 261 deletions(-) diff --git a/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp b/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp index b1791321..59da4c3d 100644 --- a/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp +++ b/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp @@ -1,261 +1,261 @@ - -#include "Pawn/VRPawnMovement.h" -#include "DrawDebugHelpers.h" -#include "Kismet/KismetSystemLibrary.h" - -UVRPawnMovement::UVRPawnMovement(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) -{ - // the capsule is used to store the players size and position, e.g., for other interactions and as starting point - // for the capsule trace (which however not use the capsule component directly) - CapsuleColliderComponent = CreateDefaultSubobject<UCapsuleComponent>(TEXT("CapsuleCollider")); - CapsuleColliderComponent->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics); - CapsuleColliderComponent->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Overlap); - CapsuleColliderComponent->SetCollisionResponseToChannel(ECollisionChannel::ECC_WorldStatic, ECollisionResponse::ECR_Block); - CapsuleColliderComponent->SetCapsuleSize(CapsuleRadius, 80.0f); - - //set some defaults for the UFloatingPawnMovement component, which are more reasonable for usage in VR - MaxSpeed = 300.f; - Acceleration = 800.f; - Deceleration = 2000.f; -} - -void UVRPawnMovement::BeginPlay() -{ - Super::BeginPlay(); - LastCapsulePosition=FVector(0,0,0); - LastSteeringCollisionVector = FVector(0,0,0); -} - - -void UVRPawnMovement::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction){ - - SetCapsuleColliderToUserSize(); - - FVector InputVector = GetPendingInputVector(); - - if (NavigationMode == EVRNavigationModes::NAV_WALK) - { - // you are only allowed to move horizontally in NAV_WALK - // everything else will be handled by stepping-up/gravity - // so remove Z component for the input vector of the UFloatingPawnMovement - InputVector.Z = 0.0f; - ConsumeInputVector(); - AddInputVector(InputVector); - } - - if(NavigationMode == EVRNavigationModes::NAV_FLY || NavigationMode == EVRNavigationModes::NAV_WALK) - { - if(InputVector.Size()>0.001){ - const FVector SafeSteeringInput = GetCollisionSafeVirtualSteeringVec(InputVector, DeltaTime); - if(SafeSteeringInput != InputVector) - { - // if we would move into something if we apply this input (estimating distance by max speed) - // we only apply its perpendicular part (unless it is facing away from the collision) - ConsumeInputVector(); - AddInputVector(SafeSteeringInput); - } - } - - // so we add stepping-up (for both walk and fly) - // and gravity for walking only - MoveByGravityOrStepUp(DeltaTime); - - //if we physically (in the tracking space) walked into something, move the world away (by moving the pawn) - CheckForPhysWalkingCollision(); - } - - if(NavigationMode == EVRNavigationModes::NAV_NONE) - { - //just remove whatever input is there - ConsumeInputVector(); - } - - Super::TickComponent(DeltaTime, TickType, ThisTickFunction); -} - -void UVRPawnMovement::SetHeadComponent(USceneComponent* NewHeadComponent) -{ - HeadComponent = NewHeadComponent; - CapsuleColliderComponent->SetupAttachment(HeadComponent); - const float HalfHeight = 80.0f; //this is just an initial value to look good in editor - CapsuleColliderComponent->SetCapsuleSize(CapsuleRadius, HalfHeight); - CapsuleColliderComponent->SetWorldLocation(FVector(0.0f, 0.0f,HalfHeight)); -} - -void UVRPawnMovement::SetCapsuleColliderToUserSize() -{ - - // the collider should be placed - // between head and floor + MaxStepHeight - // head - // / \ - // / \ - // | | - // | | - // |collider| - // | | - // | | - // \ / - // \ __ / - // | - // MaxStepHeight - // | - // floor: ______________ - - const float UserSize = HeadComponent->GetComponentLocation().Z - UpdatedComponent->GetComponentLocation().Z; - - if (UserSize > MaxStepHeight) - { - const float ColliderHeight = UserSize - MaxStepHeight; - const float ColliderHalfHeight = ColliderHeight / 2.0f; - if (ColliderHalfHeight <= CapsuleRadius) - { - //the capsule will actually be compressed to a sphere - CapsuleColliderComponent->SetCapsuleSize(ColliderHalfHeight, ColliderHalfHeight); - } - else - { - CapsuleColliderComponent->SetCapsuleSize(CapsuleRadius, ColliderHalfHeight); - } - - CapsuleColliderComponent->SetWorldLocation(HeadComponent->GetComponentLocation() - FVector(0, 0, ColliderHalfHeight)); - } - else - { - CapsuleColliderComponent->SetWorldLocation(HeadComponent->GetComponentLocation()); - } - - CapsuleColliderComponent->SetWorldRotation(FRotator::ZeroRotator); -} - -void UVRPawnMovement::CheckForPhysWalkingCollision() -{ - if(LastCapsulePosition.IsNearlyZero()) - { - //probably not yet set, so do nothing than setting it - LastCapsulePosition = CapsuleColliderComponent->GetComponentLocation(); - return; - } - - const FVector CapsuleLocation = CapsuleColliderComponent->GetComponentLocation(); - const FHitResult HitResult = CreateCapsuleTrace(LastCapsulePosition, CapsuleLocation); - - //if this was not possible move the entire pawn away to avoid the head collision - if (HitResult.bBlockingHit) - { - const FVector MoveOutVector = HitResult.Location-CapsuleLocation; - //move it out twice as far, to avoid getting stuck situations - UpdatedComponent->AddWorldOffset(2*MoveOutVector); - } - else - { - //only update if not in collision - if(!CreateCapsuleTrace(CapsuleLocation, CapsuleLocation).bBlockingHit) - { - LastCapsulePosition = CapsuleLocation; - } - } -} - -FVector UVRPawnMovement::GetCollisionSafeVirtualSteeringVec(FVector InputVector, float DeltaTime) -{ - - const float SafetyFactor = 3.0f; //so we detect collision a bit earlier - const FVector CapsuleLocation = CapsuleColliderComponent->GetComponentLocation(); - FVector ProbePosition = SafetyFactor * InputVector.GetSafeNormal() * GetMaxSpeed() * DeltaTime + CapsuleLocation; - const FHitResult TraceResult = CreateCapsuleTrace(CapsuleLocation, ProbePosition); - if (!TraceResult.bBlockingHit) - { - //everything is fine, use that vector - return InputVector; - } - - //otherwise remove the component of that vector that goes towards the collision - FVector CollisionVector = TraceResult.Location - CapsuleLocation; - - //sometimes (if by chance we already moved into collision entirely CollisionVector is 0 - if(! CollisionVector.Normalize()) - { - //then we probably start already in collision, so we use the last one - CollisionVector = LastSteeringCollisionVector; - } - else - { - LastSteeringCollisionVector = CollisionVector; - } - - FVector SafeInput = InputVector; - const float DotProduct = FVector::DotProduct(InputVector, CollisionVector); - if(DotProduct>0.0f) - { - // only keep perpendicular part of the input vector (remove anything towards hit) - SafeInput -= DotProduct * CollisionVector; - } - return SafeInput; -} - -void UVRPawnMovement::MoveByGravityOrStepUp(float DeltaSeconds) -{ - const FVector DownTraceStart = CapsuleColliderComponent->GetComponentLocation(); - const float DownTraceDist = MaxFallingDepth < 0.0f ? 1000.0f : MaxFallingDepth; - const FVector DownTraceDir = FVector(0,0,-1); - const FVector DownTraceEnd = DownTraceStart + DownTraceDist * DownTraceDir; - - const FHitResult DownTraceHitResult = CreateCapsuleTrace(DownTraceStart, DownTraceEnd); - float HeightDifference = 0.0f; - - if(DownTraceHitResult.bBlockingHit) - { - HeightDifference = DownTraceHitResult.ImpactPoint.Z - UpdatedComponent->GetComponentLocation().Z; - //so for HeightDifference>0, we have to move the pawn up; for HeightDifference<0 we have to move it down - } - - //Going up (in Fly and Walk Mode) - if (HeightDifference>0.0f && HeightDifference<=MaxStepHeight) - { - ShiftVertically(HeightDifference, UpSteppingAcceleration, DeltaSeconds); - } - - if(NavigationMode!=EVRNavigationModes::NAV_WALK) - { - return; - } - - if(!DownTraceHitResult.bBlockingHit && MaxFallingDepth<0.0f) - { - HeightDifference = -1000.0f; //just fall - } - - //Gravity (only in Walk Mode) - if (HeightDifference<0.0f) - { - ShiftVertically(HeightDifference, GravityAcceleration, DeltaSeconds); - } -} - -void UVRPawnMovement::ShiftVertically(float Distance, float VerticalAcceleration, float DeltaSeconds) -{ - VerticalSpeed += VerticalAcceleration * DeltaSeconds; - if (abs(VerticalSpeed*DeltaSeconds) < abs(Distance)) - { - UpdatedComponent->AddWorldOffset(FVector(0.f, 0.f, VerticalSpeed * DeltaSeconds)); - } - else - { - UpdatedComponent->AddWorldOffset(FVector(0.f, 0.f, Distance)); - VerticalSpeed = 0; - } -} - -FHitResult UVRPawnMovement::CreateCapsuleTrace(const FVector Start, FVector End, bool DrawDebug) const -{ - const EDrawDebugTrace::Type DrawType = DrawDebug ? EDrawDebugTrace::Type::ForDuration : EDrawDebugTrace::Type::None; - TArray<AActor*> ActorsToIgnore; - ActorsToIgnore.Add(GetOwner()); - - //UE_LOG(LogTemp, Warning, TEXT("Capsule from %s to %s"), *Start.ToString(), *End.ToString()) - - FHitResult Hit; - UKismetSystemLibrary::CapsuleTraceSingle(GetWorld(), Start, End, CapsuleColliderComponent->GetScaledCapsuleRadius(), CapsuleColliderComponent->GetScaledCapsuleHalfHeight() ,UEngineTypes::ConvertToTraceType(ECollisionChannel::ECC_Visibility), true, ActorsToIgnore, DrawType, Hit, true); - return Hit; -} + +#include "Pawn/VRPawnMovement.h" +#include "DrawDebugHelpers.h" +#include "Kismet/KismetSystemLibrary.h" + +UVRPawnMovement::UVRPawnMovement(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) +{ + // the capsule is used to store the players size and position, e.g., for other interactions and as starting point + // for the capsule trace (which however not use the capsule component directly) + CapsuleColliderComponent = CreateDefaultSubobject<UCapsuleComponent>(TEXT("CapsuleCollider")); + CapsuleColliderComponent->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics); + CapsuleColliderComponent->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Overlap); + CapsuleColliderComponent->SetCollisionResponseToChannel(ECollisionChannel::ECC_WorldStatic, ECollisionResponse::ECR_Block); + CapsuleColliderComponent->SetCapsuleSize(CapsuleRadius, 80.0f); + + //set some defaults for the UFloatingPawnMovement component, which are more reasonable for usage in VR + MaxSpeed = 300.f; + Acceleration = 800.f; + Deceleration = 2000.f; +} + +void UVRPawnMovement::BeginPlay() +{ + Super::BeginPlay(); + LastCapsulePosition = FVector(0,0,0); + LastSteeringCollisionVector = FVector(0,0,0); +} + + +void UVRPawnMovement::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction){ + + SetCapsuleColliderToUserSize(); + + FVector InputVector = GetPendingInputVector(); + + if (NavigationMode == EVRNavigationModes::NAV_WALK) + { + // you are only allowed to move horizontally in NAV_WALK + // everything else will be handled by stepping-up/gravity + // so remove Z component for the input vector of the UFloatingPawnMovement + InputVector.Z = 0.0f; + ConsumeInputVector(); + AddInputVector(InputVector); + } + + if(NavigationMode == EVRNavigationModes::NAV_FLY || NavigationMode == EVRNavigationModes::NAV_WALK) + { + if(InputVector.Size() > 0.001){ + const FVector SafeSteeringInput = GetCollisionSafeVirtualSteeringVec(InputVector, DeltaTime); + if(SafeSteeringInput != InputVector) + { + // if we would move into something if we apply this input (estimating distance by max speed) + // we only apply its perpendicular part (unless it is facing away from the collision) + ConsumeInputVector(); + AddInputVector(SafeSteeringInput); + } + } + + // so we add stepping-up (for both walk and fly) + // and gravity for walking only + MoveByGravityOrStepUp(DeltaTime); + + //if we physically (in the tracking space) walked into something, move the world away (by moving the pawn) + CheckForPhysWalkingCollision(); + } + + if(NavigationMode == EVRNavigationModes::NAV_NONE) + { + //just remove whatever input is there + ConsumeInputVector(); + } + + Super::TickComponent(DeltaTime, TickType, ThisTickFunction); +} + +void UVRPawnMovement::SetHeadComponent(USceneComponent* NewHeadComponent) +{ + HeadComponent = NewHeadComponent; + CapsuleColliderComponent->SetupAttachment(HeadComponent); + const float HalfHeight = 80.0f; //this is just an initial value to look good in editor + CapsuleColliderComponent->SetCapsuleSize(CapsuleRadius, HalfHeight); + CapsuleColliderComponent->SetWorldLocation(FVector(0.0f, 0.0f,HalfHeight)); +} + +void UVRPawnMovement::SetCapsuleColliderToUserSize() +{ + + // the collider should be placed + // between head and floor + MaxStepHeight + // head + // / \ + // / \ + // | | + // | | + // |collider| + // | | + // | | + // \ / + // \ __ / + // | + // MaxStepHeight + // | + // floor: ______________ + + const float UserSize = HeadComponent->GetComponentLocation().Z - UpdatedComponent->GetComponentLocation().Z; + + if (UserSize > MaxStepHeight) + { + const float ColliderHeight = UserSize - MaxStepHeight; + const float ColliderHalfHeight = ColliderHeight / 2.0f; + if (ColliderHalfHeight <= CapsuleRadius) + { + //the capsule will actually be compressed to a sphere + CapsuleColliderComponent->SetCapsuleSize(ColliderHalfHeight, ColliderHalfHeight); + } + else + { + CapsuleColliderComponent->SetCapsuleSize(CapsuleRadius, ColliderHalfHeight); + } + + CapsuleColliderComponent->SetWorldLocation(HeadComponent->GetComponentLocation() - FVector(0, 0, ColliderHalfHeight)); + } + else + { + CapsuleColliderComponent->SetWorldLocation(HeadComponent->GetComponentLocation()); + } + + CapsuleColliderComponent->SetWorldRotation(FRotator::ZeroRotator); +} + +void UVRPawnMovement::CheckForPhysWalkingCollision() +{ + if(LastCapsulePosition.IsNearlyZero()) + { + //probably not yet set, so do nothing than setting it + LastCapsulePosition = CapsuleColliderComponent->GetComponentLocation(); + return; + } + + const FVector CapsuleLocation = CapsuleColliderComponent->GetComponentLocation(); + const FHitResult HitResult = CreateCapsuleTrace(LastCapsulePosition, CapsuleLocation); + + //if this was not possible move the entire pawn away to avoid the head collision + if (HitResult.bBlockingHit) + { + const FVector MoveOutVector = HitResult.Location-CapsuleLocation; + //move it out twice as far, to avoid getting stuck situations + UpdatedComponent->AddWorldOffset(2*MoveOutVector); + } + else + { + //only update if not in collision + if(!CreateCapsuleTrace(CapsuleLocation, CapsuleLocation).bBlockingHit) + { + LastCapsulePosition = CapsuleLocation; + } + } +} + +FVector UVRPawnMovement::GetCollisionSafeVirtualSteeringVec(FVector InputVector, float DeltaTime) +{ + + const float SafetyFactor = 3.0f; //so we detect collision a bit earlier + const FVector CapsuleLocation = CapsuleColliderComponent->GetComponentLocation(); + FVector ProbePosition = SafetyFactor * InputVector.GetSafeNormal() * GetMaxSpeed() * DeltaTime + CapsuleLocation; + const FHitResult TraceResult = CreateCapsuleTrace(CapsuleLocation, ProbePosition); + if (!TraceResult.bBlockingHit) + { + //everything is fine, use that vector + return InputVector; + } + + //otherwise remove the component of that vector that goes towards the collision + FVector CollisionVector = TraceResult.Location - CapsuleLocation; + + //sometimes (if by chance we already moved into collision entirely CollisionVector is 0 + if(! CollisionVector.Normalize()) + { + //then we probably start already in collision, so we use the last one + CollisionVector = LastSteeringCollisionVector; + } + else + { + LastSteeringCollisionVector = CollisionVector; + } + + FVector SafeInput = InputVector; + const float DotProduct = FVector::DotProduct(InputVector, CollisionVector); + if(DotProduct>0.0f) + { + // only keep perpendicular part of the input vector (remove anything towards hit) + SafeInput -= DotProduct * CollisionVector; + } + return SafeInput; +} + +void UVRPawnMovement::MoveByGravityOrStepUp(float DeltaSeconds) +{ + const FVector DownTraceStart = CapsuleColliderComponent->GetComponentLocation(); + const float DownTraceDist = MaxFallingDepth < 0.0f ? 1000.0f : MaxFallingDepth; + const FVector DownTraceDir = FVector(0,0,-1); + const FVector DownTraceEnd = DownTraceStart + DownTraceDist * DownTraceDir; + + const FHitResult DownTraceHitResult = CreateCapsuleTrace(DownTraceStart, DownTraceEnd); + float HeightDifference = 0.0f; + + if(DownTraceHitResult.bBlockingHit) + { + HeightDifference = DownTraceHitResult.ImpactPoint.Z - UpdatedComponent->GetComponentLocation().Z; + //so for HeightDifference>0, we have to move the pawn up; for HeightDifference<0 we have to move it down + } + + //Going up (in Fly and Walk Mode) + if (HeightDifference>0.0f && HeightDifference<=MaxStepHeight) + { + ShiftVertically(HeightDifference, UpSteppingAcceleration, DeltaSeconds); + } + + if(NavigationMode!=EVRNavigationModes::NAV_WALK) + { + return; + } + + if(!DownTraceHitResult.bBlockingHit && MaxFallingDepth<0.0f) + { + HeightDifference = -1000.0f; //just fall + } + + //Gravity (only in Walk Mode) + if (HeightDifference<0.0f) + { + ShiftVertically(HeightDifference, GravityAcceleration, DeltaSeconds); + } +} + +void UVRPawnMovement::ShiftVertically(float Distance, float VerticalAcceleration, float DeltaSeconds) +{ + VerticalSpeed += VerticalAcceleration * DeltaSeconds; + if (abs(VerticalSpeed*DeltaSeconds) < abs(Distance)) + { + UpdatedComponent->AddWorldOffset(FVector(0.f, 0.f, VerticalSpeed * DeltaSeconds)); + } + else + { + UpdatedComponent->AddWorldOffset(FVector(0.f, 0.f, Distance)); + VerticalSpeed = 0; + } +} + +FHitResult UVRPawnMovement::CreateCapsuleTrace(const FVector Start, FVector End, bool DrawDebug) const +{ + const EDrawDebugTrace::Type DrawType = DrawDebug ? EDrawDebugTrace::Type::ForDuration : EDrawDebugTrace::Type::None; + TArray<AActor*> ActorsToIgnore; + ActorsToIgnore.Add(GetOwner()); + + //UE_LOG(LogTemp, Warning, TEXT("Capsule from %s to %s"), *Start.ToString(), *End.ToString()) + + FHitResult Hit; + UKismetSystemLibrary::CapsuleTraceSingle(GetWorld(), Start, End, CapsuleColliderComponent->GetScaledCapsuleRadius(), CapsuleColliderComponent->GetScaledCapsuleHalfHeight(), UEngineTypes::ConvertToTraceType(ECollisionChannel::ECC_Visibility), true, ActorsToIgnore, DrawType, Hit, true); + return Hit; +} -- GitLab From 586f0f8092cad4f6639f96aa0588131138050aef Mon Sep 17 00:00:00 2001 From: Ehret <jw210150@WIN.RZ.RWTH-AACHEN.DE> Date: Thu, 28 Apr 2022 11:41:48 +0200 Subject: [PATCH 6/8] make movement accesible, so we can also change, e.g., the navigation mode from blueprints --- Source/RWTHVRToolkit/Public/Pawn/VirtualRealityPawn.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/RWTHVRToolkit/Public/Pawn/VirtualRealityPawn.h b/Source/RWTHVRToolkit/Public/Pawn/VirtualRealityPawn.h index 9f027a03..3fccafef 100644 --- a/Source/RWTHVRToolkit/Public/Pawn/VirtualRealityPawn.h +++ b/Source/RWTHVRToolkit/Public/Pawn/VirtualRealityPawn.h @@ -28,7 +28,7 @@ public: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Interaction") UBasicVRInteractionComponent* BasicVRInteraction; /* Movement */ - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Pawn|Movement") UVRPawnMovement* PawnMovement; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Movement") UVRPawnMovement* PawnMovement; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Movement") float BaseTurnRate = 45.0f; /* CameraComponent */ -- GitLab From 231356fb353d530c6b5eae974b7e57e8c2a58bb0 Mon Sep 17 00:00:00 2001 From: Ehret <jw210150@WIN.RZ.RWTH-AACHEN.DE> Date: Fri, 20 May 2022 12:59:35 +0200 Subject: [PATCH 7/8] slight performance improvement --- Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp | 7 ++++--- Source/RWTHVRToolkit/Public/Pawn/VRPawnMovement.h | 6 +++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp b/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp index 59da4c3d..84708a50 100644 --- a/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp +++ b/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp @@ -247,11 +247,12 @@ void UVRPawnMovement::ShiftVertically(float Distance, float VerticalAcceleration } } -FHitResult UVRPawnMovement::CreateCapsuleTrace(const FVector Start, FVector End, bool DrawDebug) const +FHitResult UVRPawnMovement::CreateCapsuleTrace(const FVector Start, FVector End, bool DrawDebug) { const EDrawDebugTrace::Type DrawType = DrawDebug ? EDrawDebugTrace::Type::ForDuration : EDrawDebugTrace::Type::None; - TArray<AActor*> ActorsToIgnore; - ActorsToIgnore.Add(GetOwner()); + if(ActorsToIgnore.Num()==0){ + ActorsToIgnore.Add(GetOwner()); + } //UE_LOG(LogTemp, Warning, TEXT("Capsule from %s to %s"), *Start.ToString(), *End.ToString()) diff --git a/Source/RWTHVRToolkit/Public/Pawn/VRPawnMovement.h b/Source/RWTHVRToolkit/Public/Pawn/VRPawnMovement.h index 53ea5ecd..275bbb34 100644 --- a/Source/RWTHVRToolkit/Public/Pawn/VRPawnMovement.h +++ b/Source/RWTHVRToolkit/Public/Pawn/VRPawnMovement.h @@ -4,6 +4,7 @@ #include "GameFramework/FloatingPawnMovement.h" #include "Components/CapsuleComponent.h" #include "Camera/CameraComponent.h" +#include "Actor.h" #include "VRPawnMovement.generated.h" @@ -64,7 +65,7 @@ public: private: //check for - FHitResult CreateCapsuleTrace(const FVector Start, FVector End, bool DrawDebug=false) const; + FHitResult CreateCapsuleTrace(const FVector Start, FVector End, bool DrawDebug=false); void SetCapsuleColliderToUserSize(); void CheckForPhysWalkingCollision(); FVector GetCollisionSafeVirtualSteeringVec(FVector InputVector, float DeltaTime); @@ -77,4 +78,7 @@ private: float VerticalSpeed = 0.0f; FVector LastCapsulePosition; FVector LastSteeringCollisionVector; + + //just stored for performance gains; + TArray<AActor*> ActorsToIgnore; }; -- GitLab From 7ddb21a23c7dc058423cfb348e8a515175538ffa Mon Sep 17 00:00:00 2001 From: Ehret <jw210150@WIN.RZ.RWTH-AACHEN.DE> Date: Mon, 23 May 2022 10:36:59 +0200 Subject: [PATCH 8/8] deactivate collision when it persists (e.g., because something of he environment moved into us, like the rotation VCI logo) --- .../Private/Pawn/VRPawnMovement.cpp | 32 ++++++++++++------- .../Public/Pawn/VRPawnMovement.h | 5 ++- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp b/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp index 84708a50..40b293ec 100644 --- a/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp +++ b/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp @@ -22,7 +22,7 @@ UVRPawnMovement::UVRPawnMovement(const FObjectInitializer& ObjectInitializer) : void UVRPawnMovement::BeginPlay() { Super::BeginPlay(); - LastCapsulePosition = FVector(0,0,0); + LastCapsulePosition.Reset(); LastSteeringCollisionVector = FVector(0,0,0); } @@ -42,6 +42,12 @@ void UVRPawnMovement::TickComponent(float DeltaTime, enum ELevelTick TickType, F ConsumeInputVector(); AddInputVector(InputVector); } + + const FVector CapsuleLocation = CapsuleColliderComponent->GetComponentLocation(); + if(bDeactivatedWhileInCollision && !CreateCapsuleTrace(CapsuleLocation, CapsuleLocation).bBlockingHit) + { + bDeactivatedWhileInCollision=false; + } if(NavigationMode == EVRNavigationModes::NAV_FLY || NavigationMode == EVRNavigationModes::NAV_WALK) { @@ -61,7 +67,7 @@ void UVRPawnMovement::TickComponent(float DeltaTime, enum ELevelTick TickType, F MoveByGravityOrStepUp(DeltaTime); //if we physically (in the tracking space) walked into something, move the world away (by moving the pawn) - CheckForPhysWalkingCollision(); + if(!bDeactivatedWhileInCollision) CheckForPhysWalkingCollision(); } if(NavigationMode == EVRNavigationModes::NAV_NONE) @@ -130,15 +136,15 @@ void UVRPawnMovement::SetCapsuleColliderToUserSize() void UVRPawnMovement::CheckForPhysWalkingCollision() { - if(LastCapsulePosition.IsNearlyZero()) + if(!LastCapsulePosition.IsSet()) { - //probably not yet set, so do nothing than setting it + //not yet set, so do nothing than setting it LastCapsulePosition = CapsuleColliderComponent->GetComponentLocation(); return; } const FVector CapsuleLocation = CapsuleColliderComponent->GetComponentLocation(); - const FHitResult HitResult = CreateCapsuleTrace(LastCapsulePosition, CapsuleLocation); + const FHitResult HitResult = CreateCapsuleTrace(LastCapsulePosition.GetValue(), CapsuleLocation); //if this was not possible move the entire pawn away to avoid the head collision if (HitResult.bBlockingHit) @@ -147,13 +153,17 @@ void UVRPawnMovement::CheckForPhysWalkingCollision() //move it out twice as far, to avoid getting stuck situations UpdatedComponent->AddWorldOffset(2*MoveOutVector); } - else + + + //only update if not in collision + if(!CreateCapsuleTrace(CapsuleLocation, CapsuleLocation).bBlockingHit) { - //only update if not in collision - if(!CreateCapsuleTrace(CapsuleLocation, CapsuleLocation).bBlockingHit) - { - LastCapsulePosition = CapsuleLocation; - } + LastCapsulePosition = CapsuleColliderComponent->GetComponentLocation();; + } + else{ + //we are still in collision, so deactivate collision handling until this stopped + bDeactivatedWhileInCollision=true; + LastCapsulePosition.Reset(); } } diff --git a/Source/RWTHVRToolkit/Public/Pawn/VRPawnMovement.h b/Source/RWTHVRToolkit/Public/Pawn/VRPawnMovement.h index 275bbb34..47812548 100644 --- a/Source/RWTHVRToolkit/Public/Pawn/VRPawnMovement.h +++ b/Source/RWTHVRToolkit/Public/Pawn/VRPawnMovement.h @@ -76,9 +76,12 @@ private: UPROPERTY() USceneComponent* HeadComponent = nullptr; float VerticalSpeed = 0.0f; - FVector LastCapsulePosition; + TOptional<FVector> LastCapsulePosition; FVector LastSteeringCollisionVector; + //this will deactivate all collision avoidance, so we can move out of a collision, e.g. if movements of the scene provoked a collision + bool bDeactivatedWhileInCollision = false; + //just stored for performance gains; TArray<AActor*> ActorsToIgnore; }; -- GitLab