diff --git a/README.md b/README.md
index ed281e72867c0ac394b3e9dd3ef8f4f28338285f..1a729310cac5e51ac40529e89f0be81cb9121c08 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,56 @@
-# nDisplayExtensions
+# :card_index_dividers: nDisplayExtensions
+The nDisplayExtensions are a collection of extensions of the VR-Group at the RWTH which are used in many of our applications.
+The contents of this plugin are constantly extended and improved to reflect changes in the Unreal nDisplay Plugin that we use to support our aixCAVE with Unreal.
 
-Unreal Engine nDisplay plugin extensions, including custom CAVE game modes, pawns and more.
\ No newline at end of file
+In the following the main features are explained:
+
+## :open_file_folder:  Cluster
+This folder contains cluster specific helpers or components
+
+### :open_file_folder: Events
+This folder contains a wrapper around the Cluster Events that enables calling a method from an object on all nodes in the cluster by exposing a `DECLARE_DISPLAY_CLUSTER_EVENT` macro. This can be used to react to events that only occur on one node, e.g., input events on the master node. An example and more explanation on how to use the wrapper can be found [here](https://devhub.vr.rwth-aachen.de/VR-Group/unreal-development/unrealprojecttemplate/-/snippets/44).
+
+## :open_file_folder: Fixes
+This folder contains fixes for problems that exist in the Unreal Engine, which can be fixed at runtime. The description of every single fix can be found in the specific file.
+
+## :open_file_folder:  Interaction
+This folder contains interaction related components, which can be attached to actors in your application to reflect a specific feature. Some of these components require the corresponding component on your pawn.
+
+By simply inheriting from the `IGrabable` or `IClickable` interface, your actor will respond to the pawn's clicks. A grabable actor (an actor who inherited from `IGrabable`) is automatically attached to the controller when grabbed and can be dragged around. If you wish to alter this behavior you can give a `GrabbingBehaviorComponent` to the actor, which will constraint his movement on a line or circle. For details on the customization you can make, take a look at the header files of these GrabbingBeaviorComponents.
+Aditionally, you can override the `OnGrabbed` and `OnReleased` functions which will be called by the pawn through delegates. 
+
+A clickable actor should override the `OnClicked` function for interaction to take place. If the pawn clicks on an actor who inherited from `IClickable`, he calls the actor's `OnClicked` function.
+
+## :open_file_folder: Pawn
+This folder consists of our pawn implementation and some components that are attached to it.
+
+### :diamond_shape_with_a_dot_inside: Virtual Reality Pawn
+This simple pawn implementation only attaches the following components to itself and handles some input. All the attached components do not depend on this pawn and can thus be added to your pawn implementation as well.
+
+### :diamond_shape_with_a_dot_inside: Universal Tracked Component
+The universal tracked component can be added to your pawn and is configured via two properties:
+* `ProxyType` states for which type of tracked device this component is meant. E.g. this component can behave like the users head, left or right hand.
+* `AttachementType` states which controller is used as a tracked device in the aixCAVE. E.g. this component will use the flystick as a tracking source in the aixCAVE.
+
+This component determines on startup if it is used in a desktop, VR or cluster application and handles the attachment of itself for the corresponding scenario:
+* In desktop mode, all components of this type are attached to the pawn root, except for the head, which is attached to the pawn's camera.
+* In VR mode, the head component is attached to the HMD. All other types spawn a `MotionControllerComponent` on your Pawn and attaches themself to this component. The spawned component is configured according to the `ProxyType` above.
+* In cluster mode, these components attach themself to the tracked nDisplay components as specified by `ProxyType` and `AttachementType`
+
+The usage of this component is simple: Attach it to your pawn, set the `ProxyType` and `AttachementType` to your needs and you can attach everything to these components are needed. The components handle the tracking for you.
+
+### :diamond_shape_with_a_dot_inside: VR Pawn Movement
+This component adds a configurable movement to your pawn implementation. It can be configured via the `NavigationMode`, which behaves as follows:
+* `NAV_NONE` No input is handled
+* `NAV_GHOST` Simple flying movement, but no collision with walls
+* `NAV_FLY` Flying movement with collision checking
+* `NAV_WALK` Walking movement that simulates gravity and collisions for the pawn
+
+To use this component attach it to your pawn and make sure to call
+```cpp
+PawnMovement->SetUpdatedComponent(RootComponent);
+PawnMovement->SetCameraComponent(CameraComponent);
+```
+
+## :open_file_folder: Utility
+This folder contains the VirtualRealityUtilities.h, which is a collection of static functions which can be helpful in every application
diff --git a/Source/DisplayClusterExtensions/Private/Interaction/GrabbingBehaviorOnLineComponent.cpp b/Source/DisplayClusterExtensions/Private/Interaction/GrabbingBehaviorOnLineComponent.cpp
index f57b28f7e9284bedeff9ae2198a13e8ad2635b33..e84d2138562958034cb9a4918c9af008abe161bd 100644
--- a/Source/DisplayClusterExtensions/Private/Interaction/GrabbingBehaviorOnLineComponent.cpp
+++ b/Source/DisplayClusterExtensions/Private/Interaction/GrabbingBehaviorOnLineComponent.cpp
@@ -9,8 +9,6 @@ UGrabbingBehaviorOnLineComponent::UGrabbingBehaviorOnLineComponent()
 	// Set this component to be initialized when the game starts, and to be ticked every frame.  You can turn these features
 	// off to improve performance if you don't need them.
 	PrimaryComponentTick.bCanEverTick = true;
-
-	SetAbsolute(true, true, true);
 	
 	this->Distance = 0;
 }
@@ -23,11 +21,20 @@ void UGrabbingBehaviorOnLineComponent::SetDistance(float Dist)
 	this->Distance = Dist;
 }
 
+
 float UGrabbingBehaviorOnLineComponent::GetDistance() const 
 {
 	return this->Distance;
 }
 
+
+void UGrabbingBehaviorOnLineComponent::SetDiscreteNumberOfPoints(int Num)
+{
+	NumPoints = Num;
+	bIsDiscrete = true;
+}
+
+
 void UGrabbingBehaviorOnLineComponent::HandleNewPositionAndDirection(FVector Position, FQuat Orientation)
 {
 	FVector AttachmentPoint = this->GetRelativeLocation();
@@ -39,16 +46,29 @@ void UGrabbingBehaviorOnLineComponent::HandleNewPositionAndDirection(FVector Pos
 	FVector Temp = FVector::CrossProduct(FromHandToMe, ConstraintAxis);
 	Temp.Normalize();
 
-	FVector PlaneNormal = FVector::CrossProduct(ConstraintAxis,Temp);
+	FVector PlaneNormal = FVector::CrossProduct(ConstraintAxis, Temp);
 
 	// get intersection point defined by plane
-	FVector Intersection =  FMath::LinePlaneIntersection(Position, Position + Direction, AttachmentPoint, PlaneNormal);
+	FVector Intersection = FMath::LinePlaneIntersection(Position, Position + Direction, AttachmentPoint, PlaneNormal);
 	FVector FromOriginToIntersection = Intersection - AttachmentPoint;
 
 	// point along the constraint axis with length of the projection from intersection point onto the axis
-	FVector NewPosition = FVector::DotProduct(FromOriginToIntersection, ConstraintAxis)* ConstraintAxis;
-	
+	FVector NewPosition = FVector::DotProduct(FromOriginToIntersection, ConstraintAxis) * ConstraintAxis;
+
 	NewPosition = NewPosition.GetClampedToMaxSize(Distance);
+
+	if (bIsDiscrete)
+	{
+		float lengthOfSegment = 1.f / static_cast<float>(NumPoints + 1.f);
+		FVector LineBeginning = -ConstraintAxis * Distance;
+		float LengthOnLine = (FVector::DotProduct(FromOriginToIntersection, ConstraintAxis) / Distance + 1.f) / 2.f; // is between 0 and 1
+
+		float VectorSize = FMath::CeilToFloat(LengthOnLine / lengthOfSegment);
+		if (VectorSize <= 0) VectorSize = 1;
+		if (VectorSize > NumPoints) VectorSize = NumPoints;
+		NewPosition = LineBeginning + VectorSize * ConstraintAxis * lengthOfSegment * Distance * 2.f;
+	}
+
 	NewPosition += AttachmentPoint;
 
 	// transform the targeted actor which is owner of this component with calculated quaternion and posiition
@@ -56,6 +76,7 @@ void UGrabbingBehaviorOnLineComponent::HandleNewPositionAndDirection(FVector Pos
 	GetOwner()->SetActorLocation(NewPosition);
 }
 
+
 // Called when the game starts
 void UGrabbingBehaviorOnLineComponent::BeginPlay()
 {
diff --git a/Source/DisplayClusterExtensions/Private/Interaction/GrabbingBehaviorOnPlaneComponent.cpp b/Source/DisplayClusterExtensions/Private/Interaction/GrabbingBehaviorOnPlaneComponent.cpp
index dfc8e0ef5f7c2013c3dce5b452752d2e7466a216..1c581c6a120510280e0c833555e6f85142953642 100644
--- a/Source/DisplayClusterExtensions/Private/Interaction/GrabbingBehaviorOnPlaneComponent.cpp
+++ b/Source/DisplayClusterExtensions/Private/Interaction/GrabbingBehaviorOnPlaneComponent.cpp
@@ -9,9 +9,6 @@ UGrabbingBehaviorOnPlaneComponent::UGrabbingBehaviorOnPlaneComponent()
 	// Set this component to be initialized when the game starts, and to be ticked every frame.  You can turn these features
 	// off to improve performance if you don't need them.
 	PrimaryComponentTick.bCanEverTick = true;
-
-	SetAbsolute(true, true, true);
-	// ...
 }
 
 
@@ -43,7 +40,6 @@ void UGrabbingBehaviorOnPlaneComponent::HandleNewPositionAndDirection(FVector Po
 	// after this NewPoint is in world position
 	NewPosition += AttachmentPoint;
 
-
 	// set new position and orientation using calculated quaternion and position
 	// here rotation is not changed
 	GetOwner()->SetActorLocation(NewPosition);
diff --git a/Source/DisplayClusterExtensions/Private/Interaction/Targetable.cpp b/Source/DisplayClusterExtensions/Private/Interaction/Targetable.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fae43d0426aea760f38e9795fa703714a71490fc
--- /dev/null
+++ b/Source/DisplayClusterExtensions/Private/Interaction/Targetable.cpp
@@ -0,0 +1,8 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "Interaction/Targetable.h"
+
+UTargetable::UTargetable(const FObjectInitializer& ObjectInitializer)
+	:Super(ObjectInitializer)
+{}
diff --git a/Source/DisplayClusterExtensions/Private/Pawn/BasicVRInteractionComponent.cpp b/Source/DisplayClusterExtensions/Private/Pawn/BasicVRInteractionComponent.cpp
index 11ccc6372000783cf60798aad86825563900dc1c..c0a06d49cb89fb87288b1246293ce0335f26764d 100644
--- a/Source/DisplayClusterExtensions/Private/Pawn/BasicVRInteractionComponent.cpp
+++ b/Source/DisplayClusterExtensions/Private/Pawn/BasicVRInteractionComponent.cpp
@@ -1,132 +1,191 @@
-// Fill out your copyright notice in the Description page of Project Settings.
-
-
-#include "Pawn/BasicVRInteractionComponent.h"
-
-
-#include "Interaction/Clickable.h"
-#include "Interaction/Grabable.h"
-#include "Interaction/GrabbingBehaviorComponent.h"
-
-// Sets default values for this component's properties
-UBasicVRInteractionComponent::UBasicVRInteractionComponent()
-{
-	// Set this component to be initialized when the game starts, and to be ticked every frame.  You can turn these features
-	// off to improve performance if you don't need them.
-	PrimaryComponentTick.bCanEverTick = true;
-
-	// ...
-}
-
-void UBasicVRInteractionComponent::BeginInteraction()
-{
-	if(!InteractionRayEmitter) return;
-	
-	// start and end point for raytracing
-	const FTwoVectors StartEnd = GetHandRay(MaxClickDistance);
-	const FVector Start = StartEnd.v1;
-	const FVector End   = StartEnd.v2;	
-
-	// will be filled by the Line Trace Function
-	FHitResult Hit;
-
-	//if hit was not found return  
-	const FCollisionObjectQueryParams Params;
-	if (!GetWorld()->LineTraceSingleByObjectType(Hit, Start, End, Params))
-		return;
-
-	AActor* HitActor = Hit.GetActor();
-	
-	// try to cast HitActor into a Grabable if not succeeded will become a nullptr
-	IGrabable*  GrabableActor  = Cast<IGrabable>(HitActor);
-	IClickable* ClickableActor = Cast<IClickable>(HitActor);
-
-	if (GrabableActor != nullptr && Hit.Distance < MaxGrabDistance)
-	{
-		// call grabable actors function so he reacts to our grab
-		GrabableActor->OnGrabbed_Implementation();
-		
-		// save it for later, is needed every tick
-		Behavior = HitActor->FindComponentByClass<UGrabbingBehaviorComponent>();
-		if ( Behavior == nullptr)
-			HandlePhysicsAndAttachActor(HitActor);
-
-		// we save the grabbedActor in a general form to access all of AActors functions easily later
-		GrabbedActor = HitActor;
-	}
-	else if (ClickableActor != nullptr && Hit.Distance < MaxClickDistance)
-	{
-		ClickableActor->OnClicked_Implementation(Hit.Location);
-	}
-}
-
-
-void UBasicVRInteractionComponent::EndInteraction()
-{
-	if(!InteractionRayEmitter) return;
-	
-	// if we didnt grab anyone there is no need to release
-	if (GrabbedActor == nullptr)
-		return;
-
-	// let the grabbed object react to release
-	Cast<IGrabable>(GrabbedActor)->OnReleased_Implementation();
-
-	// Detach the Actor
-	if (GrabbedActor->FindComponentByClass<UGrabbingBehaviorComponent>() == nullptr)
-	{
-		GrabbedActor->GetRootComponent()->DetachFromComponent(FDetachmentTransformRules::KeepWorldTransform);
-		GrabbedActor->FindComponentByClass<UPrimitiveComponent>()->SetSimulatePhysics(bDidSimulatePhysics);
-	}
-
-	// forget about the actor
-	GrabbedActor = nullptr;
-	Behavior = nullptr;
-}
-
-// Called every frame
-void UBasicVRInteractionComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
-{
-	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
-
-	// if an actor is grabbed and a behavior is defined move move him accordingly  
-	if (GrabbedActor == nullptr || 	InteractionRayEmitter == nullptr) return;
-	
-	// if our Grabable Actor is not constrained
-	if (Behavior != nullptr)
-	{	
-		// specifies the hand in space
-		const FVector HandPos = InteractionRayEmitter->GetComponentLocation();
-		const FQuat HandQuat = InteractionRayEmitter->GetComponentQuat();
-
-		Behavior->HandleNewPositionAndDirection(HandPos, HandQuat); 
-	}
-}
-
-void UBasicVRInteractionComponent::Initialize(USceneComponent* RayEmitter, float InMaxGrabDistance, float InMaxClickDistance)
-{
-	if(InteractionRayEmitter) return; /* Return if already initialized */
-
-	InteractionRayEmitter = RayEmitter;
-	MaxGrabDistance = InMaxGrabDistance;
-	MaxClickDistance = InMaxClickDistance;
-}
-
-void UBasicVRInteractionComponent::HandlePhysicsAndAttachActor(AActor* HitActor)
-{
-	UPrimitiveComponent* PhysicsComp = HitActor->FindComponentByClass<UPrimitiveComponent>();	
-	
-	bDidSimulatePhysics = PhysicsComp->IsSimulatingPhysics(); // remember if we need to tun physics back on or not	
-	PhysicsComp->SetSimulatePhysics(false);
-	FAttachmentTransformRules Rules = FAttachmentTransformRules(EAttachmentRule::KeepWorld, true);
-	HitActor->AttachToComponent(InteractionRayEmitter, Rules);
-}
-
-FTwoVectors UBasicVRInteractionComponent::GetHandRay(const float Length) const
-{
-	const FVector Start = InteractionRayEmitter->GetComponentLocation();
-	const FVector Direction = InteractionRayEmitter->GetForwardVector();
-	const FVector End = Start + Length * Direction;
-
-	return FTwoVectors(Start, End);
-}
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "Pawn/BasicVRInteractionComponent.h"
+
+
+#include "Interaction/Clickable.h"
+#include "Interaction/Grabable.h"
+#include "Interaction/Targetable.h"
+#include "Interaction/GrabbingBehaviorComponent.h"
+#include "Misc/Optional.h"
+#include "DrawDebugHelpers.h"
+
+// Sets default values for this component's properties
+UBasicVRInteractionComponent::UBasicVRInteractionComponent()
+{
+	// Set this component to be initialized when the game starts, and to be ticked every frame.  You can turn these features
+	// off to improve performance if you don't need them.
+	PrimaryComponentTick.bCanEverTick = true;
+
+	// ...
+}
+
+void UBasicVRInteractionComponent::BeginInteraction()
+{
+	if(!InteractionRayEmitter) return;
+	
+	// start and end point for raytracing
+	const FTwoVectors StartEnd = GetHandRay(MaxClickDistance);
+	TOptional<FHitResult> Hit = RaytraceForFirstHit(StartEnd);
+	if (!Hit.IsSet())
+		return;
+
+	AActor* HitActor = Hit->GetActor();
+	
+	if (HitActor->Implements<UGrabable>() && Hit->Distance < MaxGrabDistance)
+	{
+		// call grabable actors function so he reacts to our grab
+		IGrabable::Execute_OnBeginGrab(HitActor);
+		
+		// save it for later, is needed every tick
+		Behavior = HitActor->FindComponentByClass<UGrabbingBehaviorComponent>();
+		if ( Behavior == nullptr)
+			HandlePhysicsAndAttachActor(HitActor);
+
+		// we save the grabbedActor in a general form to access all of AActors functions easily later
+		GrabbedActor = HitActor;
+	}
+	else if (HitActor->Implements<UClickable>() && Hit->Distance < MaxClickDistance)
+	{
+		IClickable::Execute_OnClick(HitActor, Hit->Location);
+	}
+}
+
+void UBasicVRInteractionComponent::EndInteraction()
+{
+	if(!InteractionRayEmitter) return;
+	
+	// if we didnt grab anyone there is no need to release
+	if (GrabbedActor == nullptr)
+		return;
+
+	// let the grabbed object react to release
+	IGrabable::Execute_OnEndGrab(GrabbedActor);
+
+	// Detach the Actor
+	if (GrabbedActor->FindComponentByClass<UGrabbingBehaviorComponent>() == nullptr)
+	{
+		if (ComponentSimulatingPhysics) {
+			ComponentSimulatingPhysics->DetachFromComponent(FDetachmentTransformRules::KeepWorldTransform);
+			ComponentSimulatingPhysics->SetSimulatePhysics(true);
+		}
+		else {
+			GrabbedActor->GetRootComponent()->DetachFromComponent(FDetachmentTransformRules::KeepWorldTransform);
+		}
+	}
+
+	// forget about the actor
+	GrabbedActor = nullptr;
+	ComponentSimulatingPhysics = nullptr;
+	Behavior = nullptr;
+}
+
+// Called every frame
+void UBasicVRInteractionComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
+{
+	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
+
+	if (!InteractionRayEmitter) return;
+	
+	// if our Grabable Actor is not constrained we need to calculate the position dynamically
+	if (Behavior != nullptr)
+	{	
+		// specifies the hand in space
+		const FVector HandPos = InteractionRayEmitter->GetComponentLocation();
+		const FQuat HandQuat = InteractionRayEmitter->GetComponentQuat();
+
+		Behavior->HandleNewPositionAndDirection(HandPos, HandQuat); 
+	}
+
+	// only raytrace for targetable objects if bool user wants to enable this feature
+	if (!bCanRaytraceEveryTick)
+		return;
+
+	const FTwoVectors StartEnd = GetHandRay(MaxClickDistance);
+	TOptional<FHitResult> Hit = RaytraceForFirstHit(StartEnd);
+	if (!Hit.IsSet())
+		return;
+	AActor* HitActor = Hit->GetActor();
+
+	// for now uses the same distance as clicking
+	if (HitActor->Implements<UTargetable>() && Hit->Distance < MaxClickDistance)
+	{
+		ITargetable::Execute_OnTargeted(HitActor, Hit->Location);
+	}
+}
+
+void UBasicVRInteractionComponent::Initialize(USceneComponent* RayEmitter, float InMaxGrabDistance, float InMaxClickDistance)
+{
+	if(InteractionRayEmitter) return; /* Return if already initialized */
+
+	InteractionRayEmitter = RayEmitter;
+	MaxGrabDistance = InMaxGrabDistance;
+	MaxClickDistance = InMaxClickDistance;
+}
+
+void UBasicVRInteractionComponent::HandlePhysicsAndAttachActor(AActor* HitActor)
+{
+	UPrimitiveComponent* PhysicsSimulatingComp = GetFirstComponentSimulatingPhysics(HitActor);
+	const FAttachmentTransformRules Rules = FAttachmentTransformRules(EAttachmentRule::KeepWorld, false);
+	
+	if (PhysicsSimulatingComp) {
+		PhysicsSimulatingComp->SetSimulatePhysics(false);
+		PhysicsSimulatingComp->AttachToComponent(InteractionRayEmitter, Rules);	
+		ComponentSimulatingPhysics = PhysicsSimulatingComp;
+	}
+	else {
+		HitActor->GetRootComponent()->AttachToComponent(InteractionRayEmitter, Rules);
+	}
+}
+
+FTwoVectors UBasicVRInteractionComponent::GetHandRay(const float Length) const
+{
+	const FVector Start = InteractionRayEmitter->GetComponentLocation();
+	const FVector Direction = InteractionRayEmitter->GetForwardVector();
+	const FVector End = Start + Length * Direction;
+
+	return FTwoVectors(Start, End);
+}
+
+TOptional<FHitResult> UBasicVRInteractionComponent::RaytraceForFirstHit(const FTwoVectors& Ray) const
+{
+	const FVector Start = Ray.v1;
+	const FVector End   = Ray.v2;	
+	
+	// will be filled by the Line Trace Function
+	FHitResult Hit;
+
+	const FCollisionObjectQueryParams Params;	
+	FCollisionQueryParams Params2; 
+	Params2.AddIgnoredActor(GetOwner()->GetUniqueID()); // prevents actor hitting itself
+	if (GetWorld()->LineTraceSingleByObjectType(Hit, Start, End, Params, Params2))
+		return {Hit};
+	else
+		return {};
+}
+
+UPrimitiveComponent* GetFirstComponentSimulatingPhysics(const AActor* TargetActor)
+{
+	TArray<UPrimitiveComponent*> PrimitiveComponents;
+	TargetActor->GetComponents<UPrimitiveComponent>(PrimitiveComponents);	
+
+	// find any component that simulates physics, then traverse the hierarchy
+	for (const auto& Component : PrimitiveComponents) {
+		if (Component->IsSimulatingPhysics()) {
+			return GetHighestParentSimulatingPhysics(Component);
+		}	
+	}
+	return nullptr;
+}
+
+// recursively goes up the hierarchy and returns the highest parent simulating physics
+UPrimitiveComponent* GetHighestParentSimulatingPhysics(UPrimitiveComponent* Comp)
+{	
+	if (Cast<UPrimitiveComponent>(Comp->GetAttachParent()) && Comp->GetAttachParent()->IsSimulatingPhysics()) {
+		return GetHighestParentSimulatingPhysics(Cast<UPrimitiveComponent>(Comp->GetAttachParent()));
+	}
+	else {
+		return Comp;
+	}
+}
diff --git a/Source/DisplayClusterExtensions/Private/Pawn/VRPawnMovement.cpp b/Source/DisplayClusterExtensions/Private/Pawn/VRPawnMovement.cpp
index 182c2b5ed2fc40f2687bd3cbcfb7459ef26d4862..b11f6bd87526002e7165bd85e4c4e353897d271d 100644
--- a/Source/DisplayClusterExtensions/Private/Pawn/VRPawnMovement.cpp
+++ b/Source/DisplayClusterExtensions/Private/Pawn/VRPawnMovement.cpp
@@ -8,7 +8,7 @@ UVRPawnMovement::UVRPawnMovement(const FObjectInitializer& ObjectInitializer) :
 	CapsuleColliderComponent->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
 	CapsuleColliderComponent->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
 	CapsuleColliderComponent->SetCollisionResponseToChannel(ECollisionChannel::ECC_WorldStatic, ECollisionResponse::ECR_Block);
-	CapsuleColliderComponent->SetCapsuleSize(40.0f, 96.0f);
+	CapsuleColliderComponent->SetCapsuleSize(CapsuleRadius, 80.0f);
 }
 
 void UVRPawnMovement::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction){
@@ -61,6 +61,9 @@ 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()
@@ -71,14 +74,13 @@ void UVRPawnMovement::SetCapsuleColliderToUserSize()
 	{
 		float ColliderHeight = CharachterSize - MaxStepHeight;
 		float ColliderHalfHeight = ColliderHeight / 2.0f;
-		float ColliderRadius = 40.0f;
-		if (ColliderHalfHeight <= ColliderRadius)
+		if (ColliderHalfHeight <= CapsuleRadius)
 		{//Make the collider to a Sphere
 			CapsuleColliderComponent->SetCapsuleSize(ColliderHalfHeight, ColliderHalfHeight);
 		}
 		else
 		{//Make the collider to a Capsule
-			CapsuleColliderComponent->SetCapsuleSize(ColliderRadius, ColliderHalfHeight);
+			CapsuleColliderComponent->SetCapsuleSize(CapsuleRadius, ColliderHalfHeight);
 		}
 
 		CapsuleColliderComponent->SetWorldLocation(HeadComponent->GetComponentLocation());
diff --git a/Source/DisplayClusterExtensions/Private/Pawn/VirtualRealityPawn.cpp b/Source/DisplayClusterExtensions/Private/Pawn/VirtualRealityPawn.cpp
index de5947b1189bb24008208536e196c61c7e2d1914..583154554c37cea3b6d0b2acdccb905cf506eeec 100644
--- a/Source/DisplayClusterExtensions/Private/Pawn/VirtualRealityPawn.cpp
+++ b/Source/DisplayClusterExtensions/Private/Pawn/VirtualRealityPawn.cpp
@@ -1,109 +1,120 @@
-// Fill out your copyright notice in the Description page of Project Settings.
-
-
-#include "Pawn/VirtualRealityPawn.h"
-
-
-
-#include "GameFramework/InputSettings.h"
-#include "GameFramework/PlayerInput.h"
-#include "Pawn/UniversalTrackedComponent.h"
-#include "Utility/VirtualRealityUtilities.h"
-#include "Pawn/VRPawnMovement.h"
-
-AVirtualRealityPawn::AVirtualRealityPawn(const FObjectInitializer& ObjectInitializer)
-	: Super(ObjectInitializer)
-{
-	bUseControllerRotationYaw = true;
-	bUseControllerRotationPitch = true;
-	bUseControllerRotationRoll = true;
-	BaseEyeHeight = 160.0f;
-	
-	AutoPossessPlayer = EAutoReceiveInput::Player0; // Necessary for receiving motion controller events.
-
-	SetRootComponent(CreateDefaultSubobject<USceneComponent>(TEXT("Root")));
-	
-	CameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
-	CameraComponent->SetupAttachment(RootComponent);
-	CameraComponent->SetAbsolute();
-
-	Head = CreateDefaultSubobject<UUniversalTrackedComponent>(TEXT("Head"));
-	Head->ProxyType = ETrackedComponentType::TCT_HEAD;
-	Head->SetupAttachment(RootComponent);
-
-	PawnMovement = CreateDefaultSubobject<UVRPawnMovement>(TEXT("Pawn Movement"));
-	PawnMovement->SetUpdatedComponent(RootComponent);
-	PawnMovement->SetHeadComponent(Head);
-	
-	RightHand = CreateDefaultSubobject<UUniversalTrackedComponent>(TEXT("Right Hand"));
-	RightHand->ProxyType = ETrackedComponentType::TCT_RIGHT_HAND;
-	RightHand->AttachementType = EAttachementType::AT_FLYSTICK;
-	RightHand->SetupAttachment(RootComponent);
-	
-	LeftHand = CreateDefaultSubobject<UUniversalTrackedComponent>(TEXT("Left Hand"));
-	LeftHand->ProxyType = ETrackedComponentType::TCT_LEFT_HAND;
-	LeftHand->AttachementType = EAttachementType::AT_HANDTARGET;
-	LeftHand->SetupAttachment(RootComponent);
-
-	BasicVRInteraction = CreateDefaultSubobject<UBasicVRInteractionComponent>(TEXT("Basic VR Interaction"));
-	BasicVRInteraction->Initialize(RightHand);
-}
-
-void AVirtualRealityPawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
-{
-	Super::SetupPlayerInputComponent(PlayerInputComponent);
-	if (!PlayerInputComponent) return;
-	
-	PlayerInputComponent->BindAxis("MoveForward", this, &AVirtualRealityPawn::OnForward);
-	PlayerInputComponent->BindAxis("MoveRight", this, &AVirtualRealityPawn::OnRight);
-	PlayerInputComponent->BindAxis("TurnRate", this, &AVirtualRealityPawn::OnTurnRate);
-	PlayerInputComponent->BindAxis("LookUpRate", this, &AVirtualRealityPawn::OnLookUpRate);
-
-	// function bindings for grabbing and releasing
-	PlayerInputComponent->BindAction("Fire", IE_Pressed, this, &AVirtualRealityPawn::OnBeginFire);
-	PlayerInputComponent->BindAction("Fire", IE_Released, this, &AVirtualRealityPawn::OnEndFire);
-}
-
-void AVirtualRealityPawn::OnForward_Implementation(float Value)
-{
-	if (RightHand)
-	{
-		AddMovementInput(RightHand->GetForwardVector(), Value);
-	}
-}
-
-void AVirtualRealityPawn::OnRight_Implementation(float Value)
-{
-	if (RightHand)
-	{
-		AddMovementInput(RightHand->GetRightVector(), Value);
-	}
-}
-
-void AVirtualRealityPawn::OnTurnRate_Implementation(float Rate)
-{
-	/* Turning the user externally will make them sick */
-	if (UVirtualRealityUtilities::IsDesktopMode())
-	{
-		AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds() * CustomTimeDilation);
-	}
-}
-
-void AVirtualRealityPawn::OnLookUpRate_Implementation(float Rate)
-{
-	/* Turning the user externally will make them sick */
-	if (UVirtualRealityUtilities::IsDesktopMode())
-	{
-		AddControllerPitchInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds() * CustomTimeDilation);
-	}
-}
-
-void AVirtualRealityPawn::OnBeginFire_Implementation()
-{
-	BasicVRInteraction->BeginInteraction();
-}
-
-void AVirtualRealityPawn::OnEndFire_Implementation()
-{
-	BasicVRInteraction->EndInteraction();
-}
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "Pawn/VirtualRealityPawn.h"
+
+
+
+#include "GameFramework/InputSettings.h"
+#include "GameFramework/PlayerInput.h"
+#include "Pawn/UniversalTrackedComponent.h"
+#include "Utility/VirtualRealityUtilities.h"
+#include "Pawn/VRPawnMovement.h"
+
+AVirtualRealityPawn::AVirtualRealityPawn(const FObjectInitializer& ObjectInitializer)
+	: Super(ObjectInitializer)
+{
+	bUseControllerRotationYaw = true;
+	bUseControllerRotationPitch = true;
+	bUseControllerRotationRoll = true;
+	BaseEyeHeight = 160.0f;
+	
+	AutoPossessPlayer = EAutoReceiveInput::Player0; // Necessary for receiving motion controller events.
+
+	SetRootComponent(CreateDefaultSubobject<USceneComponent>(TEXT("Root")));
+	
+	CameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
+	CameraComponent->SetupAttachment(RootComponent);
+	CameraComponent->SetRelativeLocation(FVector(0.0f, 0.0f, BaseEyeHeight)); //so it is rendered correctly in editor
+	
+	Head = CreateDefaultSubobject<UUniversalTrackedComponent>(TEXT("Head"));
+	Head->ProxyType = ETrackedComponentType::TCT_HEAD;
+	Head->SetupAttachment(RootComponent);
+
+	PawnMovement = CreateDefaultSubobject<UVRPawnMovement>(TEXT("Pawn Movement"));
+	PawnMovement->SetUpdatedComponent(RootComponent);
+	PawnMovement->SetHeadComponent(Head);
+	
+	RightHand = CreateDefaultSubobject<UUniversalTrackedComponent>(TEXT("Right Hand"));
+	RightHand->ProxyType = ETrackedComponentType::TCT_RIGHT_HAND;
+	RightHand->AttachementType = EAttachementType::AT_FLYSTICK;
+	RightHand->SetupAttachment(RootComponent);
+	
+	LeftHand = CreateDefaultSubobject<UUniversalTrackedComponent>(TEXT("Left Hand"));
+	LeftHand->ProxyType = ETrackedComponentType::TCT_LEFT_HAND;
+	LeftHand->AttachementType = EAttachementType::AT_HANDTARGET;
+	LeftHand->SetupAttachment(RootComponent);
+
+	BasicVRInteraction = CreateDefaultSubobject<UBasicVRInteractionComponent>(TEXT("Basic VR Interaction"));
+	BasicVRInteraction->Initialize(RightHand);
+}
+
+void AVirtualRealityPawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
+{
+	Super::SetupPlayerInputComponent(PlayerInputComponent);
+	if (!PlayerInputComponent) return;
+	
+	PlayerInputComponent->BindAxis("MoveForward", this, &AVirtualRealityPawn::OnForward);
+	PlayerInputComponent->BindAxis("MoveRight", this, &AVirtualRealityPawn::OnRight);
+	PlayerInputComponent->BindAxis("TurnRate", this, &AVirtualRealityPawn::OnTurnRate);
+	PlayerInputComponent->BindAxis("LookUpRate", this, &AVirtualRealityPawn::OnLookUpRate);
+
+	// function bindings for grabbing and releasing
+	PlayerInputComponent->BindAction("Fire", IE_Pressed, this, &AVirtualRealityPawn::OnBeginFire);
+	PlayerInputComponent->BindAction("Fire", IE_Released, this, &AVirtualRealityPawn::OnEndFire);
+}
+
+void AVirtualRealityPawn::SetCameraOffset() const
+{
+	// this also incorporates the BaseEyeHeight, if set as static offset,
+	// rotations are still around the center of the pawn (on the floor), so pitch rotations look weird
+	FVector Location;
+	FRotator Rotation;
+	GetActorEyesViewPoint(Location, Rotation);
+	CameraComponent->SetWorldLocationAndRotation(Location, Rotation);
+}
+
+void AVirtualRealityPawn::OnForward_Implementation(float Value)
+{
+	if (RightHand)
+	{
+		AddMovementInput(RightHand->GetForwardVector(), Value);
+	}
+}
+
+void AVirtualRealityPawn::OnRight_Implementation(float Value)
+{
+	if (RightHand)
+	{
+		AddMovementInput(RightHand->GetRightVector(), Value);
+	}
+}
+
+void AVirtualRealityPawn::OnTurnRate_Implementation(float Rate)
+{
+	/* Turning the user externally will make them sick */
+	if (UVirtualRealityUtilities::IsDesktopMode())
+	{
+		AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds() * CustomTimeDilation);
+	}
+}
+
+void AVirtualRealityPawn::OnLookUpRate_Implementation(float Rate)
+{
+	/* Turning the user externally will make them sick */
+	if (UVirtualRealityUtilities::IsDesktopMode())
+	{
+		AddControllerPitchInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds() * CustomTimeDilation);
+		SetCameraOffset();
+	}
+}
+
+void AVirtualRealityPawn::OnBeginFire_Implementation()
+{
+	BasicVRInteraction->BeginInteraction();
+}
+
+void AVirtualRealityPawn::OnEndFire_Implementation()
+{
+	BasicVRInteraction->EndInteraction();
+}
diff --git a/Source/DisplayClusterExtensions/Public/Cluster/CAVEOverlay/CAVEOverlay.h b/Source/DisplayClusterExtensions/Public/Cluster/CAVEOverlay/CAVEOverlay.h
index 3d12951c0b18e9c73d0ac7f9aef5d7bc5a529787..ced7309b750ac71fd756b6c023ffef621f97d415 100644
--- a/Source/DisplayClusterExtensions/Public/Cluster/CAVEOverlay/CAVEOverlay.h
+++ b/Source/DisplayClusterExtensions/Public/Cluster/CAVEOverlay/CAVEOverlay.h
@@ -18,7 +18,7 @@ struct DISPLAYCLUSTEREXTENSIONS_API FCAVEOverlay
 	void Register();
 	void Unregister() const;
 private:
-	TBaseDelegate<void, UWorld*, const UWorld::InitializationValues> On_Post_World_Initialization_Delegate;
+	TDelegate<void(UWorld*, const UWorld::InitializationValues)> On_Post_World_Initialization_Delegate;
 	void OnSessionStart(UWorld* World, UWorld::InitializationValues);
 	FDelegateHandle SessionStartDelegate;
 };
diff --git a/Source/DisplayClusterExtensions/Public/Cluster/Events/DisplayClusterEventParameterHelper.h b/Source/DisplayClusterExtensions/Public/Cluster/Events/DisplayClusterEventParameterHelper.h
index 705d893d14456b800995b17c2e20fb830e6f07d0..e841ce2d491ec7029ef43f746613b2a9d2639de4 100644
--- a/Source/DisplayClusterExtensions/Public/Cluster/Events/DisplayClusterEventParameterHelper.h
+++ b/Source/DisplayClusterExtensions/Public/Cluster/Events/DisplayClusterEventParameterHelper.h
@@ -3,114 +3,42 @@
 #include "Serialization/MemoryReader.h"
 #include "Serialization/MemoryWriter.h"
 
-// Helper function to put the arguments into a string map. It uses template substitution to choose the correct specialization.
-template <typename... Values>
-struct FillParameterMapImpl;
-
-// This specialization is chosen when there no argument left to serialize.
-template <>
-struct FillParameterMapImpl<>
-{
-	template <int ArgumentIndex>
-	static inline void Invoke(TMap<FString, FString>*)
-	{
-		// There is nothing left to do here.
-	}
-};
-
-// This specialization is chosen when there is at least one argument left to serialize.
-template <typename CurrentValueType, typename... RemainingValueTypes>
-struct FillParameterMapImpl<CurrentValueType, RemainingValueTypes...>
+template <typename ParameterType, typename... RemainingParameterTypes>
+inline void SerializeParameters(FMemoryWriter* MemoryWriter, ParameterType&& Parameter, RemainingParameterTypes&&... RemainingParameters)
 {
-	template <int ArgumentIndex>
-	static inline void Invoke(
-		TMap<FString, FString>* ParameterMap, const CurrentValueType& CurrentValue, RemainingValueTypes&&... RemainingValues)
-	{
-		// If this assertion fails: implement the workaround described below!
-		static_assert(sizeof(TCHAR) > sizeof(uint8), "TCHAR needs to have extra room!");
-
-		TArray<uint8> SerializedValue;
-		// TODO: maybe it is a good idea to guess the amount of bytes the serialized value will have. This would probably reduce the
-		// number of reallocations in the serialization process. However, I don't know if this is already taken care of in the
-		// FArchive class. Also it is hard to implement correctly, so we have to see whether it is worth it.
-		FMemoryWriter writer(SerializedValue);
-
-		// Const cast is necessary here because the "<<" operator is used for reading and writing and thus does not take a const
-		// argument. However, it should be fine as, hopefully, the "<<" operator doesn't modify the value in "reading mode".
-		auto NonConstCurrentValue = const_cast<CurrentValueType&>(CurrentValue);
-		writer << NonConstCurrentValue;
-
-		// We have an array of bytes. Now we need to convert them to a string.
-		FString SerializedDataString;
-		SerializedDataString.Empty(SerializedValue.Num());	  // Preallocate memory to avoid reallocations.
-		for (const uint8 Byte : SerializedValue)
-		{
-			// This is potentially dangerous:
-			// We treat the individual bytes as characters in a string. The character with the value 0 is normally used to mark the
-			// end of the string. Because of this, FString will not add zero values to the underlying data array. To avoid this I
-			// add 1 to every value. This only works, because the TCHAR is usually more than one byte long. However, this is not
-			// guaranteed. Currently I enforce it with a static_assert above. If this is not the case we would need to split each
-			// byte into two characters. Another option would be to access the underlying TCHAR array directly. However I don't know
-			// if they are transported correctly using the cluster events.
-			SerializedDataString += static_cast<TCHAR>(static_cast<TCHAR>(Byte) + 1);
-		}
-
-		ParameterMap->Add(FString::FromInt(ArgumentIndex), SerializedDataString);
-
-		// Recursive call for the remaining values.
-		FillParameterMapImpl<RemainingValueTypes...>::template Invoke<ArgumentIndex + 1>(
-			ParameterMap, Forward<RemainingValueTypes>(RemainingValues)...);
-	}
-};
+	using NonConstType = typename TRemoveCV<typename TRemoveReference<ParameterType>::Type>::Type;
+	(*MemoryWriter) << const_cast<NonConstType&>(Parameter); // const_cast because the same operator (<<) is used for reading and writing
+	SerializeParameters(MemoryWriter, Forward<RemainingParameterTypes>(RemainingParameters)...);
+}
 
-// This function creates a string map with the arguments it gets passed. The resulting map will contain an entry for every argument.
-// The first argument will have the key "0", the second "1" and so on.
-template <typename... ArgTypes>
-inline TMap<FString, FString> CreateParameterMap(ArgTypes&&... Arguments)
+inline void SerializeParameters(FMemoryWriter* MemoryWriter)
 {
-	TMap<FString, FString> ParameterMap;
-	ParameterMap.Empty(sizeof...(ArgTypes));	// Preallocate to avoid allocations.
-	FillParameterMapImpl<ArgTypes...>::template Invoke<0>(&ParameterMap, Forward<ArgTypes>(Arguments)...);
-	return ParameterMap;
 }
 
 // This is a wrapper function to recursively fill the argument tuple. This overload is only used if the index indicating the
 // currently handled attribute is less than the number of total attributes. I.e., if the attribute index is valid.
 template <int CurrentIndex, typename... ArgTypes>
-inline typename TEnableIf<(CurrentIndex < sizeof...(ArgTypes))>::Type FillArgumentTuple(
-	TTuple<ArgTypes...>* ArgumentTuple, const TMap<FString, FString>& Parameters)
+inline typename TEnableIf<(CurrentIndex < sizeof...(ArgTypes))>::Type
+FillArgumentTuple(FMemoryReader* MemoryReader, TTuple<ArgTypes...>* ArgumentTuple)
 {
-	const FString& SerializedDataString = Parameters[FString::FromInt(CurrentIndex)];
-	TArray<uint8> SerializedData;
-	// Preallocate to avoid reallocation
-	SerializedData.Empty(SerializedDataString.Len());
-
-	// Reconstruct the original bytes. I.e., reversing the addition by one.
-	for (const auto Character : SerializedDataString)
-	{
-		SerializedData.Add(static_cast<uint8>(Character - 1));
-	}
-
-	FMemoryReader Reader(SerializedData);
 	// Read the "<<" as ">>" operator here. FArchive uses the same for both and decides based on an internal type on what to do. So
 	// this statement parses the bytes that were passed into reader and puts the parsed object into the tuple at index CurrentIndex.
-	Reader << ArgumentTuple->template Get<CurrentIndex>();
+	(*MemoryReader) << ArgumentTuple->template Get<CurrentIndex>();
 
 	// Recursive call for the remaining attributes.
-	FillArgumentTuple<CurrentIndex + 1>(
-		Forward<TTuple<ArgTypes...>*>(ArgumentTuple), Forward<const TMap<FString, FString>&>(Parameters));
+	FillArgumentTuple<CurrentIndex + 1>(MemoryReader, Forward<TTuple<ArgTypes...>*>(ArgumentTuple));
 }
 
 // The overload that is called if we are "passed the end" of attributes.
 template <int CurrentIndex, typename... ArgTypes>
-inline typename TEnableIf<(CurrentIndex >= sizeof...(ArgTypes))>::Type FillArgumentTuple(
-	TTuple<ArgTypes...>* ArgumentTuple, const TMap<FString, FString>& Parameters)
+inline typename TEnableIf<(CurrentIndex >= sizeof...(ArgTypes))>::Type
+FillArgumentTuple(FMemoryReader* MemoryReader, TTuple<ArgTypes...>* ArgumentTuple)
 {
 }
 
 template <typename RetType, typename... ArgTypes>
 inline RetType CallDelegateWithParameterMap(
-	const TBaseDelegate<RetType, ArgTypes...>& Delegate, const TMap<FString, FString>& Parameters)
+	const TDelegate<RetType, ArgTypes...>& Delegate, const TMap<FString, FString>& Parameters)
 {
 	// Create a tuple that holds all arguments. This assumes that all argument types are default constructible. However, all
 	// types that overload the FArchive "<<" operator probably are.
diff --git a/Source/DisplayClusterExtensions/Public/Cluster/Events/DisplayClusterEventWrapper.h b/Source/DisplayClusterExtensions/Public/Cluster/Events/DisplayClusterEventWrapper.h
index 8ad2c51f4aaf59efd1a5aee5bc4e417ae165519c..7fd08fd36082d09f58aaf828230231a39d9f360e 100644
--- a/Source/DisplayClusterExtensions/Public/Cluster/Events/DisplayClusterEventWrapper.h
+++ b/Source/DisplayClusterExtensions/Public/Cluster/Events/DisplayClusterEventWrapper.h
@@ -6,51 +6,72 @@
 #include "DisplayClusterEventParameterHelper.h"
 #include "Templates/IsInvocable.h"
 
+static constexpr int32 CLUSTER_EVENT_WRAPPER_EVENT_ID = 1337420;
+
 template <typename MemberFunctionType, MemberFunctionType MemberFunction>
 class ClusterEventWrapperEvent;
 
 template <typename ObjectType, typename ReturnType, typename... ArgTypes, ReturnType (ObjectType::*MemberFunction)(ArgTypes...)>
 class ClusterEventWrapperEvent<ReturnType (ObjectType::*)(ArgTypes...), MemberFunction>
 {
-	static_assert(TIsDerivedFrom<ObjectType, AActor>::IsDerived, "Object needs to derive from AActor");
+	static_assert(TIsDerivedFrom<ObjectType, UObject>::IsDerived, "Object needs to derive from UObject");
 
 public:
 	using MemberFunctionType = decltype(MemberFunction);
 
-	ClusterEventWrapperEvent(const TCHAR* EventTypeName) : EventTypeName{EventTypeName}
+	ClusterEventWrapperEvent(const TCHAR* MethodName) : MethodName{ MethodName }
 	{
 	}
 
 	void Attach(ObjectType* NewObject)
 	{
-		IDisplayClusterClusterManager* ClusterManager = IDisplayCluster::Get().GetClusterMgr();
-		check(ClusterManager != nullptr);
 
 		checkf(Object == nullptr, TEXT("The event is already attached."));
 		Object = NewObject;
-		ObjectName = AActor::GetDebugName(Object);
+		ObjectId = Object->GetUniqueID();
 
-		if (!ClusterManager->IsStandalone())
+		EDisplayClusterOperationMode OperationMode = IDisplayCluster::Get().GetOperationMode();
+		if (OperationMode == EDisplayClusterOperationMode::Cluster)
 		{
+			IDisplayClusterClusterManager* ClusterManager = IDisplayCluster::Get().GetClusterMgr();
+			check(ClusterManager != nullptr);
+
 			check(!ClusterEventListenerDelegate.IsBound());
-			ClusterEventListenerDelegate = FOnClusterEventListener::CreateLambda([this](const FDisplayClusterClusterEvent& Event) {
-				if (Event.Type == EventTypeName && Event.Name == ObjectName)
+			ClusterEventListenerDelegate = FOnClusterEventBinaryListener::CreateLambda([this](const FDisplayClusterClusterEventBinary& Event) {
+				if (Event.EventId != CLUSTER_EVENT_WRAPPER_EVENT_ID)
 				{
-					// Create a tuple that holds all arguments. This assumes that all
-					// argument types are default constructible. However, all
-					// types that overload the FArchive "<<" operator probably are.
-					TTuple<typename TRemoveCV<typename TRemoveReference<ArgTypes>::Type>::Type...> ArgumentTuple;
-
-					// This call will parse the string map and fill all values in the
-					// tuple appropriately.
-					FillArgumentTuple<0>(&ArgumentTuple, Event.Parameters);
-
-					ArgumentTuple.ApplyBefore([this](const ArgTypes&... Arguments) {
-						(Object->*MemberFunction)(Forward<const ArgTypes&>(Arguments)...);
-					});
+					return;
+				}
+
+				FMemoryReader MemoryReader(Event.EventData);
+
+				uint32 EventObjectId;
+				MemoryReader << EventObjectId; // This is reads the value!
+				if (EventObjectId != ObjectId) {
+					// Event does not belong to this object.
+					return;
+				}
+
+				FString EventMethodName;
+				MemoryReader << EventMethodName; // This is reads the value!
+				if (EventMethodName != MethodName) {
+					// This event does not belong to this method.
+					return;
 				}
+
+				// Create a tuple that holds all arguments. This assumes that all
+				// argument types are default constructible. However, all
+				// types that overload the FArchive "<<" operator probably are.
+				TTuple<typename TRemoveCV<typename TRemoveReference<ArgTypes>::Type>::Type...> ArgumentTuple;
+
+				// This call will deserialze the values and fill all values in the tuple appropriately.
+				FillArgumentTuple<0>(&MemoryReader, &ArgumentTuple);
+
+				ArgumentTuple.ApplyBefore([this](const ArgTypes&... Arguments) {
+					(Object->*MemberFunction)(Forward<const ArgTypes&>(Arguments)...);
+				});
 			});
-			ClusterManager->AddClusterEventListener(ClusterEventListenerDelegate);
+			ClusterManager->AddClusterEventBinaryListener(ClusterEventListenerDelegate);
 		}
 	}
 
@@ -58,44 +79,50 @@ public:
 	{
 		checkf(Object != nullptr, TEXT("The event was never attached."));
 
-		IDisplayClusterClusterManager* ClusterManager = IDisplayCluster::Get().GetClusterMgr();
-		check(ClusterManager != nullptr);
-
-		if (!ClusterManager->IsStandalone())
+		EDisplayClusterOperationMode OperationMode = IDisplayCluster::Get().GetOperationMode();
+		if (OperationMode == EDisplayClusterOperationMode::Cluster)
 		{
+
+			IDisplayClusterClusterManager* ClusterManager = IDisplayCluster::Get().GetClusterMgr();
+			check(ClusterManager != nullptr);
+
 			// check(ClusterEventListenerDelegate.IsBound());
-			ClusterManager->RemoveClusterEventListener(ClusterEventListenerDelegate);
+			ClusterManager->RemoveClusterEventBinaryListener(ClusterEventListenerDelegate);
 		}
 	}
 
 	void Send(ArgTypes&&... Arguments)
 	{
-		IDisplayClusterClusterManager* ClusterManager = IDisplayCluster::Get().GetClusterMgr();
-		check(ClusterManager != nullptr);
-
 		checkf(Object != nullptr, TEXT("The event was not attached."));
 
-		if (ClusterManager->IsStandalone())
+		EDisplayClusterOperationMode OperationMode = IDisplayCluster::Get().GetOperationMode();
+		if (OperationMode != EDisplayClusterOperationMode::Cluster)
 		{
 			(Object->*MemberFunction)(Forward<ArgTypes>(Arguments)...);
 		}
 		else
 		{
-			FDisplayClusterClusterEvent ClusterEvent;
-			ClusterEvent.Category = "DisplayClusterEventWrapper";
-			ClusterEvent.Type = EventTypeName;
-			ClusterEvent.Name = ObjectName;
-			ClusterEvent.Parameters = CreateParameterMap(Forward<ArgTypes>(Arguments)...);
+			IDisplayClusterClusterManager* ClusterManager = IDisplayCluster::Get().GetClusterMgr();
+			check(ClusterManager != nullptr);
+
+			FDisplayClusterClusterEventBinary ClusterEvent;
+			ClusterEvent.EventId = CLUSTER_EVENT_WRAPPER_EVENT_ID;
+			ClusterEvent.bShouldDiscardOnRepeat = false;
+
+			FMemoryWriter MemoryWriter(ClusterEvent.EventData);
+			MemoryWriter << ObjectId;
+			MemoryWriter << const_cast<FString&>(MethodName); // const_cast... thanks unreal!
+			SerializeParameters(&MemoryWriter, Forward<ArgTypes>(Arguments)...);
 
-			ClusterManager->EmitClusterEvent(ClusterEvent, true);
+			ClusterManager->EmitClusterEventBinary(ClusterEvent, true);
 		}
 	}
 
 private:
-	const TCHAR* EventTypeName;
+	const FString MethodName;
+	uint32 ObjectId;
 	ObjectType* Object = nullptr;
-	FString ObjectName;
-	FOnClusterEventListener ClusterEventListenerDelegate;
+	FOnClusterEventBinaryListener ClusterEventListenerDelegate;
 };
 
 #define DCEW_STRINGIFY(x) #x
diff --git a/Source/DisplayClusterExtensions/Public/Interaction/Clickable.h b/Source/DisplayClusterExtensions/Public/Interaction/Clickable.h
index 141dbc50f133290faa8d1ba10feb30e82f794799..7117f45935a00a9ee30098f407fe18d2147e992d 100644
--- a/Source/DisplayClusterExtensions/Public/Interaction/Clickable.h
+++ b/Source/DisplayClusterExtensions/Public/Interaction/Clickable.h
@@ -7,7 +7,6 @@
 #include "UObject/Interface.h"
 #include "Clickable.generated.h"
 
-
 UINTERFACE(BlueprintType)
 class DISPLAYCLUSTEREXTENSIONS_API UClickable : public UInterface
 {
@@ -22,8 +21,6 @@ class IClickable
 public:
 	// function that will be called when clickable actor got clicked, and passed the world pos of the click
 	UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = Gameplay)
-	void OnClicked(FVector WorldPositionOfClick);
-	
-
+	void OnClick(FVector WorldPositionOfClick);
 };
 
diff --git a/Source/DisplayClusterExtensions/Public/Interaction/Grabable.h b/Source/DisplayClusterExtensions/Public/Interaction/Grabable.h
index a431e7cf64aec83f1b4aa0e2bf6ed3e71fe47277..c25c7648727f67e99509689d75444adbdbffb7df 100644
--- a/Source/DisplayClusterExtensions/Public/Interaction/Grabable.h
+++ b/Source/DisplayClusterExtensions/Public/Interaction/Grabable.h
@@ -21,10 +21,9 @@ class IGrabable
 public:
 	// function that will be called when grabbed by a pawn
 	UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = Gameplay)
-	void OnGrabbed();
+	void OnBeginGrab();
 	
 	// called when pawn released the object
 	UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = Gameplay)
-	void OnReleased();
-
+	void OnEndGrab();
 };
diff --git a/Source/DisplayClusterExtensions/Public/Interaction/GrabbingBehaviorOnLineComponent.h b/Source/DisplayClusterExtensions/Public/Interaction/GrabbingBehaviorOnLineComponent.h
index decc8ead1962e9e8f825b3cf9511c4a827f3e759..ad49e2303a69fc428d6ad4c7d4e1ca8eb42fe29f 100644
--- a/Source/DisplayClusterExtensions/Public/Interaction/GrabbingBehaviorOnLineComponent.h
+++ b/Source/DisplayClusterExtensions/Public/Interaction/GrabbingBehaviorOnLineComponent.h
@@ -19,6 +19,7 @@ public:
 	// defining a constraint line with these 3 parameters
 	UFUNCTION(BlueprintCallable) void SetDistance(float Dist);
 	UFUNCTION(BlueprintCallable) float GetDistance() const;
+	UFUNCTION(BlueprintCallable) void SetDiscreteNumberOfPoints(int Num);
 
 	virtual void HandleNewPositionAndDirection(FVector position, FQuat orientation) override;
 
@@ -26,12 +27,12 @@ protected:
 	// Called when the game starts
 	virtual void BeginPlay() override;
 
-public:	
+public:
 	// Called every frame
 	virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
 
