Skip to content
Snippets Groups Projects
Commit 81b854aa authored by Daniel Rupp's avatar Daniel Rupp
Browse files

merged VRPawn

parents 077961e1 4f7f253b
Branches
Tags
No related merge requests found
No preview for this file type
No preview for this file type
File deleted
......@@ -5,7 +5,6 @@
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "LandscapeRender.h"
#include "Kismet/KismetSystemLibrary.h"
#include "Utility/VirtualRealityUtilities.h"
......

#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.Reset();
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)
{
PositionChange.Z = 0.0f;
// 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(PositionChange);
AddInputVector(InputVector);
}
if(NavigationMode == EVRNavigationModes::NAV_FLY || NavigationMode == EVRNavigationModes::NAV_WALK)
const FVector CapsuleLocation = CapsuleColliderComponent->GetComponentLocation();
if(bDeactivatedWhileInCollision && !CreateCapsuleTrace(CapsuleLocation, CapsuleLocation).bBlockingHit)
{
MoveByGravityOrStepUp(DeltaTime);
CheckForPhysWalkingCollision();
bDeactivatedWhileInCollision=false;
}
if(CheckForVirtualMovCollision(PositionChange, DeltaTime))
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)
if(!bDeactivatedWhileInCollision) CheckForPhysWalkingCollision();
}
if(NavigationMode == EVRNavigationModes::NAV_NONE)
{
//just remove whatever input is there
ConsumeInputVector();
}
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
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)
......@@ -68,126 +90,183 @@ 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
{
//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->SetWorldRotation(FRotator(0, 0, 1));
CapsuleColliderComponent->SetWorldLocation(HeadComponent->GetComponentLocation() - FVector(0, 0, ColliderHalfHeight));
}
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);
if (FHitResultPhys.bBlockingHit)
if(!LastCapsulePosition.IsSet())
{
UpdatedComponent->AddLocalOffset(FHitResultPhys.Normal*FHitResultPhys.PenetrationDepth);
}
//not yet set, so do nothing than setting it
LastCapsulePosition = CapsuleColliderComponent->GetComponentLocation();
return;
}
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);
//Going up (in Fly and Walk Mode)
if ((HitDetailsMultiLineTrace.bBlockingHit && HitDetailsMultiLineTrace.Distance < MaxStepHeight))
const FVector CapsuleLocation = CapsuleColliderComponent->GetComponentLocation();
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)
{
ShiftVertically(DistanceDifference, UpSteppingAcceleration, DeltaSeconds, 1);
const FVector MoveOutVector = HitResult.Location-CapsuleLocation;
//move it out twice as far, to avoid getting stuck situations
UpdatedComponent->AddWorldOffset(2*MoveOutVector);
}
//Gravity (only in Walk Mode)
else if (NavigationMode==EVRNavigationModes::NAV_WALK && ((HitDetailsMultiLineTrace.bBlockingHit && HitDetailsMultiLineTrace.Distance > MaxStepHeight) || (HitDetailsMultiLineTrace.GetActor() == nullptr && HitDetailsMultiLineTrace.Distance != -1.0f)))
//only update if not in collision
if(!CreateCapsuleTrace(CapsuleLocation, CapsuleLocation).bBlockingHit)
{
ShiftVertically(DistanceDifference, GravityAcceleration, DeltaSeconds, -1);
LastCapsulePosition = CapsuleColliderComponent->GetComponentLocation();;
}
else{
//we are still in collision, so deactivate collision handling until this stopped
bDeactivatedWhileInCollision=true;
LastCapsulePosition.Reset();
}
}
void UVRPawnMovement::ShiftVertically(float DiffernceDistance, float VerticalAcceleration, float DeltaSeconds, int Direction)
FVector UVRPawnMovement::GetCollisionSafeVirtualSteeringVec(FVector InputVector, float DeltaTime)
{
VerticalSpeed += VerticalAcceleration * DeltaSeconds;
if (VerticalSpeed*DeltaSeconds < DiffernceDistance)
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)
{
UpdatedComponent->AddWorldOffset(FVector(0.f, 0.f, Direction * VerticalSpeed * DeltaSeconds));
//everything is fine, use that vector
return InputVector;
}
else
//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())
{
UpdatedComponent->AddWorldOffset(FVector(0.f, 0.f, Direction * DiffernceDistance));
VerticalSpeed = 0;
//then we probably start already in collision, so we use the last one
CollisionVector = LastSteeringCollisionVector;
}
else
{
LastSteeringCollisionVector = CollisionVector;
}
FHitResult UVRPawnMovement::CreateLineTrace(FVector Direction, const FVector Start, bool Visibility)
FVector SafeInput = InputVector;
const float DotProduct = FVector::DotProduct(InputVector, CollisionVector);
if(DotProduct>0.0f)
{
//Re-initialize hit info
FHitResult HitDetails = FHitResult(ForceInit);
// only keep perpendicular part of the input vector (remove anything towards hit)
SafeInput -= DotProduct * CollisionVector;
}
return SafeInput;
}
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.
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;
if (Visibility)
DrawDebugLine(GetWorld(), Start, End, FColor::Green, false, 1, 0, 1);
const FHitResult DownTraceHitResult = CreateCapsuleTrace(DownTraceStart, DownTraceEnd);
float HeightDifference = 0.0f;
if (GetWorld()->LineTraceSingleByChannel(HitDetails, Start, End, ECC_Visibility, TraceParams))
if(DownTraceHitResult.bBlockingHit)
{
if (HitDetails.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;
}
return HitDetails;
if(!DownTraceHitResult.bBlockingHit && MaxFallingDepth<0.0f)
{
HeightDifference = -1000.0f; //just fall
}
FHitResult UVRPawnMovement::CreateMultiLineTrace(FVector Direction, const FVector Start, float Radius, bool Visibility)
//Gravity (only in Walk Mode)
if (HeightDifference<0.0f)
{
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)
ShiftVertically(HeightDifference, GravityAcceleration, DeltaSeconds);
}
}
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
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;
}
}
bool IsBlockingHitAndSameActor = true;
bool IsAllNothingHiting = true;
// loop through TArray
for (FVector& Vector : StartVectors)
FHitResult UVRPawnMovement::CreateCapsuleTrace(const FVector Start, FVector End, bool DrawDebug)
{
FHitResult OutHit = CreateLineTrace(Direction, Vector, Visibility);
OutHits.Add(OutHit);
IsBlockingHitAndSameActor &= (OutHit.GetActor() == OutHits[0].GetActor()); //If all Hiting the same Object, then you are (going up/down) or (walking)
IsAllNothingHiting &= (OutHit.GetActor() == nullptr); //If all Hiting nothing, then you are falling
const EDrawDebugTrace::Type DrawType = DrawDebug ? EDrawDebugTrace::Type::ForDuration : EDrawDebugTrace::Type::None;
if(ActorsToIgnore.Num()==0){
ActorsToIgnore.Add(GetOwner());
}
if (IsBlockingHitAndSameActor || IsAllNothingHiting)
HitDetailsMultiLineTrace = OutHits[0];
//UE_LOG(LogTemp, Warning, TEXT("Capsule from %s to %s"), *Start.ToString(), *End.ToString())
return HitDetailsMultiLineTrace;
FHitResult Hit;
UKismetSystemLibrary::CapsuleTraceSingle(GetWorld(), Start, End, CapsuleColliderComponent->GetScaledCapsuleRadius(), CapsuleColliderComponent->GetScaledCapsuleHalfHeight(), UEngineTypes::ConvertToTraceType(ECollisionChannel::ECC_Visibility), true, ActorsToIgnore, DrawType, Hit, true);
return Hit;
}
......@@ -4,6 +4,7 @@
#include "GameFramework/FloatingPawnMovement.h"
#include "Components/CapsuleComponent.h"
#include "Camera/CameraComponent.h"
#include "Actor.h"
#include "VRPawnMovement.generated.h"
......@@ -35,6 +36,8 @@ class RWTHVRTOOLKIT_API UVRPawnMovement : public UFloatingPawnMovement
public:
virtual void BeginPlay() override;
virtual void TickComponent(float DeltaTime, enum ELevelTick TickType,
FActorComponentTickFunction* ThisTickFunction) override;
......@@ -43,31 +46,42 @@ 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 GravityAcceleration = 981.0f;
float MaxFallingDepth = 1000.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement")
float UpSteppingAcceleration = 500.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement", meta = (ClampMax="0.0"))
float GravityAcceleration = -981.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement")
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement", meta = (ClampMin="0.0"))
float UpSteppingAcceleration = 981.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement", meta = (ClampMin="0.0"))
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=false);
void SetCapsuleColliderToUserSize();
void CheckForPhysWalkingCollision();
bool CheckForVirtualMovCollision(FVector PositionChange, float DeltaTime);
FVector GetCollisionSafeVirtualSteeringVec(FVector InputVector, 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;
float VerticalSpeed = 0.0f;
FVector LastHeadPosition;
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;
};
......@@ -37,6 +37,11 @@ public:
UBasicVRInteractionComponent* BasicVRInteraction;
<<<<<<< Source/RWTHVRToolkit/Public/Pawn/VirtualRealityPawn.h
/* Movement */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Movement") UVRPawnMovement* PawnMovement;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Movement") float BaseTurnRate = 45.0f;
=======
/** Workaround dummy component to prevent the Capsule from rotating in the editor, if LiveLink tracking is being used.
* This happens due to the rotation of the Capsule being set only while in Play Mode (instead of using e.g. absolute rotation).
* Additionally, there is an implicit race condition in Tick, due to LiveLink adjusting the parent's rotation, while the capsule
......@@ -49,6 +54,7 @@ public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Movement")
UVRPawnMovement* PawnMovement;
>>>>>>> Source/RWTHVRToolkit/Public/Pawn/VirtualRealityPawn.h
/* CameraComponent */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Camera")
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment