From e16f499aa2b5d37d0775873715e715c329d467f1 Mon Sep 17 00:00:00 2001
From: jehret <ehret@vr.rwth-aachen.de>
Date: Mon, 30 Oct 2023 17:29:39 +0100
Subject: [PATCH 1/3] walking movement improvements: fix blocked ghost
 movement, be able to handle collisions, e.g., already at program start (so
 just temporarily disable some collision checks)

---
 .../Private/Pawn/VRPawnMovement.cpp           | 67 +++++++++++++++----
 .../Public/Pawn/Navigation/VRPawnMovement.h   |  4 +-
 2 files changed, 55 insertions(+), 16 deletions(-)

diff --git a/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp b/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp
index 5822f34a..3f6def5f 100644
--- a/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp
+++ b/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp
@@ -22,8 +22,8 @@ UVRPawnMovement::UVRPawnMovement(const FObjectInitializer& ObjectInitializer) :
 void UVRPawnMovement::BeginPlay()
 {
 	Super::BeginPlay();
-	LastCollisionFreeCapsulePosition = CapsuleColliderComponent->GetComponentLocation();
 	LastSteeringCollisionVector = FVector(0, 0, 0);
+	LastCollisionFreeCapsulePosition.Reset();
 
 	ActorsToIgnore = {GetOwner()};
 }
@@ -46,13 +46,14 @@ void UVRPawnMovement::TickComponent(float DeltaTime, enum ELevelTick TickType,
 		AddInputVector(InputVector);
 	}
 
-	//if me managed to get into a collision revert the movement since last Tick
-	CheckAndRevertCollisionSinceLastTick();
-	//check whether we are still in collision e.g. if an object has moved and got us into collision
-	MoveOutOfNewDynamicCollisions();
-
+	
 	if (NavigationMode == EVRNavigationModes::NAV_FLY || NavigationMode == EVRNavigationModes::NAV_WALK)
 	{
+		//if me managed to get into a collision revert the movement since last Tick
+		CheckAndRevertCollisionSinceLastTick();
+		//check whether we are still in collision e.g. if an object has moved and got us into collision
+		MoveOutOfNewDynamicCollisions();
+
 		if (InputVector.Size() > 0.001)
 		{
 			const FVector SafeSteeringInput = GetCollisionSafeVirtualSteeringVec(InputVector, DeltaTime);
@@ -64,6 +65,7 @@ void UVRPawnMovement::TickComponent(float DeltaTime, enum ELevelTick TickType,
 				AddInputVector(SafeSteeringInput);
 			}
 		}
+		
 
 		// so we add stepping-up (for both walk and fly)
 		// and gravity for walking only
@@ -139,12 +141,23 @@ void UVRPawnMovement::SetCapsuleColliderToUserSize() const
 
 void UVRPawnMovement::CheckAndRevertCollisionSinceLastTick()
 {
-	//check whether we are in a collision at the current position
 	const FVector CapsuleLocation = CapsuleColliderComponent->GetComponentLocation();
+
+	if(!LastCollisionFreeCapsulePosition.IsSet())
+	{
+		//we cannot revert anyways so only check if the current position is collision free
+		if (!CreateCapsuleTrace(CapsuleLocation, CapsuleLocation).bBlockingHit)
+		{
+			LastCollisionFreeCapsulePosition = CapsuleLocation;
+		}
+		return;
+	}
+
+	//check whether we are in a collision at the current position
 	if (CreateCapsuleTrace(CapsuleLocation, CapsuleLocation).bBlockingHit)
 	{
 		//if so move back to last position
-		UpdatedComponent->AddWorldOffset(LastCollisionFreeCapsulePosition - CapsuleLocation);
+		UpdatedComponent->AddWorldOffset(LastCollisionFreeCapsulePosition.GetValue() - CapsuleLocation);
 	}
 	else
 	{
@@ -154,15 +167,28 @@ void UVRPawnMovement::CheckAndRevertCollisionSinceLastTick()
 
 void UVRPawnMovement::MoveOutOfNewDynamicCollisions()
 {
-	FVector ResolveDirection = GetOverlapResolveDirection();
-	ResolveDirection *= 1.5f; //scale it up for security distance
-	UpdatedComponent->AddWorldOffset(ResolveDirection);
+	TOptional<FVector> ResolveDirectionOptional = GetOverlapResolveDirection();
+
+	if(ResolveDirectionOptional.IsSet())
+	{
+		FVector ResolveDirection = 1.5f * ResolveDirectionOptional.GetValue(); //scale it up for security distance
+		UpdatedComponent->AddWorldOffset(ResolveDirection);
+
+		//invalidate the last collision-free position, since apparently something change so ew got into this collision
+		LastCollisionFreeCapsulePosition.Reset();
+	}
 }
 
 void UVRPawnMovement::CheckForPhysWalkingCollision()
 {
+	if(!LastCollisionFreeCapsulePosition.IsSet())
+	{
+		//we don't know any old location-free location, so do nothing here
+		return;
+	}
+
 	const FVector CapsuleLocation = CapsuleColliderComponent->GetComponentLocation();
-	const FHitResult HitResult = CreateCapsuleTrace(LastCollisionFreeCapsulePosition, CapsuleLocation);
+	const FHitResult HitResult = CreateCapsuleTrace(LastCollisionFreeCapsulePosition.GetValue(), CapsuleLocation);
 
 	//if this was not possible move the entire pawn away to avoid the head collision
 	if (HitResult.bBlockingHit)
@@ -175,6 +201,14 @@ void UVRPawnMovement::CheckForPhysWalkingCollision()
 
 FVector UVRPawnMovement::GetCollisionSafeVirtualSteeringVec(FVector InputVector, float DeltaTime)
 {
+	//if we were in a collision in the last step already (so no LastCollisionFreeCapsulePosition is set)
+	// we allow movement to resole this collision (otherwise you wold be stuck forever)
+	if(!LastCollisionFreeCapsulePosition.IsSet())
+	{
+		return InputVector;
+	}
+
+
 	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;
@@ -276,7 +310,7 @@ FHitResult UVRPawnMovement::CreateCapsuleTrace(const FVector& Start, const FVect
 	return Hit;
 }
 
-FVector UVRPawnMovement::GetOverlapResolveDirection()
+TOptional<FVector> UVRPawnMovement::GetOverlapResolveDirection() const
 {
 	TArray<UPrimitiveComponent*> OverlappingComponents;
 	TArray<TEnumAsByte<EObjectTypeQuery>> traceObjectTypes;
@@ -286,8 +320,13 @@ FVector UVRPawnMovement::GetOverlapResolveDirection()
 	                                               CapsuleColliderComponent->GetScaledCapsuleHalfHeight(),
 	                                               traceObjectTypes, nullptr, ActorsToIgnore, OverlappingComponents);
 
-	FVector ResolveVector = FVector::ZeroVector;
+	if(OverlappingComponents.Num() == 0)
+	{
+		// return unset optional
+		return TOptional<FVector>();
+	}
 
+	FVector ResolveVector = FVector::ZeroVector;
 	//check what to do to move out of these collisions (or nothing if non is there)
 	//we just add the penetrations so in very unfortunate conditions this can become problematic/blocking but for now and our regular use cases this works
 	for (const UPrimitiveComponent* OverlappingComp : OverlappingComponents)
diff --git a/Source/RWTHVRToolkit/Public/Pawn/Navigation/VRPawnMovement.h b/Source/RWTHVRToolkit/Public/Pawn/Navigation/VRPawnMovement.h
index 63dc5b0e..f5e34a4d 100644
--- a/Source/RWTHVRToolkit/Public/Pawn/Navigation/VRPawnMovement.h
+++ b/Source/RWTHVRToolkit/Public/Pawn/Navigation/VRPawnMovement.h
@@ -65,7 +65,7 @@ public:
 private:
 	//check for
 	FHitResult CreateCapsuleTrace(const FVector& Start, const FVector& End, bool DrawDebug = false) const;
-	FVector GetOverlapResolveDirection();
+	TOptional<FVector> GetOverlapResolveDirection() const;
 	void SetCapsuleColliderToUserSize() const;
 	void CheckForPhysWalkingCollision();
 	FVector GetCollisionSafeVirtualSteeringVec(FVector InputVector, float DeltaTime);
@@ -78,7 +78,7 @@ private:
 	USceneComponent* HeadComponent = nullptr;
 
 	float VerticalSpeed = 0.0f;
-	FVector LastCollisionFreeCapsulePosition;
+	TOptional<FVector> LastCollisionFreeCapsulePosition;
 	FVector LastSteeringCollisionVector;
 
 	//just stored for performance gains;
-- 
GitLab


From 1fa4703d4fb078a7d4a2ce39ce3a7fcb8fd4c8bd Mon Sep 17 00:00:00 2001
From: David Gilbert <gilbert@vr.rwth-aachen.de>
Date: Tue, 31 Oct 2023 09:08:54 +0100
Subject: [PATCH 2/3] style(navigation): unify formatting in VRPawnMovement

---
 .../Private/Pawn/VRPawnMovement.cpp              | 16 ++++++++--------
 .../Public/Pawn/Navigation/VRPawnMovement.h      |  1 +
 2 files changed, 9 insertions(+), 8 deletions(-)

diff --git a/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp b/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp
index 3f6def5f..14fa63ff 100644
--- a/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp
+++ b/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp
@@ -1,5 +1,5 @@
 #include "Pawn/Navigation/VRPawnMovement.h"
-#include "DrawDebugHelpers.h"
+
 #include "Kismet/KismetSystemLibrary.h"
 
 UVRPawnMovement::UVRPawnMovement(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
@@ -46,7 +46,7 @@ void UVRPawnMovement::TickComponent(float DeltaTime, enum ELevelTick TickType,
 		AddInputVector(InputVector);
 	}
 
-	
+
 	if (NavigationMode == EVRNavigationModes::NAV_FLY || NavigationMode == EVRNavigationModes::NAV_WALK)
 	{
 		//if me managed to get into a collision revert the movement since last Tick
@@ -65,7 +65,7 @@ void UVRPawnMovement::TickComponent(float DeltaTime, enum ELevelTick TickType,
 				AddInputVector(SafeSteeringInput);
 			}
 		}
-		
+
 
 		// so we add stepping-up (for both walk and fly)
 		// and gravity for walking only
@@ -143,7 +143,7 @@ void UVRPawnMovement::CheckAndRevertCollisionSinceLastTick()
 {
 	const FVector CapsuleLocation = CapsuleColliderComponent->GetComponentLocation();
 
-	if(!LastCollisionFreeCapsulePosition.IsSet())
+	if (!LastCollisionFreeCapsulePosition.IsSet())
 	{
 		//we cannot revert anyways so only check if the current position is collision free
 		if (!CreateCapsuleTrace(CapsuleLocation, CapsuleLocation).bBlockingHit)
@@ -169,7 +169,7 @@ void UVRPawnMovement::MoveOutOfNewDynamicCollisions()
 {
 	TOptional<FVector> ResolveDirectionOptional = GetOverlapResolveDirection();
 
-	if(ResolveDirectionOptional.IsSet())
+	if (ResolveDirectionOptional.IsSet())
 	{
 		FVector ResolveDirection = 1.5f * ResolveDirectionOptional.GetValue(); //scale it up for security distance
 		UpdatedComponent->AddWorldOffset(ResolveDirection);
@@ -181,7 +181,7 @@ void UVRPawnMovement::MoveOutOfNewDynamicCollisions()
 
 void UVRPawnMovement::CheckForPhysWalkingCollision()
 {
-	if(!LastCollisionFreeCapsulePosition.IsSet())
+	if (!LastCollisionFreeCapsulePosition.IsSet())
 	{
 		//we don't know any old location-free location, so do nothing here
 		return;
@@ -203,7 +203,7 @@ FVector UVRPawnMovement::GetCollisionSafeVirtualSteeringVec(FVector InputVector,
 {
 	//if we were in a collision in the last step already (so no LastCollisionFreeCapsulePosition is set)
 	// we allow movement to resole this collision (otherwise you wold be stuck forever)
-	if(!LastCollisionFreeCapsulePosition.IsSet())
+	if (!LastCollisionFreeCapsulePosition.IsSet())
 	{
 		return InputVector;
 	}
@@ -320,7 +320,7 @@ TOptional<FVector> UVRPawnMovement::GetOverlapResolveDirection() const
 	                                               CapsuleColliderComponent->GetScaledCapsuleHalfHeight(),
 	                                               traceObjectTypes, nullptr, ActorsToIgnore, OverlappingComponents);
 
-	if(OverlappingComponents.Num() == 0)
+	if (OverlappingComponents.Num() == 0)
 	{
 		// return unset optional
 		return TOptional<FVector>();
diff --git a/Source/RWTHVRToolkit/Public/Pawn/Navigation/VRPawnMovement.h b/Source/RWTHVRToolkit/Public/Pawn/Navigation/VRPawnMovement.h
index f5e34a4d..95e88e0d 100644
--- a/Source/RWTHVRToolkit/Public/Pawn/Navigation/VRPawnMovement.h
+++ b/Source/RWTHVRToolkit/Public/Pawn/Navigation/VRPawnMovement.h
@@ -74,6 +74,7 @@ private:
 
 	UPROPERTY(VisibleAnywhere)
 	UCapsuleComponent* CapsuleColliderComponent = nullptr;
+
 	UPROPERTY()
 	USceneComponent* HeadComponent = nullptr;
 
-- 
GitLab


From b062819e75989dc983e7f9287292ef17dc2223a8 Mon Sep 17 00:00:00 2001
From: David Gilbert <gilbert@vr.rwth-aachen.de>
Date: Tue, 31 Oct 2023 09:13:02 +0100
Subject: [PATCH 3/3] docs(navigation): small typo fixes

---
 Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp b/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp
index 14fa63ff..1fcabf5b 100644
--- a/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp
+++ b/Source/RWTHVRToolkit/Private/Pawn/VRPawnMovement.cpp
@@ -174,7 +174,7 @@ void UVRPawnMovement::MoveOutOfNewDynamicCollisions()
 		FVector ResolveDirection = 1.5f * ResolveDirectionOptional.GetValue(); //scale it up for security distance
 		UpdatedComponent->AddWorldOffset(ResolveDirection);
 
-		//invalidate the last collision-free position, since apparently something change so ew got into this collision
+		//invalidate the last collision-free position, since apparently something changed so we got into this collision
 		LastCollisionFreeCapsulePosition.Reset();
 	}
 }
@@ -183,7 +183,7 @@ void UVRPawnMovement::CheckForPhysWalkingCollision()
 {
 	if (!LastCollisionFreeCapsulePosition.IsSet())
 	{
-		//we don't know any old location-free location, so do nothing here
+		//we don't know any old collision-free location, so do nothing here
 		return;
 	}
 
@@ -201,7 +201,7 @@ void UVRPawnMovement::CheckForPhysWalkingCollision()
 
 FVector UVRPawnMovement::GetCollisionSafeVirtualSteeringVec(FVector InputVector, float DeltaTime)
 {
-	//if we were in a collision in the last step already (so no LastCollisionFreeCapsulePosition is set)
+	// if we were in a collision in the last step already (so no LastCollisionFreeCapsulePosition is set)
 	// we allow movement to resole this collision (otherwise you wold be stuck forever)
 	if (!LastCollisionFreeCapsulePosition.IsSet())
 	{
@@ -327,7 +327,7 @@ TOptional<FVector> UVRPawnMovement::GetOverlapResolveDirection() const
 	}
 
 	FVector ResolveVector = FVector::ZeroVector;
-	//check what to do to move out of these collisions (or nothing if non is there)
+	//check what to do to move out of these collisions (or nothing if none is there)
 	//we just add the penetrations so in very unfortunate conditions this can become problematic/blocking but for now and our regular use cases this works
 	for (const UPrimitiveComponent* OverlappingComp : OverlappingComponents)
 	{
-- 
GitLab