-
 private:
-	UPROPERTY(EditAnywhere)	float Distance; // distance the object can be moved from the center 
-
+	UPROPERTY(EditAnywhere)	float Distance = 100; // distance the object can be moved from the center 
+	UPROPERTY(EditAnywhere) bool bIsDiscrete = false;
+	UPROPERTY(EditAnywhere) int NumPoints = 1;
 };
diff --git a/Source/DisplayClusterExtensions/Public/Interaction/Targetable.h b/Source/DisplayClusterExtensions/Public/Interaction/Targetable.h
new file mode 100644
index 0000000000000000000000000000000000000000..a11da8bd223ecfdc4f66863eabc70d71ee5f15e8
--- /dev/null
+++ b/Source/DisplayClusterExtensions/Public/Interaction/Targetable.h
@@ -0,0 +1,25 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "UObject/Object.h"
+#include "UObject/Interface.h"
+#include "Targetable.generated.h"
+
+UINTERFACE(BlueprintType)
+class DISPLAYCLUSTEREXTENSIONS_API UTargetable: public UInterface
+{
+	// has to be empty, this is Unreals syntax to make it visible in blueprints
+	GENERATED_UINTERFACE_BODY()
+};
+
+class ITargetable
+{
+	GENERATED_IINTERFACE_BODY()
+
+public:
+	// function that will be called when clickable actor got clicked, and passed the world pos of the click
+	UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = Gameplay)
+	void OnTargeted(FVector WorldPositionOfTarget);
+};
diff --git a/Source/DisplayClusterExtensions/Public/Pawn/BasicVRInteractionComponent.h b/Source/DisplayClusterExtensions/Public/Pawn/BasicVRInteractionComponent.h
index 525c37ed39681ed1ec7198558f4e3672809c8b27..6e86c208e971ad141a34c489bc1d6d3aca123d5c 100644
--- a/Source/DisplayClusterExtensions/Public/Pawn/BasicVRInteractionComponent.h
+++ b/Source/DisplayClusterExtensions/Public/Pawn/BasicVRInteractionComponent.h
@@ -26,19 +26,37 @@ public:
 
 	UPROPERTY(BlueprintReadWrite) float MaxGrabDistance = 50;
 	UPROPERTY(BlueprintReadWrite) float MaxClickDistance = 500;
+	// Enable this if you want to interact with Targetable classes
+	UPROPERTY(EditAnywhere) bool bCanRaytraceEveryTick = false;
 
 	UFUNCTION(BlueprintCallable) void Initialize(USceneComponent* RayEmitter, float InMaxGrabDistance = 50, float InMaxClickDistance = 500);
 	
-	UFUNCTION(BlueprintCallable, BlueprintPure) AActor* GetGrabbedActor() const { return GrabbedActor;	}
+	UFUNCTION(BlueprintCallable, BlueprintPure) AActor* GetGrabbedActor() const { return GrabbedActor;}
 	UFUNCTION(BlueprintCallable, BlueprintPure) USceneComponent* GetInteractionRayEmitter() const { return InteractionRayEmitter;	}
 private:
-	/* indicates if the grabbed actor was simulating physics before we grabbed it */
-	UPROPERTY() bool bDidSimulatePhysics;	
 	/* Holding a reference to the actor that is currently being grabbed */
 	UPROPERTY() AActor* GrabbedActor;
+	/* Holds a reference to the grabbed actors physics simulating component if there was one*/
+	UPROPERTY() UPrimitiveComponent* ComponentSimulatingPhysics = nullptr;
 	UPROPERTY() UGrabbingBehaviorComponent* Behavior = nullptr;
 	UPROPERTY() USceneComponent* InteractionRayEmitter = nullptr;
 	
 	void HandlePhysicsAndAttachActor(AActor* HitActor);
 	FTwoVectors GetHandRay(float Length) const;
+	TOptional<FHitResult> RaytraceForFirstHit(const FTwoVectors& Ray) const;
 };
+
+// Free utility functions 
+/*
+	Returns the UPrimitiveComponent simulating physics that is highest in the hierarchy
+*/
+UPrimitiveComponent* GetFirstComponentSimulatingPhysics(const AActor* TargetActor);
+/*
+	Recursive Function
+	If parent component simulates physics returns GetHighestParentSimulatingPhysics(Parent)
+	else returns Comp itself
+*/
+UPrimitiveComponent* GetHighestParentSimulatingPhysics(UPrimitiveComponent* Comp);
+
+
+
diff --git a/Source/DisplayClusterExtensions/Public/Pawn/VRPawnMovement.h b/Source/DisplayClusterExtensions/Public/Pawn/VRPawnMovement.h
index 31bb8611508c484f62f346789ab9a5c4c654af34..d90c30c7ab47a8847590bb7894f8447f6642a58e 100644
--- a/Source/DisplayClusterExtensions/Public/Pawn/VRPawnMovement.h
+++ b/Source/DisplayClusterExtensions/Public/Pawn/VRPawnMovement.h
@@ -52,6 +52,9 @@ public:
 	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement")
 	float UpSteppingAcceleration = 500.0f;
 
+	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "VR Movement")
+	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);
diff --git a/Source/DisplayClusterExtensions/Public/Pawn/VirtualRealityPawn.h b/Source/DisplayClusterExtensions/Public/Pawn/VirtualRealityPawn.h
index d729c7dbf659d14414f3b1bfe69ca25afdf6f708..c37e7b85fc3f2d4f2f451cfc6ae1219789b120b7 100644
--- a/Source/DisplayClusterExtensions/Public/Pawn/VirtualRealityPawn.h
+++ b/Source/DisplayClusterExtensions/Public/Pawn/VirtualRealityPawn.h
@@ -23,8 +23,6 @@ public:
 	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Proxy Objects") UUniversalTrackedComponent* Head;
 	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Proxy Objects") UUniversalTrackedComponent* RightHand;
 	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Proxy Objects") UUniversalTrackedComponent* LeftHand;
-	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Proxy Objects") UUniversalTrackedComponent* Tracker1;
-	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Proxy Objects") UUniversalTrackedComponent* Tracker2;
 
 	/* Interaction */
 	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pawn|Interaction") UBasicVRInteractionComponent* BasicVRInteraction;
@@ -47,5 +45,7 @@ protected:
 
 	/* Interaction */
 	UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn|Interaction") void OnBeginFire(); 
-	UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn|Interaction") void OnEndFire(); 
+	UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Pawn|Interaction") void OnEndFire();
+
+	void SetCameraOffset() const;
 };