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