Skip to content
Snippets Groups Projects
Commit ce188108 authored by David Gilbert's avatar David Gilbert :bug:
Browse files

Merge branch 'feature/pawn_replication' into 'dev/5.3'

Initial Pawn Replication

See merge request !31
parents d320b42b 642246bb
No related branches found
No related tags found
1 merge request!31Initial Pawn Replication
Showing
with 1310 additions and 206 deletions
No preview for this file type
No preview for this file type
File added
{
"nDisplay":
{
"description": "nDisplay configuration",
"version": "5.00",
"assetPath": "/RWTHVRToolkit/RWTHVRCluster/Config/ExampleConfigs/NDC_CaveUnwrap.NDC_CaveUnwrap",
"misc":
{
"bFollowLocalPlayerCamera": false,
"bExitOnEsc": true,
"bOverrideViewportsFromExternalConfig": false
},
"scene":
{
"xforms":
{
"cave_origin":
{
"parentId": "",
"location":
{
"x": 0,
"y": 0,
"z": 10
},
"rotation":
{
"pitch": 0,
"yaw": 0,
"roll": 0
}
},
"cave_center":
{
"parentId": "cave_origin",
"location":
{
"x": 0,
"y": 0,
"z": 100
},
"rotation":
{
"pitch": 0,
"yaw": 0,
"roll": 0
}
},
"display_front":
{
"parentId": "cave_center",
"location":
{
"x": 150,
"y": 0,
"z": 0
},
"rotation":
{
"pitch": 0,
"yaw": 0,
"roll": 0
}
},
"display_floor":
{
"parentId": "cave_center",
"location":
{
"x": 0,
"y": 0,
"z": -100
},
"rotation":
{
"pitch": -90,
"yaw": 0,
"roll": 0
}
},
"angle_left":
{
"parentId": "cave_center",
"location":
{
"x": 150,
"y": -150,
"z": 0
},
"rotation":
{
"pitch": 0,
"yaw": -89.999992370605469,
"roll": 0
}
},
"display_left":
{
"parentId": "angle_left",
"location":
{
"x": -1.52587890625e-05,
"y": -149.99996948242188,
"z": 0
},
"rotation":
{
"pitch": 0,
"yaw": 0,
"roll": 0
}
},
"angle_right":
{
"parentId": "cave_center",
"location":
{
"x": 150,
"y": 150,
"z": 0
},
"rotation":
{
"pitch": 0,
"yaw": 89.999992370605469,
"roll": 0
}
},
"display_right":
{
"parentId": "angle_right",
"location":
{
"x": 0,
"y": 150,
"z": 0
},
"rotation":
{
"pitch": 0,
"yaw": 0,
"roll": 0
}
},
"angle_ceiling":
{
"parentId": "cave_center",
"location":
{
"x": 150,
"y": 0,
"z": 100
},
"rotation":
{
"pitch": 90,
"yaw": 0,
"roll": 0
}
},
"display_ceiling":
{
"parentId": "angle_ceiling",
"location":
{
"x": -7.62939453125e-06,
"y": 0,
"z": 74.999984741210938
},
"rotation":
{
"pitch": 0,
"yaw": 0,
"roll": 0
}
}
},
"cameras":
{
"DefaultViewPoint":
{
"interpupillaryDistance": 6.4000000953674316,
"swapEyes": false,
"stereoOffset": "none",
"parentId": "",
"location":
{
"x": 0,
"y": 0,
"z": 50
},
"rotation":
{
"pitch": 0,
"yaw": 0,
"roll": 0
}
}
},
"screens":
{
"scr_left":
{
"size":
{
"width": 300,
"height": 200
},
"parentId": "display_left",
"location":
{
"x": 0,
"y": 0,
"z": 0
},
"rotation":
{
"pitch": 0,
"yaw": 0,
"roll": 0
}
},
"scr_front":
{
"size":
{
"width": 300,
"height": 200
},
"parentId": "display_front",
"location":
{
"x": 0,
"y": 0,
"z": 0
},
"rotation":
{
"pitch": 0,
"yaw": 0,
"roll": 0
}
},
"scr_right":
{
"size":
{
"width": 300,
"height": 200
},
"parentId": "display_right",
"location":
{
"x": 0,
"y": 0,
"z": 0
},
"rotation":
{
"pitch": 0,
"yaw": 0,
"roll": 0
}
},
"scr_ceiling":
{
"size":
{
"width": 300,
"height": 150
},
"parentId": "display_ceiling",
"location":
{
"x": 0,
"y": 0,
"z": 0
},
"rotation":
{
"pitch": 0,
"yaw": 0,
"roll": 0
}
},
"scr_floor":
{
"size":
{
"width": 300,
"height": 300
},
"parentId": "display_floor",
"location":
{
"x": 0,
"y": 0,
"z": 0
},
"rotation":
{
"pitch": 0,
"yaw": 0,
"roll": 0
}
}
}
},
"cluster":
{
"primaryNode":
{
"id": "node_left",
"ports":
{
"ClusterSync": 41001,
"ClusterEventsJson": 41003,
"ClusterEventsBinary": 41004
}
},
"sync":
{
"renderSyncPolicy":
{
"type": "ethernet",
"parameters":
{
}
},
"inputSyncPolicy":
{
"type": "ReplicatePrimary",
"parameters":
{
}
}
},
"network":
{
"ConnectRetriesAmount": "300",
"ConnectRetryDelay": "1000",
"GameStartBarrierTimeout": "18000000",
"FrameStartBarrierTimeout": "1800000",
"FrameEndBarrierTimeout": "1800000",
"RenderSyncBarrierTimeout": "1800000"
},
"failover":
{
"failoverPolicy": "Disabled"
},
"nodes":
{
"node_left":
{
"host": "127.0.0.1",
"sound": true,
"fullScreen": false,
"renderHeadless": false,
"graphicsAdapter": -1,
"textureShare": false,
"window":
{
"x": 0,
"y": 300,
"w": 600,
"h": 400
},
"postprocess":
{
},
"viewports":
{
"vp_left":
{
"camera": "DefaultViewPoint",
"bufferRatio": 1,
"gPUIndex": -1,
"allowCrossGPUTransfer": false,
"isShared": false,
"overscan":
{
"bEnabled": false,
"mode": "percent",
"left": 0,
"right": 0,
"top": 0,
"bottom": 0,
"oversize": true
},
"region":
{
"x": 0,
"y": 0,
"w": 600,
"h": 400
},
"projectionPolicy":
{
"type": "simple",
"parameters":
{
"screen": "scr_left"
}
}
}
},
"outputRemap":
{
"bEnable": false,
"dataSource": "mesh",
"staticMeshAsset": "",
"externalFile": ""
}
},
"node_front":
{
"host": "134.130.70.51",
"sound": false,
"fullScreen": false,
"renderHeadless": false,
"graphicsAdapter": -1,
"textureShare": false,
"window":
{
"x": 0,
"y": 0,
"w": 600,
"h": 400
},
"postprocess":
{
},
"viewports":
{
"vp_front":
{
"camera": "DefaultViewPoint",
"bufferRatio": 1,
"gPUIndex": -1,
"allowCrossGPUTransfer": false,
"isShared": false,
"overscan":
{
"bEnabled": false,
"mode": "percent",
"left": 0,
"right": 0,
"top": 0,
"bottom": 0,
"oversize": true
},
"region":
{
"x": 0,
"y": 0,
"w": 600,
"h": 400
},
"projectionPolicy":
{
"type": "simple",
"parameters":
{
"screen": "scr_front"
}
}
}
},
"outputRemap":
{
"bEnable": false,
"dataSource": "mesh",
"staticMeshAsset": "",
"externalFile": ""
}
},
"node_right":
{
"host": "127.0.0.1",
"sound": false,
"fullScreen": false,
"renderHeadless": false,
"graphicsAdapter": -1,
"textureShare": false,
"window":
{
"x": 600,
"y": 900,
"w": 600,
"h": 400
},
"postprocess":
{
},
"viewports":
{
"vp_right":
{
"camera": "DefaultViewPoint",
"bufferRatio": 1,
"gPUIndex": -1,
"allowCrossGPUTransfer": false,
"isShared": false,
"overscan":
{
"bEnabled": false,
"mode": "percent",
"left": 0,
"right": 0,
"top": 0,
"bottom": 0,
"oversize": true
},
"region":
{
"x": 0,
"y": 0,
"w": 600,
"h": 400
},
"projectionPolicy":
{
"type": "simple",
"parameters":
{
"screen": "scr_right"
}
}
}
},
"outputRemap":
{
"bEnable": false,
"dataSource": "mesh",
"staticMeshAsset": "",
"externalFile": ""
}
},
"node_ceiling":
{
"host": "127.0.0.1",
"sound": false,
"fullScreen": false,
"renderHeadless": false,
"graphicsAdapter": -1,
"textureShare": false,
"window":
{
"x": 600,
"y": 0,
"w": 600,
"h": 300
},
"postprocess":
{
},
"viewports":
{
"vp_ceiling":
{
"camera": "DefaultViewPoint",
"bufferRatio": 1,
"gPUIndex": -1,
"allowCrossGPUTransfer": false,
"isShared": false,
"overscan":
{
"bEnabled": false,
"mode": "percent",
"left": 0,
"right": 0,
"top": 0,
"bottom": 0,
"oversize": true
},
"region":
{
"x": 0,
"y": 0,
"w": 600,
"h": 300
},
"projectionPolicy":
{
"type": "simple",
"parameters":
{
"screen": "scr_ceiling"
}
}
}
},
"outputRemap":
{
"bEnable": false,
"dataSource": "mesh",
"staticMeshAsset": "",
"externalFile": ""
}
},
"node_floor":
{
"host": "127.0.0.1",
"sound": false,
"fullScreen": false,
"renderHeadless": false,
"graphicsAdapter": -1,
"textureShare": false,
"window":
{
"x": 600,
"y": 300,
"w": 600,
"h": 600
},
"postprocess":
{
},
"viewports":
{
"vp_floor":
{
"camera": "DefaultViewPoint",
"bufferRatio": 1,
"gPUIndex": -1,
"allowCrossGPUTransfer": false,
"isShared": false,
"overscan":
{
"bEnabled": false,
"mode": "percent",
"left": 0,
"right": 0,
"top": 0,
"bottom": 0,
"oversize": true
},
"region":
{
"x": 0,
"y": 0,
"w": 600,
"h": 600
},
"projectionPolicy":
{
"type": "simple",
"parameters":
{
"screen": "scr_floor"
}
}
}
},
"outputRemap":
{
"bEnable": false,
"dataSource": "mesh",
"staticMeshAsset": "",
"externalFile": ""
}
}
}
},
"customParameters":
{
"SampleArg1": "SampleVal1",
"SampleArg2": "SampleVal2"
},
"diagnostics":
{
"simulateLag": false,
"minLagTime": 0.0099999997764825821,
"maxLagTime": 0.5
}
}
}
\ No newline at end of file
No preview for this file type
No preview for this file type
// Fill out your copyright notice in the Description page of Project Settings.
#include "Core/ClientTransformReplication.h"
#include "Net/UnrealNetwork.h"
UClientTransformReplication::UClientTransformReplication()
{
PrimaryComponentTick.bCanEverTick = true;
PrimaryComponentTick.TickGroup = TG_PostUpdateWork;
PrimaryComponentTick.SetTickFunctionEnable(true);
SetIsReplicatedByDefault(true);
// Direct transform replication
ControllerNetUpdateRate = 100.0f; // 100 htz is default
ControllerNetUpdateCount = 0.0f;
}
// Naive direct transform replication (replace with input rep?)
void UClientTransformReplication::UpdateState(float DeltaTime)
{
const auto* OwningActor = GetOwner();
if (OwningActor && OwningActor->HasLocalNetOwner())
{
if (GetIsReplicated())
{
const FVector Loc = OwningActor->GetActorLocation();
const FRotator Rot = OwningActor->GetActorRotation();
if (!Loc.Equals(ReplicatedTransform.Position) || !Rot.Equals(ReplicatedTransform.Rotation))
{
ControllerNetUpdateCount += DeltaTime;
if (ControllerNetUpdateCount >= (1.0f / ControllerNetUpdateRate)) // todo save inverse?
{
ControllerNetUpdateCount = 0.0f;
ReplicatedTransform.Position = Loc;
ReplicatedTransform.Rotation = Rot;
if (GetNetMode() == NM_Client) // why do we differentiate here between netmode and authority?
{
SendControllerTransform_ServerRpc(ReplicatedTransform);
}
}
}
}
}
}
void UClientTransformReplication::TickComponent(float DeltaTime, ELevelTick TickType,
FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
UpdateState(DeltaTime);
}
void UClientTransformReplication::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty >& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
// Skipping the owner with this as the owner will use the controllers location directly
DOREPLIFETIME_CONDITION(UClientTransformReplication, ReplicatedTransform, COND_SkipOwner);
DOREPLIFETIME(UClientTransformReplication, ControllerNetUpdateRate);
}
void UClientTransformReplication::SendControllerTransform_ServerRpc_Implementation(FVRTransformRep NewTransform)
{
// Store new transform and trigger OnRep_Function
ReplicatedTransform = NewTransform;
if (!GetOwner()->HasLocalNetOwner())
OnRep_ReplicatedTransform();
}
bool UClientTransformReplication::SendControllerTransform_ServerRpc_Validate(FVRTransformRep NewTransform)
{
return true;
// Optionally check to make sure that player is inside of their bounds and deny it if they aren't?
}
\ No newline at end of file
// Fill out your copyright notice in the Description page of Project Settings.
#include "Core/RWTHVRGameModeBase.h"
#include "Core/RWTHVRPlayerState.h"
#include "Kismet/GameplayStatics.h"
#include "Logging/StructuredLog.h"
FString ARWTHVRGameModeBase::InitNewPlayer(APlayerController* NewPlayerController, const FUniqueNetIdRepl& UniqueId,
const FString& Options, const FString& Portal)
{
// Used by the DisplayClusterNetDriver/Connection to handshake nodes. Could use their types directly
// but I don't really want to introduce a hard dependency here.
const FString NodeNameKey = "node";
const FString PrimaryNodeIdKey = "PrimaryNodeId";
// Check if we're using our custom PlayerState so that we can save the player type there.
// If not, just ingore all related args.
ARWTHVRPlayerState* State = Cast<ARWTHVRPlayerState>(NewPlayerController->PlayerState);
if (State != nullptr)
{
if (UGameplayStatics::HasOption(Options, PrimaryNodeIdKey))
{
const FString PrimaryNodeId = UGameplayStatics::ParseOption(Options, PrimaryNodeIdKey);
// When the primary node is a listen server, it apparently doesn't get the node option...
// Could additionally check for listen, but this should be save enough.
const FString NodeName = UGameplayStatics::HasOption(Options, NodeNameKey)
? UGameplayStatics::ParseOption(Options, NodeNameKey)
: PrimaryNodeId;
const EPlayerType Type = NodeName == PrimaryNodeId ? EPlayerType::nDisplayPrimary : EPlayerType::nDisplaySecondary;
State->RequestSetPlayerType(Type);
}
}
return Super::InitNewPlayer(NewPlayerController, UniqueId, Options, Portal);
}
// Fill out your copyright notice in the Description page of Project Settings.
#include "Core/RWTHVRPlayerState.h"
#include "Net/UnrealNetwork.h"
#include "Net/Core/PushModel/PushModel.h"
// Boilerplate, copies properties to new state
void ARWTHVRPlayerState::CopyProperties(class APlayerState* PlayerState)
{
Super::CopyProperties(PlayerState);
if (IsValid(PlayerState))
{
ARWTHVRPlayerState* RWTHVRPlayerState = Cast<ARWTHVRPlayerState>(PlayerState);
if (IsValid(RWTHVRPlayerState))
{
RWTHVRPlayerState->SetPlayerType(GetPlayerType());
}
}
}
// Boilerplate
void ARWTHVRPlayerState::OverrideWith(class APlayerState* PlayerState)
{
Super::OverrideWith(PlayerState);
if (IsValid(PlayerState))
{
const ARWTHVRPlayerState* RWTHVRPlayerState = Cast<ARWTHVRPlayerState>(PlayerState);
if (IsValid(RWTHVRPlayerState))
{
SetPlayerType(RWTHVRPlayerState->GetPlayerType());
}
}
}
// Replicate our property similar to the other state properties
void ARWTHVRPlayerState::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
FDoRepLifetimeParams SharedParams;
SharedParams.bIsPushBased = true;
DOREPLIFETIME_WITH_PARAMS_FAST(ARWTHVRPlayerState, PlayerType, SharedParams);
}
void ARWTHVRPlayerState::SetPlayerTypeServerRpc_Implementation(const EPlayerType NewPlayerType)
{
SetPlayerType(NewPlayerType);
}
void ARWTHVRPlayerState::SetPlayerType(const EPlayerType NewPlayerType)
{
MARK_PROPERTY_DIRTY_FROM_NAME(ARWTHVRPlayerState, PlayerType, this);
PlayerType = NewPlayerType;
}
void ARWTHVRPlayerState::RequestSetPlayerType(const EPlayerType NewPlayerType)
{
if (HasAuthority())
{
SetPlayerType(NewPlayerType);
}
else
{
SetPlayerTypeServerRpc(NewPlayerType);
}
}
#pragma once
#include "CoreMinimal.h"
#include "VRTransformRep.generated.h"
// Simple custom transform struct for more efficient repliation, from VRE Plugin
USTRUCT()
struct RWTHVRTOOLKIT_API FVRTransformRep
{
GENERATED_USTRUCT_BODY()
public:
UPROPERTY(Transient)
FVector Position;
UPROPERTY(Transient)
FRotator Rotation;
FVRTransformRep()
{
Position = FVector::ZeroVector;
Rotation = FRotator::ZeroRotator;
}
/**
* @param Ar FArchive to read or write from.
* @param Map PackageMap used to resolve references to UObject*
* @param bOutSuccess return value to signify if the serialization was succesfull (if false, an error will be logged by the calling function)
*
* @return return true if the serialization was fully mapped. If false, the property will be considered 'dirty' and will replicate again on the next update.
* This is needed for UActor* properties. If an actor's Actorchannel is not fully mapped, properties referencing it must stay dirty.
* Note that UPackageMap::SerializeObject returns false if an object is unmapped. Generally, you will want to return false from your ::NetSerialize
* if you make any calls to ::SerializeObject that return false.
*
*/
bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess)
{
bOutSuccess = true;
bOutSuccess &= SerializePackedVector<1, 24>(Position, Ar);
Rotation.SerializeCompressed(Ar);
return bOutSuccess;
}
};
\ No newline at end of file
...@@ -93,11 +93,19 @@ void UGrabComponent::BeginPlay() ...@@ -93,11 +93,19 @@ void UGrabComponent::BeginPlay()
void UGrabComponent::SetupInputActions() void UGrabComponent::SetupInputActions()
{ {
auto InputSubsystem = UVirtualRealityUtilities::GetVRPawnLocalPlayerSubsystem(GetWorld()); const APawn* Pawn = Cast<APawn>(GetOwner());
const APlayerController* PlayerController = Cast<APlayerController>(Pawn->GetController());
const ULocalPlayer* LP = PlayerController ? PlayerController->GetLocalPlayer() : nullptr;
if (LP == nullptr)
return;
UEnhancedInputLocalPlayerSubsystem* InputSubsystem = LP->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>();
// add Input Mapping context // add Input Mapping context
InputSubsystem->AddMappingContext(IMCGrab,0); InputSubsystem->AddMappingContext(IMCGrab,0);
UEnhancedInputComponent* EI = UVirtualRealityUtilities::GetVRPawnInputComponent(GetWorld()); UEnhancedInputComponent* EI = Cast<UEnhancedInputComponent>(Pawn->InputComponent);
if (EI == nullptr)
return;
EI->BindAction(GrabInputAction, ETriggerEvent::Started, this, &UGrabComponent::OnBeginGrab); EI->BindAction(GrabInputAction, ETriggerEvent::Started, this, &UGrabComponent::OnBeginGrab);
EI->BindAction(GrabInputAction, ETriggerEvent::Completed, this, &UGrabComponent::OnEndGrab); EI->BindAction(GrabInputAction, ETriggerEvent::Completed, this, &UGrabComponent::OnEndGrab);
......
...@@ -79,11 +79,19 @@ void URaycastSelectionComponent::TickComponent(float DeltaTime, ELevelTick TickT ...@@ -79,11 +79,19 @@ void URaycastSelectionComponent::TickComponent(float DeltaTime, ELevelTick TickT
void URaycastSelectionComponent::SetupInputActions() void URaycastSelectionComponent::SetupInputActions()
{ {
auto InputSubsystem = UVirtualRealityUtilities::GetVRPawnLocalPlayerSubsystem(GetWorld()); const APawn* Pawn = Cast<APawn>(GetOwner());
const APlayerController* PlayerController = Cast<APlayerController>(Pawn->GetController());
const ULocalPlayer* LP = PlayerController ? PlayerController->GetLocalPlayer() : nullptr;
if (LP == nullptr)
return;
UEnhancedInputLocalPlayerSubsystem* InputSubsystem = LP->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>();
// add Input Mapping context // add Input Mapping context
InputSubsystem->AddMappingContext(IMCRaycastSelection,0); InputSubsystem->AddMappingContext(IMCRaycastSelection,0);
UEnhancedInputComponent* EI = UVirtualRealityUtilities::GetVRPawnInputComponent(GetWorld()); UEnhancedInputComponent* EI = Cast<UEnhancedInputComponent>(Pawn->InputComponent);
if (EI == nullptr)
return;
EI->BindAction(RayCastSelectInputAction, ETriggerEvent::Started, this, &URaycastSelectionComponent::OnBeginSelect); EI->BindAction(RayCastSelectInputAction, ETriggerEvent::Started, this, &URaycastSelectionComponent::OnBeginSelect);
EI->BindAction(RayCastSelectInputAction, ETriggerEvent::Completed, this, &URaycastSelectionComponent::OnEndSelect); EI->BindAction(RayCastSelectInputAction, ETriggerEvent::Completed, this, &URaycastSelectionComponent::OnEndSelect);
......
...@@ -3,15 +3,12 @@ ...@@ -3,15 +3,12 @@
#include "Pawn/BasicVRInteractionComponent.h" #include "Pawn/BasicVRInteractionComponent.h"
#include "Interaction/Clickable.h" #include "Interaction/Clickable.h"
#include "Interaction/Grabable.h" #include "Interaction/Grabable.h"
#include "Interaction/Targetable.h" #include "Interaction/Targetable.h"
#include "Interaction/GrabbingBehaviorComponent.h" #include "Interaction/GrabbingBehaviorComponent.h"
#include "Misc/Optional.h" #include "Misc/Optional.h"
#include "DrawDebugHelpers.h" #include "DrawDebugHelpers.h"
#include "Components/WidgetComponent.h"
#include "UObject/ConstructorHelpers.h"
DEFINE_LOG_CATEGORY(LogVRInteractionComponent); DEFINE_LOG_CATEGORY(LogVRInteractionComponent);
...@@ -24,17 +21,15 @@ UBasicVRInteractionComponent::UBasicVRInteractionComponent() ...@@ -24,17 +21,15 @@ UBasicVRInteractionComponent::UBasicVRInteractionComponent()
// Setup the interaction ray. // Setup the interaction ray.
InteractionRay = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Interaction Ray")); InteractionRay = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Interaction Ray"));
//this ray model has an inlayed cross with flipped normals so it can be seen as a cross in desktop mode where the right hand is attached to the head InteractionRay->SetCastShadow(false);
ConstructorHelpers::FObjectFinder<UStaticMesh> MeshAsset(TEXT("/RWTHVRToolkit/PointingRay/Ray_Mesh"));
if (MeshAsset.Object != nullptr)
{
InteractionRay->SetStaticMesh(MeshAsset.Object);
}
// turns off collisions as the InteractionRay is only meant to visualize the ray // turns off collisions as the InteractionRay is only meant to visualize the ray
InteractionRay->SetCollisionProfileName(TEXT("NoCollision")); InteractionRay->SetCollisionProfileName(TEXT("NoCollision"));
bShowDebug = false; //otherwise the WidgetInteractionComponent debug vis is shown // The static mesh reference is set in the blueprint, avoid ConstructorHelpers and hardcoded paths!
InteractionSource = EWidgetInteractionSource::Custom; //can also be kept at default (World), this way, however, we efficiently reuse the line traces // this ray model has an inlayed cross with flipped normals so it can be seen as a cross in desktop mode where the right hand is attached to the head
bShowDebug = false; //otherwise the WidgetInteractionComponent debug vis is shown
InteractionSource = EWidgetInteractionSource::Custom;
//can also be kept at default (World), this way, however, we efficiently reuse the line traces
} }
void UBasicVRInteractionComponent::BeginPlay() void UBasicVRInteractionComponent::BeginPlay()
...@@ -43,7 +38,8 @@ void UBasicVRInteractionComponent::BeginPlay() ...@@ -43,7 +38,8 @@ void UBasicVRInteractionComponent::BeginPlay()
//WidgetInteractionComponent //WidgetInteractionComponent
InteractionDistance = MaxClickDistance; InteractionDistance = MaxClickDistance;
InteractionRay->SetRelativeScale3D(FVector(MaxClickDistance / 100.0f, 0.5f, 0.5f)); //the ray model has a length of 100cm (and is a bit too big in Y/Z dir) InteractionRay->SetRelativeScale3D(FVector(MaxClickDistance / 100.0f, 0.5f, 0.5f));
//the ray model has a length of 100cm (and is a bit too big in Y/Z dir)
SetInteractionRayVisibility(InteractionRayVisibility); SetInteractionRayVisibility(InteractionRayVisibility);
} }
...@@ -101,11 +97,13 @@ void UBasicVRInteractionComponent::EndInteraction() ...@@ -101,11 +97,13 @@ void UBasicVRInteractionComponent::EndInteraction()
// Detach the Actor // Detach the Actor
if (GrabbedActor->FindComponentByClass<UGrabbingBehaviorComponent>() == nullptr) if (GrabbedActor->FindComponentByClass<UGrabbingBehaviorComponent>() == nullptr)
{ {
if (ComponentSimulatingPhysics) { if (ComponentSimulatingPhysics)
{
ComponentSimulatingPhysics->DetachFromComponent(FDetachmentTransformRules::KeepWorldTransform); ComponentSimulatingPhysics->DetachFromComponent(FDetachmentTransformRules::KeepWorldTransform);
ComponentSimulatingPhysics->SetSimulatePhysics(true); ComponentSimulatingPhysics->SetSimulatePhysics(true);
} }
else { else
{
GrabbedActor->GetRootComponent()->DetachFromComponent(FDetachmentTransformRules::KeepWorldTransform); GrabbedActor->GetRootComponent()->DetachFromComponent(FDetachmentTransformRules::KeepWorldTransform);
} }
} }
...@@ -117,7 +115,8 @@ void UBasicVRInteractionComponent::EndInteraction() ...@@ -117,7 +115,8 @@ void UBasicVRInteractionComponent::EndInteraction()
} }
// Called every frame // Called every frame
void UBasicVRInteractionComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) void UBasicVRInteractionComponent::TickComponent(float DeltaTime, ELevelTick TickType,
FActorComponentTickFunction* ThisTickFunction)
{ {
Super::TickComponent(DeltaTime, TickType, ThisTickFunction); Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
...@@ -221,31 +220,38 @@ void UBasicVRInteractionComponent::SetInteractionRayVisibility(EInteractionRayVi ...@@ -221,31 +220,38 @@ void UBasicVRInteractionComponent::SetInteractionRayVisibility(EInteractionRayVi
case Invisible: case Invisible:
InteractionRay->SetVisibility(false); InteractionRay->SetVisibility(false);
break; break;
default: ;
} }
} }
if (InteractionRayVisibility == EInteractionRayVisibility::VisibleOnHoverOnly && !bCanRaytraceEveryTick) if (InteractionRayVisibility == EInteractionRayVisibility::VisibleOnHoverOnly && !bCanRaytraceEveryTick)
{ {
UE_LOG(LogVRInteractionComponent, Warning, TEXT("VisibleOnHoverOnly needs bCanRaytraceEveryTick=true, so this is set!")); UE_LOG(LogVRInteractionComponent, Warning,
TEXT("VisibleOnHoverOnly needs bCanRaytraceEveryTick=true, so this is set!"));
bCanRaytraceEveryTick = true; bCanRaytraceEveryTick = true;
} }
if (InteractionRayVisibility == EInteractionRayVisibility::Visible && !bCanRaytraceEveryTick) if (InteractionRayVisibility == EInteractionRayVisibility::Visible && !bCanRaytraceEveryTick)
{ {
UE_LOG(LogVRInteractionComponent, Warning, TEXT("VisibleOnHoverOnly will need two clicks to interact with widgets if bCanRaytraceEveryTick is not set!")); UE_LOG(LogVRInteractionComponent, Warning,
TEXT(
"VisibleOnHoverOnly will need two clicks to interact with widgets if bCanRaytraceEveryTick is not set!"
));
} }
} }
void UBasicVRInteractionComponent::HandlePhysicsAndAttachActor(AActor* HitActor) void UBasicVRInteractionComponent::HandlePhysicsAndAttachActor(const AActor* HitActor)
{ {
UPrimitiveComponent* PhysicsSimulatingComp = GetFirstComponentSimulatingPhysics(HitActor); UPrimitiveComponent* PhysicsSimulatingComp = GetFirstComponentSimulatingPhysics(HitActor);
const FAttachmentTransformRules Rules = FAttachmentTransformRules(EAttachmentRule::KeepWorld, false); const FAttachmentTransformRules Rules = FAttachmentTransformRules(EAttachmentRule::KeepWorld, false);
if (PhysicsSimulatingComp) { if (PhysicsSimulatingComp)
{
PhysicsSimulatingComp->SetSimulatePhysics(false); PhysicsSimulatingComp->SetSimulatePhysics(false);
PhysicsSimulatingComp->AttachToComponent(InteractionRayEmitter, Rules); PhysicsSimulatingComp->AttachToComponent(InteractionRayEmitter, Rules);
ComponentSimulatingPhysics = PhysicsSimulatingComp; ComponentSimulatingPhysics = PhysicsSimulatingComp;
} }
else { else
{
HitActor->GetRootComponent()->AttachToComponent(InteractionRayEmitter, Rules); HitActor->GetRootComponent()->AttachToComponent(InteractionRayEmitter, Rules);
} }
} }
...@@ -275,14 +281,16 @@ TOptional<FHitResult> UBasicVRInteractionComponent::RaytraceForFirstHit(const FT ...@@ -275,14 +281,16 @@ TOptional<FHitResult> UBasicVRInteractionComponent::RaytraceForFirstHit(const FT
return {}; return {};
} }
UPrimitiveComponent* GetFirstComponentSimulatingPhysics(const AActor* TargetActor) UPrimitiveComponent* UBasicVRInteractionComponent::GetFirstComponentSimulatingPhysics(const AActor* TargetActor)
{ {
TArray<UPrimitiveComponent*> PrimitiveComponents; TArray<UPrimitiveComponent*> PrimitiveComponents;
TargetActor->GetComponents<UPrimitiveComponent>(PrimitiveComponents); TargetActor->GetComponents<UPrimitiveComponent>(PrimitiveComponents);
// find any component that simulates physics, then traverse the hierarchy // find any component that simulates physics, then traverse the hierarchy
for (const auto& Component : PrimitiveComponents) { for (const auto& Component : PrimitiveComponents)
if (Component->IsSimulatingPhysics()) { {
if (Component->IsSimulatingPhysics())
{
return GetHighestParentSimulatingPhysics(Component); return GetHighestParentSimulatingPhysics(Component);
} }
} }
...@@ -290,12 +298,14 @@ UPrimitiveComponent* GetFirstComponentSimulatingPhysics(const AActor* TargetActo ...@@ -290,12 +298,14 @@ UPrimitiveComponent* GetFirstComponentSimulatingPhysics(const AActor* TargetActo
} }
// recursively goes up the hierarchy and returns the highest parent simulating physics // recursively goes up the hierarchy and returns the highest parent simulating physics
UPrimitiveComponent* GetHighestParentSimulatingPhysics(UPrimitiveComponent* Comp) UPrimitiveComponent* UBasicVRInteractionComponent::GetHighestParentSimulatingPhysics(UPrimitiveComponent* Comp)
{
if (Cast<UPrimitiveComponent>(Comp->GetAttachParent()) && Comp->GetAttachParent()->IsSimulatingPhysics())
{ {
if (Cast<UPrimitiveComponent>(Comp->GetAttachParent()) && Comp->GetAttachParent()->IsSimulatingPhysics()) {
return GetHighestParentSimulatingPhysics(Cast<UPrimitiveComponent>(Comp->GetAttachParent())); return GetHighestParentSimulatingPhysics(Cast<UPrimitiveComponent>(Comp->GetAttachParent()));
} }
else { else
{
return Comp; return Comp;
} }
} }
...@@ -10,43 +10,33 @@ ...@@ -10,43 +10,33 @@
#include "Utility/VirtualRealityUtilities.h" #include "Utility/VirtualRealityUtilities.h"
#include "MotionControllerComponent.h" #include "MotionControllerComponent.h"
void UContinuousMovementComponent::BeginPlay() void UContinuousMovementComponent::SetupPlayerInput(UInputComponent* PlayerInputComponent)
{ {
Super::BeginPlay(); Super::SetupPlayerInput(PlayerInputComponent);
SetupInputActions(); if (!VRPawn || !VRPawn->HasLocalNetOwner() || !InputSubsystem)
}
void UContinuousMovementComponent::SetupInputActions()
{ {
Super::SetupInputActions(); return;
}
const AVirtualRealityPawn* VRPawn = Cast<AVirtualRealityPawn>(GetOwner());
// simple way of changing the handedness // simple way of changing the handedness
if (bMoveWithRightHand) if (bMoveWithRightHand)
{ {
MovementHand = VRPawn->RightHand; MovementHand = VRPawn->RightHand;
RotationHand = VRPawn->LeftHand; RotationHand = VRPawn->LeftHand;
IMCMovement = IMCMovementRight; IMCMovement = IMCMovementRight;
} else }
else
{ {
MovementHand = VRPawn->LeftHand; MovementHand = VRPawn->LeftHand;
RotationHand = VRPawn->RightHand; RotationHand = VRPawn->RightHand;
IMCMovement = IMCMovementLeft; IMCMovement = IMCMovementLeft;
} }
const APlayerController* PlayerController = Cast<APlayerController>(VRPawn->GetController());
UEnhancedInputLocalPlayerSubsystem* InputSubsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer());
if(!InputSubsystem)
{
UE_LOG(Toolkit,Error,TEXT("InputSubsystem IS NOT VALID"));
return;
}
// add Input Mapping context // add Input Mapping context
InputSubsystem->AddMappingContext(IMCMovement, 0); InputSubsystem->AddMappingContext(IMCMovement, 0);
UEnhancedInputComponent* EI = Cast<UEnhancedInputComponent>(VRPawn->InputComponent); UEnhancedInputComponent* EI = Cast<UEnhancedInputComponent>(PlayerInputComponent);
if (!EI) if (!EI)
{ {
UE_LOG(Toolkit, Error, TEXT("Cannot cast Input Component to Enhanced Inpu Component in VRPawnMovement")); UE_LOG(Toolkit, Error, TEXT("Cannot cast Input Component to Enhanced Inpu Component in VRPawnMovement"));
...@@ -62,14 +52,19 @@ void UContinuousMovementComponent::SetupInputActions() ...@@ -62,14 +52,19 @@ void UContinuousMovementComponent::SetupInputActions()
void UContinuousMovementComponent::OnMove(const FInputActionValue& Value) void UContinuousMovementComponent::OnMove(const FInputActionValue& Value)
{ {
AVirtualRealityPawn* VRPawn = Cast<AVirtualRealityPawn>(GetOwner()); if (!VRPawn || !VRPawn->Controller)
const bool bGazeDirected = UVirtualRealityUtilities::IsDesktopMode() || SteeringMode == EVRSteeringModes::STEER_GAZE_DIRECTED; return;
const FVector ForwardDir = bGazeDirected ? VRPawn->HeadCameraComponent->GetForwardVector() : MovementHand->GetForwardVector(); const bool bGazeDirected = UVirtualRealityUtilities::IsDesktopMode() || SteeringMode ==
const FVector RightDir = bGazeDirected ? VRPawn->HeadCameraComponent->GetRightVector() : MovementHand->GetRightVector(); EVRSteeringModes::STEER_GAZE_DIRECTED;
const FVector ForwardDir = bGazeDirected
? VRPawn->HeadCameraComponent->GetForwardVector()
: MovementHand->GetForwardVector();
const FVector RightDir = bGazeDirected
? VRPawn->HeadCameraComponent->GetRightVector()
: MovementHand->GetRightVector();
if (VRPawn->Controller != nullptr)
{
const FVector2D MoveValue = Value.Get<FVector2D>(); const FVector2D MoveValue = Value.Get<FVector2D>();
// Forward/Backward direction // Forward/Backward direction
...@@ -84,11 +79,12 @@ void UContinuousMovementComponent::OnMove(const FInputActionValue& Value) ...@@ -84,11 +79,12 @@ void UContinuousMovementComponent::OnMove(const FInputActionValue& Value)
VRPawn->AddMovementInput(RightDir, MoveValue.Y); VRPawn->AddMovementInput(RightDir, MoveValue.Y);
} }
} }
}
void UContinuousMovementComponent::OnMoveUp(const FInputActionValue& Value) void UContinuousMovementComponent::OnMoveUp(const FInputActionValue& Value)
{ {
AVirtualRealityPawn* VRPawn = Cast<AVirtualRealityPawn>(GetOwner()); if (!VRPawn)
return;
const float MoveValue = Value.Get<FVector2D>().X; const float MoveValue = Value.Get<FVector2D>().X;
//the right hand is rotated on desktop to follow the cursor so it's forward is also changing with cursor position //the right hand is rotated on desktop to follow the cursor so it's forward is also changing with cursor position
VRPawn->AddMovementInput(FVector::UpVector, MoveValue); VRPawn->AddMovementInput(FVector::UpVector, MoveValue);
......
// Fill out your copyright notice in the Description page of Project Settings.
#include "Pawn/InputExtensionInterface.h"
UEnhancedInputLocalPlayerSubsystem* IInputExtensionInterface::GetEnhancedInputLocalPlayerSubsystem(APawn* Pawn) const
{
const APlayerController* PlayerController = Pawn ? Cast<APlayerController>(Pawn->GetController()) : nullptr;
const ULocalPlayer* LP = PlayerController ? PlayerController->GetLocalPlayer() : nullptr;
return LP->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>();
}
...@@ -11,36 +11,34 @@ ...@@ -11,36 +11,34 @@
#include "Pawn/VRPawnInputConfig.h" #include "Pawn/VRPawnInputConfig.h"
#include "Utility/VirtualRealityUtilities.h" #include "Utility/VirtualRealityUtilities.h"
void UMovementComponentBase::BeginPlay() void UMovementComponentBase::SetupPlayerInput(UInputComponent* PlayerInputComponent)
{ {
Super::BeginPlay(); IInputExtensionInterface::SetupPlayerInput(PlayerInputComponent);
SetupInputActions(); VRPawn = Cast<AVirtualRealityPawn>(GetOwner());
}
void UMovementComponentBase::SetupInputActions() if (!VRPawn || !VRPawn->HasLocalNetOwner())
{ {
return;
}
const AVirtualRealityPawn* VRPawn = Cast<AVirtualRealityPawn>(GetOwner()); InputSubsystem = GetEnhancedInputLocalPlayerSubsystem(VRPawn);
const APlayerController* PlayerController = Cast<APlayerController>(VRPawn->GetController());
UEnhancedInputLocalPlayerSubsystem* InputSubsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer());
if (!InputSubsystem) if (!InputSubsystem)
{ {
UE_LOG(Toolkit, Error, TEXT("InputSubsystem IS NOT VALID")); UE_LOG(Toolkit, Error, TEXT("InputSubsystem IS NOT VALID"));
return; return;
} }
// add Input Mapping context // add Input Mapping context
InputSubsystem->AddMappingContext(IMCRotation, 0); InputSubsystem->AddMappingContext(IMCRotation, 0);
UEnhancedInputComponent* EI = Cast<UEnhancedInputComponent>(VRPawn->InputComponent); UEnhancedInputComponent* EI = Cast<UEnhancedInputComponent>(PlayerInputComponent);
if (!EI) if (!EI)
{ {
UE_LOG(Toolkit, Error, TEXT("Cannot cast Input Component to Enhanced Inpu Component in VRPawnMovement")); UE_LOG(Toolkit, Error, TEXT("Cannot cast Input Component to Enhanced Inpu Component in VRPawnMovement"));
return; return;
} }
// turning // turning
if (bAllowTurning) if (bAllowTurning)
{ {
...@@ -48,7 +46,8 @@ void UMovementComponentBase::SetupInputActions() ...@@ -48,7 +46,8 @@ void UMovementComponentBase::SetupInputActions()
if (bSnapTurn && !UVirtualRealityUtilities::IsDesktopMode()) if (bSnapTurn && !UVirtualRealityUtilities::IsDesktopMode())
{ {
EI->BindAction(Turn, ETriggerEvent::Started, this, &UMovementComponentBase::OnBeginSnapTurn); EI->BindAction(Turn, ETriggerEvent::Started, this, &UMovementComponentBase::OnBeginSnapTurn);
} else }
else
{ {
EI->BindAction(Turn, ETriggerEvent::Triggered, this, &UMovementComponentBase::OnBeginTurn); EI->BindAction(Turn, ETriggerEvent::Triggered, this, &UMovementComponentBase::OnBeginTurn);
} }
...@@ -72,15 +71,14 @@ void UMovementComponentBase::EndDesktopRotation() ...@@ -72,15 +71,14 @@ void UMovementComponentBase::EndDesktopRotation()
bApplyDesktopRotation = false; bApplyDesktopRotation = false;
} }
void UMovementComponentBase::OnBeginTurn(const FInputActionValue& Value) void UMovementComponentBase::OnBeginTurn(const FInputActionValue& Value)
{ {
AVirtualRealityPawn* VRPawn = Cast<AVirtualRealityPawn>(GetOwner()); if (UVirtualRealityUtilities::IsDesktopMode() && !bApplyDesktopRotation)
return;
if(UVirtualRealityUtilities::IsDesktopMode() && !bApplyDesktopRotation) return; if (!VRPawn || !VRPawn->Controller)
return;
if (VRPawn->Controller != nullptr)
{
const FVector2D TurnValue = Value.Get<FVector2D>(); const FVector2D TurnValue = Value.Get<FVector2D>();
if (TurnValue.X != 0.f) if (TurnValue.X != 0.f)
...@@ -96,16 +94,18 @@ void UMovementComponentBase::OnBeginTurn(const FInputActionValue& Value) ...@@ -96,16 +94,18 @@ void UMovementComponentBase::OnBeginTurn(const FInputActionValue& Value)
} }
} }
} }
}
void UMovementComponentBase::OnBeginSnapTurn(const FInputActionValue& Value) void UMovementComponentBase::OnBeginSnapTurn(const FInputActionValue& Value)
{ {
AVirtualRealityPawn* VRPawn = Cast<AVirtualRealityPawn>(GetOwner()); if (!VRPawn || !VRPawn->Controller)
return;
const FVector2D TurnValue = Value.Get<FVector2D>(); const FVector2D TurnValue = Value.Get<FVector2D>();
if (TurnValue.X > 0.f) if (TurnValue.X > 0.f)
{ {
VRPawn->AddControllerYawInput(SnapTurnAngle); VRPawn->AddControllerYawInput(SnapTurnAngle);
} else if (TurnValue.X < 0.f) }
else if (TurnValue.X < 0.f)
{ {
VRPawn->AddControllerYawInput(-SnapTurnAngle); VRPawn->AddControllerYawInput(-SnapTurnAngle);
} }
......
...@@ -4,19 +4,20 @@ ...@@ -4,19 +4,20 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "Components/ActorComponent.h" #include "Components/ActorComponent.h"
#include "Pawn/InputExtensionInterface.h"
#include "MovementComponentBase.generated.h" #include "MovementComponentBase.generated.h"
/** /**
* *
*/ */
UCLASS(Blueprintable) UCLASS(Blueprintable)
class RWTHVRTOOLKIT_API UMovementComponentBase : public UActorComponent class RWTHVRTOOLKIT_API UMovementComponentBase : public UActorComponent, public IInputExtensionInterface
{ {
GENERATED_BODY() GENERATED_BODY()
public: public:
virtual void BeginPlay() override; virtual void SetupPlayerInput(UInputComponent* PlayerInputComponent) override;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement") UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement")
bool bAllowTurning = true; bool bAllowTurning = true;
...@@ -53,14 +54,15 @@ public: ...@@ -53,14 +54,15 @@ public:
void EndDesktopRotation(); void EndDesktopRotation();
protected: protected:
void SetupInputActions();
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "VR Movement|Input") UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "VR Movement|Input")
class UInputMappingContext* IMCRotation; class UInputMappingContext* IMCRotation;
private: UPROPERTY()
AVirtualRealityPawn* VRPawn;
UPROPERTY()
UEnhancedInputLocalPlayerSubsystem* InputSubsystem;
bool bApplyDesktopRotation = false; bool bApplyDesktopRotation = false;
}; };
// Fill out your copyright notice in the Description page of Project Settings.
#include "Pawn/ReplicatedCameraComponent.h"
#include "Net/UnrealNetwork.h"
UReplicatedCameraComponent::UReplicatedCameraComponent()
{
PrimaryComponentTick.bCanEverTick = true;
PrimaryComponentTick.SetTickFunctionEnable(true);
SetIsReplicatedByDefault(true);
// Direct transform replication
ControllerNetUpdateRate = 100.0f; // 100 htz is default
ControllerNetUpdateCount = 0.0f;
}
// Naive direct transform replication (replace with input rep?)
void UReplicatedCameraComponent::UpdateState(float DeltaTime)
{
if (GetOwner()->HasLocalNetOwner())
{
if (GetIsReplicated())
{
const FVector Loc = GetRelativeLocation();
const FRotator Rot = GetRelativeRotation();
if (!Loc.Equals(ReplicatedTransform.Position) || !Rot.Equals(ReplicatedTransform.Rotation))
{
ControllerNetUpdateCount += DeltaTime;
if (ControllerNetUpdateCount >= (1.0f / ControllerNetUpdateRate)) // todo save inverse?
{
ControllerNetUpdateCount = 0.0f;
ReplicatedTransform.Position = Loc;
ReplicatedTransform.Rotation = Rot;
if (GetNetMode() == NM_Client) // why do we differentiate here between netmode and authority?
{
ServerSendControllerTransformRpc(ReplicatedTransform);
}
}
}
}
}
}
void UReplicatedCameraComponent::TickComponent(float DeltaTime, ELevelTick TickType,
FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
UpdateState(DeltaTime);
}
void UReplicatedCameraComponent::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty >& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DISABLE_REPLICATED_PRIVATE_PROPERTY(USceneComponent, RelativeLocation);
DISABLE_REPLICATED_PRIVATE_PROPERTY(USceneComponent, RelativeRotation);
DISABLE_REPLICATED_PRIVATE_PROPERTY(USceneComponent, RelativeScale3D);
// Skipping the owner with this as the owner will use the controllers location directly
DOREPLIFETIME_CONDITION(UReplicatedCameraComponent, ReplicatedTransform, COND_SkipOwner);
DOREPLIFETIME(UReplicatedCameraComponent, ControllerNetUpdateRate);
}
void UReplicatedCameraComponent::ServerSendControllerTransformRpc_Implementation(FVRTransformRep NewTransform)
{
// Store new transform and trigger OnRep_Function
ReplicatedTransform = NewTransform;
if (!GetOwner()->HasLocalNetOwner())
OnRep_ReplicatedTransform();
}
bool UReplicatedCameraComponent::ServerSendControllerTransformRpc_Validate(FVRTransformRep NewTransform)
{
return true;
// Optionally check to make sure that player is inside of their bounds and deny it if they aren't?
}
\ No newline at end of file
// Fill out your copyright notice in the Description page of Project Settings.
#include "Pawn/ReplicatedMotionControllerComponent.h"
#include "Net/UnrealNetwork.h"
UReplicatedMotionControllerComponent::UReplicatedMotionControllerComponent()
{
PrimaryComponentTick.bCanEverTick = true;
PrimaryComponentTick.SetTickFunctionEnable(true);
SetIsReplicatedByDefault(true);
// Direct transform replication
ControllerNetUpdateRate = 100.0f; // 100 htz is default
ControllerNetUpdateCount = 0.0f;
}
// Naive direct transform replication (replace with input rep?)
void UReplicatedMotionControllerComponent::UpdateState(float DeltaTime)
{
if (GetOwner()->HasLocalNetOwner())
{
if (GetIsReplicated())
{
const FVector Loc = GetRelativeLocation();
const FRotator Rot = GetRelativeRotation();
if (!Loc.Equals(ReplicatedTransform.Position) || !Rot.Equals(ReplicatedTransform.Rotation))
{
ControllerNetUpdateCount += DeltaTime;
if (ControllerNetUpdateCount >= (1.0f / ControllerNetUpdateRate)) // todo save inverse?
{
ControllerNetUpdateCount = 0.0f;
ReplicatedTransform.Position = Loc;
ReplicatedTransform.Rotation = Rot;
if (GetNetMode() == NM_Client) // why do we differentiate here between netmode and authority?
{
SendControllerTransform_ServerRpc(ReplicatedTransform);
}
}
}
}
}
}
void UReplicatedMotionControllerComponent::TickComponent(float DeltaTime, ELevelTick TickType,
FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
UpdateState(DeltaTime);
}
void UReplicatedMotionControllerComponent::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty >& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DISABLE_REPLICATED_PRIVATE_PROPERTY(USceneComponent, RelativeLocation);
DISABLE_REPLICATED_PRIVATE_PROPERTY(USceneComponent, RelativeRotation);
DISABLE_REPLICATED_PRIVATE_PROPERTY(USceneComponent, RelativeScale3D);
// Skipping the owner with this as the owner will use the controllers location directly
DOREPLIFETIME_CONDITION(UReplicatedMotionControllerComponent, ReplicatedTransform, COND_SkipOwner);
DOREPLIFETIME(UReplicatedMotionControllerComponent, ControllerNetUpdateRate);
}
void UReplicatedMotionControllerComponent::SendControllerTransform_ServerRpc_Implementation(FVRTransformRep NewTransform)
{
// Store new transform and trigger OnRep_Function
ReplicatedTransform = NewTransform;
if (!GetOwner()->HasLocalNetOwner())
OnRep_ReplicatedTransform();
}
bool UReplicatedMotionControllerComponent::SendControllerTransform_ServerRpc_Validate(FVRTransformRep NewTransform)
{
return true;
// Optionally check to make sure that player is inside of their bounds and deny it if they aren't?
}
\ No newline at end of file
...@@ -16,18 +16,20 @@ ...@@ -16,18 +16,20 @@
#include "MotionControllerComponent.h" #include "MotionControllerComponent.h"
// Called when the game starts void UTeleportationComponent::SetupPlayerInput(UInputComponent* PlayerInputComponent)
void UTeleportationComponent::BeginPlay()
{ {
Super::BeginPlay(); Super::SetupPlayerInput(PlayerInputComponent);
SetupInputActions(); if (!VRPawn || !VRPawn->HasLocalNetOwner() || !InputSubsystem)
{
return;
}
TeleportTraceComponent = UNiagaraFunctionLibrary::SpawnSystemAtLocation TeleportTraceComponent = UNiagaraFunctionLibrary::SpawnSystemAtLocation
( (
GetWorld(), GetWorld(),
TeleportTraceSystem, TeleportTraceSystem,
GetOwner()->GetActorLocation(), VRPawn->GetActorLocation(),
FRotator(0), FRotator(0),
FVector(1), FVector(1),
true, true,
...@@ -38,47 +40,33 @@ void UTeleportationComponent::BeginPlay() ...@@ -38,47 +40,33 @@ void UTeleportationComponent::BeginPlay()
FActorSpawnParameters SpawnParameters = FActorSpawnParameters(); FActorSpawnParameters SpawnParameters = FActorSpawnParameters();
SpawnParameters.Name = "TeleportVisualizer"; SpawnParameters.Name = "TeleportVisualizer";
if (BPTeleportVisualizer) if (BPTeleportVisualizer)
{ {
TeleportVisualizer = GetWorld()->SpawnActor<AActor>(BPTeleportVisualizer,GetOwner()->GetActorLocation(),GetOwner()->GetActorRotation(),SpawnParameters); TeleportVisualizer = GetWorld()->SpawnActor<AActor>(BPTeleportVisualizer, VRPawn->GetActorLocation(),
VRPawn->GetActorRotation(), SpawnParameters);
} }
TeleportTraceComponent->SetVisibility(false); TeleportTraceComponent->SetVisibility(false);
TeleportVisualizer->SetActorHiddenInGame(true); TeleportVisualizer->SetActorHiddenInGame(true);
}
void UTeleportationComponent::SetupInputActions()
{
Super::SetupInputActions();
const AVirtualRealityPawn* VRPawn = Cast<AVirtualRealityPawn>(GetOwner());
// simple way of changing the handedness // simple way of changing the handedness
if (bMoveWithRightHand) if (bMoveWithRightHand)
{ {
TeleportationHand = VRPawn->RightHand; TeleportationHand = VRPawn->RightHand;
RotationHand = VRPawn->LeftHand; RotationHand = VRPawn->LeftHand;
IMCMovement = IMCTeleportRight; IMCMovement = IMCTeleportRight;
} else }
else
{ {
TeleportationHand = VRPawn->LeftHand; TeleportationHand = VRPawn->LeftHand;
RotationHand = VRPawn->RightHand; RotationHand = VRPawn->RightHand;
IMCMovement = IMCTeleportLeft; IMCMovement = IMCTeleportLeft;
} }
const APlayerController* PlayerController = Cast<APlayerController>(VRPawn->GetController());
UEnhancedInputLocalPlayerSubsystem* InputSubsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer());
if(!InputSubsystem)
{
UE_LOG(Toolkit,Error,TEXT("InputSubsystem IS NOT VALID"));
return;
}
// add Input Mapping context // add Input Mapping context
InputSubsystem->AddMappingContext(IMCMovement,0); InputSubsystem->AddMappingContext(IMCMovement,0);
UEnhancedInputComponent* EI = Cast<UEnhancedInputComponent>(VRPawn->InputComponent); UEnhancedInputComponent* EI = Cast<UEnhancedInputComponent>(PlayerInputComponent);
if (!EI) if (!EI)
{ {
UE_LOG(Toolkit,Error,TEXT("Cannot cast Input Component to Enhanced Inpu Component in VRPawnMovement")); UE_LOG(Toolkit,Error,TEXT("Cannot cast Input Component to Enhanced Inpu Component in VRPawnMovement"));
...@@ -107,8 +95,8 @@ void UTeleportationComponent::OnStartTeleportTrace(const FInputActionValue& Valu ...@@ -107,8 +95,8 @@ void UTeleportationComponent::OnStartTeleportTrace(const FInputActionValue& Valu
void UTeleportationComponent::UpdateTeleportTrace(const FInputActionValue& Value) void UTeleportationComponent::UpdateTeleportTrace(const FInputActionValue& Value)
{ {
// Update the teleport trace // Update the teleport trace
FVector StartPosition = TeleportationHand->GetComponentLocation(); const FVector StartPosition = TeleportationHand->GetComponentLocation();
FVector ForwardVector = TeleportationHand->GetForwardVector(); const FVector ForwardVector = TeleportationHand->GetForwardVector();
TArray<AActor> ActorsToIgnore; TArray<AActor> ActorsToIgnore;
...@@ -121,31 +109,28 @@ void UTeleportationComponent::UpdateTeleportTrace(const FInputActionValue& Value ...@@ -121,31 +109,28 @@ void UTeleportationComponent::UpdateTeleportTrace(const FInputActionValue& Value
ECC_WorldStatic ECC_WorldStatic
); );
PredictParams.ActorsToIgnore.Add(GetOwner()); PredictParams.ActorsToIgnore.Add(VRPawn);
PredictParams.ActorsToIgnore.Add(TeleportVisualizer); PredictParams.ActorsToIgnore.Add(TeleportVisualizer);
UGameplayStatics::PredictProjectilePath(GetWorld(),PredictParams,PredictResult); UGameplayStatics::PredictProjectilePath(GetWorld(),PredictParams,PredictResult);
FVector HitLocation = PredictResult.HitResult.Location; const FVector HitLocation = PredictResult.HitResult.Location;
bool bValidHit = PredictResult.HitResult.IsValidBlockingHit(); const bool bValidHit = PredictResult.HitResult.IsValidBlockingHit();
// check if this is a valid location to move to // check if this is a valid location to move to
FNavLocation OutLocation;
FNavAgentProperties AgentProperties = FNavAgentProperties(15, 160); FVector OutLocation;
const bool bValidProjection = IsValidTeleportLocation(PredictResult.HitResult, OutLocation);
UNavigationSystemV1* NavSystem = UNavigationSystemV1::GetCurrent(GetWorld());
// TODO: does not give valid location
const bool bValidProjection = NavSystem->ProjectPointToNavigation(HitLocation,OutLocation,FVector(1,1,1), &AgentProperties);
if (bUseNavMesh) if (bUseNavMesh)
{ {
FinalTeleportLocation = OutLocation.Location; FinalTeleportLocation = OutLocation;
if (bValidTeleportLocation != bValidProjection) if (bValidTeleportLocation != bValidProjection)
{ {
bValidTeleportLocation = bValidProjection; bValidTeleportLocation = bValidProjection;
TeleportVisualizer->SetActorHiddenInGame(!bValidTeleportLocation); TeleportVisualizer->SetActorHiddenInGame(!bValidTeleportLocation);
} }
} else }
else
{ {
if (bValidHit) if (bValidHit)
{ {
...@@ -156,7 +141,6 @@ void UTeleportationComponent::UpdateTeleportTrace(const FInputActionValue& Value ...@@ -156,7 +141,6 @@ void UTeleportationComponent::UpdateTeleportTrace(const FInputActionValue& Value
} }
} }
TArray<FVector> PathPoints; TArray<FVector> PathPoints;
PathPoints.Add(StartPosition); PathPoints.Add(StartPosition);
for (FPredictProjectilePathPointData PData : PredictResult.PathData) for (FPredictProjectilePathPointData PData : PredictResult.PathData)
...@@ -164,20 +148,29 @@ void UTeleportationComponent::UpdateTeleportTrace(const FInputActionValue& Value ...@@ -164,20 +148,29 @@ void UTeleportationComponent::UpdateTeleportTrace(const FInputActionValue& Value
PathPoints.Add(PData.Location); PathPoints.Add(PData.Location);
} }
UNiagaraDataInterfaceArrayFunctionLibrary::SetNiagaraArrayVector(TeleportTraceComponent,FName("User.PointArray"),PathPoints); UNiagaraDataInterfaceArrayFunctionLibrary::SetNiagaraArrayVector(TeleportTraceComponent,FName("User.PointArray"),PathPoints);
}
bool UTeleportationComponent::IsValidTeleportLocation(const FHitResult& Hit, FVector& ProjectedLocation) const
{
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(GetWorld());
const FNavAgentProperties& AgentProps = FNavAgentProperties(15, 160);
FNavLocation ProjectedNavLocation;
const bool bProjectPoint = (NavSys && NavSys->ProjectPointToNavigation(Hit.Location, ProjectedNavLocation, INVALID_NAVEXTENT, &AgentProps));
ProjectedLocation = ProjectedNavLocation.Location;
return bProjectPoint /*&& Hit.IsValidBlockingHit()*/;
} }
// On button release -> remove trace and teleport user to location // On button release -> remove trace and teleport user to location
void UTeleportationComponent::OnEndTeleportTrace(const FInputActionValue& Value) void UTeleportationComponent::OnEndTeleportTrace(const FInputActionValue& Value)
{ {
if (!VRPawn)
return;
// End Teleport Trace // End Teleport Trace
bTeleportTraceActive = false; bTeleportTraceActive = false;
TeleportTraceComponent->SetVisibility(false); TeleportTraceComponent->SetVisibility(false);
TeleportVisualizer->SetActorHiddenInGame(true); TeleportVisualizer->SetActorHiddenInGame(true);
bValidTeleportLocation = false; bValidTeleportLocation = false;
GetOwner()->TeleportTo(FinalTeleportLocation,GetOwner()->GetActorRotation()); VRPawn->TeleportTo(FinalTeleportLocation, VRPawn->GetActorRotation());
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment